Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
140 commits
Select commit Hold shift + click to select a range
c327cf1
Initial backend endpoint for returning crossref cited by information …
tombch Apr 27, 2026
5e669f6
Display citations in frontend
tombch Apr 27, 2026
2f82180
Updated to use seqSetId and version to locate DOI first
tombch Apr 27, 2026
9497e96
Formatting
tombch Apr 27, 2026
8c831f0
Switched SeqSet details page modals to dialogs in line with other Loc…
tombch Apr 28, 2026
5730e79
Formatting
tombch Apr 28, 2026
ab09d96
Updated frontend to use get-seqset-citations for total citations and …
tombch Apr 29, 2026
e23931d
Same syntax for empty titles
tombch Apr 29, 2026
05bcef9
Add caching for crossref API call
tombch Apr 29, 2026
27470df
Formatting
tombch Apr 29, 2026
2c67ebc
Fixed tests by changing crossref properties from final to private, an…
tombch Apr 29, 2026
b891a49
Merge branch 'main' into seqset-citations
tombch Apr 29, 2026
e38caa8
Merge branch 'main' into seqset-citations
tombch Apr 30, 2026
d3b5500
Merge branch 'main' into seqset-citations
tombch May 4, 2026
9199679
Fixing lockfile
tombch May 4, 2026
f0a2c64
Merge branch 'main' into seqset-citations
tombch May 4, 2026
9e865b0
parseCrossRefCitedByXML tests
tombch May 4, 2026
b8c8d18
Added types + comments
tombch May 6, 2026
5c20008
Added tests for getSeqSetCitations, removed relaxed from crossref moc…
tombch May 6, 2026
a98c60d
Remove caching dependency
tombch May 7, 2026
0890599
Merge branch 'main' into seqset-citations
tombch May 7, 2026
c9ab8a3
Added seqset citations table, enforced unique on non-null seqset DOIs…
tombch May 7, 2026
c31180a
Merge branch 'main' into seqset-citations
tombch May 7, 2026
2e0a647
Update schema documentation based on migration changes
actions-user May 7, 2026
5d20ffa
Added skipping of seqset dois if not in the db
tombch May 7, 2026
c136152
Task depends on seqsets being enabled
tombch May 8, 2026
f35d284
Updated backend to have a normalised structure for citations - seqset…
tombch May 12, 2026
cc86479
Merge branch 'main' into seqset-citations
tombch May 12, 2026
cb53341
Update schema documentation based on migration changes
actions-user May 12, 2026
588f7bb
Formatting
tombch May 12, 2026
040a7a9
Update frontend to use citing source structure
tombch May 12, 2026
f1c5c6a
Merge branch 'seqset-citations' of github.com:loculus-project/loculus…
tombch May 12, 2026
ddfd5c1
Merge branch 'main' into seqset-citations
tombch May 13, 2026
55c8a4d
Merge branch 'main' into seqset-citations
tombch May 18, 2026
1562e46
Update schema documentation based on migration changes
actions-user May 18, 2026
f5c39ba
Update citing source to use doi as primary key and remove source type
tombch May 18, 2026
766a462
Merge branch 'seqset-citations' of github.com:loculus-project/loculus…
tombch May 18, 2026
814564e
Update schema documentation based on migration changes
actions-user May 18, 2026
69b1e16
No longer returns seqset dois in DTO, throws exceptions for unparseab…
tombch May 18, 2026
162d834
Increased task fixed delay to six hours, removed where clause on pres…
tombch May 18, 2026
bacb448
Remove requirement for seqset DOI
tombch May 19, 2026
f1eca3c
Merge branch 'main' into seqset-citations
tombch May 20, 2026
5ac697f
Wait/read timeouts for crossref citedby
tombch May 20, 2026
d728ae5
Refactor parseCrossRefCitedByXML and mergeCitingSources to have list …
tombch May 20, 2026
90e27c8
Merge branch 'seqset-citations' of github.com:loculus-project/loculus…
tombch May 20, 2026
e3ae3f5
Removed unique constraint on doi for now - address in follow-up
tombch May 20, 2026
978e375
Claude nitpicks
tombch May 20, 2026
ca32d4d
Removed new get-seqset-citations and refactored it into existing get-…
tombch May 20, 2026
f04e67e
Update schema documentation based on migration changes
actions-user May 20, 2026
d16b13c
Restored log comment
tombch May 20, 2026
ce4ffae
Merge branch 'seqset-citations' of github.com:loculus-project/loculus…
tombch May 20, 2026
77e25dd
Make base dialog transition smoothly
tombch May 20, 2026
5e939b9
Prevent reloading seq set citations on modal open
tombch May 20, 2026
ed87cfc
Close connection in all cases
tombch May 20, 2026
4dde77f
Fix tests failing due to modal transitions
tombch May 20, 2026
9a56335
Revert "Make base dialog transition smoothly"
tombch May 20, 2026
64215e0
Revert "Fix tests failing due to modal transitions"
tombch May 20, 2026
2d8c1c5
Reverting dialog transitions
tombch May 20, 2026
9300db2
Moved year from string to int
tombch May 20, 2026
7b6d90c
Update schema documentation based on migration changes
actions-user May 20, 2026
4c1d82c
Updated schema to use integer primary key for citing sources - DOI is…
tombch May 20, 2026
112f49e
Merge branch 'seqset-citations' of github.com:loculus-project/loculus…
tombch May 20, 2026
c14e35d
Update schema documentation based on migration changes
actions-user May 20, 2026
cf19235
Fixing linting errors
tombch May 20, 2026
0b6d55f
Merge branch 'seqset-citations' of github.com:loculus-project/loculus…
tombch May 20, 2026
153cdac
Switch to using loculus date provider
tombch May 21, 2026
e38f4c8
Added citations for sequences
tombch May 21, 2026
53e0eae
Merge branch 'main' into seqset-citations
tombch May 22, 2026
f35ab9d
Fix merge conflicts
tombch May 22, 2026
34bf89f
Merge branch 'seqset-citations' into sequence-citations
tombch May 22, 2026
8fe2d44
Added view citations button for sequence preview, and updated citatio…
tombch May 22, 2026
c686b17
Switch to customEnumeration to remove arbitrary length requirement
tombch May 22, 2026
04317b6
Updated crossref parsing function to throw on malformed xml and xml m…
tombch May 22, 2026
3ad141a
Restructuring backend types to have CitationSource, SeqSetCitationSou…
tombch May 26, 2026
8e0ff24
Update schema documentation based on migration changes
actions-user May 26, 2026
65229a7
Merge branch 'main' into seqset-citations
tombch May 26, 2026
f585239
Merge branch 'seqset-citations' into sequence-citations
tombch May 26, 2026
a482653
Updated to use composed source in endpoint return type
tombch May 26, 2026
27824a7
Merge branch 'main' into seqset-citations
tombch May 28, 2026
173fa80
Merge branch 'seqset-citations' into sequence-citations
tombch May 28, 2026
35f044c
Updated to generic citations list component
tombch May 28, 2026
0c0e5b2
Merge branch 'seqset-citations' into sequence-citations
tombch May 28, 2026
f719206
Formatting
tombch May 28, 2026
b748218
Updated to citation table
tombch May 28, 2026
579dcc3
Count of citations in view button
tombch May 28, 2026
2c8301d
Merge branch 'main' into seqset-citations
tombch May 28, 2026
4fd697e
Merge branch 'seqset-citations' into sequence-citations
tombch May 28, 2026
57b905a
Updated to use citation table
tombch May 28, 2026
728d062
Updated crossref parsing function to return validation errors in forw…
tombch May 29, 2026
2482a1f
Merge branch 'main' into seqset-citations
tombch May 29, 2026
fdbf8ab
Merge branch 'seqset-citations' into sequence-citations
tombch May 29, 2026
59f8afe
Final css tweaks
tombch May 29, 2026
a1b9f03
Merge branch 'seqset-citations' of github.com:loculus-project/loculus…
tombch May 29, 2026
ca675c9
Fixed test
tombch May 29, 2026
3ddcd39
Merge branch 'seqset-citations' into sequence-citations
tombch May 29, 2026
63e9b76
Merge branch 'main' into seqset-citations
tombch Jun 9, 2026
60ed7aa
Remove invalid width tag
tombch Jun 9, 2026
d49eca0
Updated migration
tombch Jun 9, 2026
6b3cce2
Added schema annotation for citations and description for crossref ci…
tombch Jun 9, 2026
19379fb
Updated min width tag
tombch Jun 9, 2026
dbc2e52
Format names wiht missing firstname/surname
tombch Jun 9, 2026
d6c4ef8
Renamed get-seqset-cited-by-publication to get-seqset-citations
tombch Jun 9, 2026
f5f95ea
Parameterised crossref citedby xml validation tests
tombch Jun 9, 2026
f2aa649
Updated task description
tombch Jun 9, 2026
e2fb4a4
Added link to crossref content registration docs in values yaml
tombch Jun 9, 2026
d289025
Updated parseCrossRefCitedByXML function to handle different title tags
tombch Jun 9, 2026
2c3e2aa
Remove start_dev addition
tombch Jun 9, 2026
d673f27
Moved duplicate test seqset creation into function
tombch Jun 9, 2026
0227537
Merge branch 'seqset-citations' into sequence-citations
tombch Jun 9, 2026
52304df
Merge branch 'main' into seqset-citations
tombch Jun 10, 2026
7e91593
Updated migration name
tombch Jun 10, 2026
c55cf73
Merge branch 'seqset-citations' into sequence-citations
tombch Jun 10, 2026
ac717e3
Merge branch 'main' into sequence-citations
tombch Jun 10, 2026
1062c8f
Updated via seqset to from seqset
tombch Jun 10, 2026
b6652c1
Renamed get-sequence-cited-by-publication to get-sequence-citations
tombch Jun 10, 2026
a9aaf5c
Formatting
tombch Jun 10, 2026
02a33a5
Updated sequence citations to show inline with modal to expand
tombch Jun 10, 2026
f1c0cd0
Reverting previous ui
tombch Jun 10, 2026
4574643
Update get-sequence-citations to have optional version parameter
tombch Jun 12, 2026
be86cb0
Update sequence citation website display
tombch Jun 12, 2026
a477fa0
Fixed error with % using different escape character
tombch Jun 12, 2026
d0a9d77
Added parameterised test for sequence citations
tombch Jun 12, 2026
2009f62
Cleaned up website logic
tombch Jun 12, 2026
e98764e
Merge branch 'main' into sequence-citations
tombch Jun 12, 2026
898ed7b
Removed dead query provider
tombch Jun 12, 2026
52bfa35
Merge branch 'sequence-citations' of github.com:loculus-project/locul…
tombch Jun 12, 2026
a6801e9
Removed other now redundant change
tombch Jun 12, 2026
d643c05
Cleaned up test
tombch Jun 12, 2026
e967558
Merge branch 'main' into sequence-citations
tombch Jun 12, 2026
20b31e1
Add consistent sort order for citing sources
tombch Jun 12, 2026
50c668d
Comments
tombch Jun 12, 2026
3e710bb
Added commas
tombch Jun 12, 2026
4e32293
Merge branch 'main' into sequence-citations
tombch Jun 19, 2026
eef72e7
Log version only when not null
tombch Jun 19, 2026
3e01065
Add comment
tombch Jun 19, 2026
f39ab08
Clarify seqset accession version and sequence accession with potentia…
tombch Jun 19, 2026
8084033
Updated schema
tombch Jun 19, 2026
d2533ce
Folded in sequence citations to general sections, gated fetching for …
tombch Jun 19, 2026
23e8312
Merge branch 'main' into sequence-citations
tombch Jun 19, 2026
fc02ecb
Move citations promise resolve to after redirect/not found response c…
tombch Jun 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions backend/src/main/kotlin/org/loculus/backend/api/SeqSetCitations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ data class SeqSetCitationSource(val source: CitationSource, val seqSetDOIs: Set<
@Schema(description = "A citation of a SeqSet.")
data class SeqSetCitation(val source: CitationSource)

data class SeqSetCitingSequence(
@Schema(
description = "The accession and version of the SeqSet that was cited.",
type = "string",
example = "PP_SS_1.1",
)
val seqSetAccessionVersion: String,
@Schema(
description = "The accession of the sequence within the cited SeqSet. Can be either versioned or unversioned.",
type = "string",
example = "PP_123456.1",
)
val sequenceAccession: String,
)

@Schema(description = "A citation of a sequence.")
data class SequenceCitation(val source: CitationSource, val seqSets: List<SeqSetCitingSequence>)

data class ResponseSeqSet(val seqSetId: String, val seqSetVersion: Long)

data class CitedBy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class SecurityConfig {
"/get-seqset",
"/get-seqset-records",
"/get-seqset-citations",
"/get-sequence-citations",
"/get-author",
"/*/get-released-data",
"/files/get/**",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package org.loculus.backend.controller

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import org.loculus.backend.api.AccessionVersion
import org.loculus.backend.api.AuthorProfile
import org.loculus.backend.api.CitedBy
import org.loculus.backend.api.ResponseSeqSet
import org.loculus.backend.api.SeqSet
import org.loculus.backend.api.SeqSetCitation
import org.loculus.backend.api.SeqSetRecord
import org.loculus.backend.api.SequenceCitation
import org.loculus.backend.api.SubmittedSeqSet
import org.loculus.backend.api.SubmittedSeqSetRecord
import org.loculus.backend.api.SubmittedSeqSetUpdate
Expand Down Expand Up @@ -109,6 +111,11 @@ class SeqSetCitationsController(
fun getSeqSetCitations(@RequestParam seqSetId: String, @RequestParam version: Long): List<SeqSetCitation> =
seqSetCitationsService.getSeqSetCitations(seqSetId, version)

@Operation(description = "Get sequence citations from publications or other sources")
@GetMapping("/get-sequence-citations")
Comment thread
tombch marked this conversation as resolved.
Comment thread
tombch marked this conversation as resolved.
fun getSequenceCitations(@RequestParam accession: String, @RequestParam version: Long?): List<SequenceCitation> =
seqSetCitationsService.getSequenceCitations(accession, version)

@Operation(description = "Get an author")
@GetMapping("/get-author")
fun getAuthor(@RequestParam username: String): AuthorProfile {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import kotlinx.datetime.toLocalDateTime
import mu.KotlinLogging
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.JoinType
import org.jetbrains.exposed.sql.LikePattern
import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
import org.jetbrains.exposed.sql.alias
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.andWhere
Expand All @@ -17,6 +21,7 @@ import org.jetbrains.exposed.sql.batchUpsert
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.max
import org.jetbrains.exposed.sql.or
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.update
import org.keycloak.representations.idm.UserRepresentation
Expand All @@ -30,7 +35,9 @@ import org.loculus.backend.api.SeqSet
import org.loculus.backend.api.SeqSetCitation
import org.loculus.backend.api.SeqSetCitationSource
import org.loculus.backend.api.SeqSetCitationsConstants
import org.loculus.backend.api.SeqSetCitingSequence
import org.loculus.backend.api.SeqSetRecord
import org.loculus.backend.api.SequenceCitation
import org.loculus.backend.api.Status.APPROVED_FOR_RELEASE
import org.loculus.backend.api.SubmittedSeqSetRecord
import org.loculus.backend.auth.AuthenticatedUser
Expand Down Expand Up @@ -531,6 +538,53 @@ class SeqSetCitationsDatabaseService(
}
}

fun getSequenceCitations(accession: String, version: Long?): List<SequenceCitation> {
log.info { "Get sequence citations for accession ${accession}${version?.let { ", version $it" } ?: ""}" }

// If a version is provided, return citations pinned to that exact accession and version
// Otherwise, return all citations for the accession, regardless of version, including unversioned
// NOTE: In this table, the 'accession' column can contain both accessions and accessionVersions
val accessionQuery: Op<Boolean> = if (version != null) {
val accessionVersion = AccessionVersion(accession, version)
SeqSetRecordsTable.accession eq accessionVersion.displayAccessionVersion()
Comment thread
tombch marked this conversation as resolved.
} else {
(SeqSetRecordsTable.accession eq accession) or
(SeqSetRecordsTable.accession like (LikePattern.ofLiteral("$accession.") + "%"))
}

return SeqSetCitationSourceTable.innerJoin(
Comment thread
tombch marked this conversation as resolved.
Comment thread
tombch marked this conversation as resolved.
SeqSetToCitationSourceTable,
).innerJoin(
Comment thread
tombch marked this conversation as resolved.
SeqSetsTable,
).innerJoin(SeqSetToRecordsTable).innerJoin(SeqSetRecordsTable).selectAll()
.where { accessionQuery }
.orderBy(
SeqSetCitationSourceTable.year to SortOrder.DESC,
SeqSetCitationSourceTable.citationSourceId to SortOrder.DESC,
)
.groupBy { it[SeqSetCitationSourceTable.citationSourceId] }
.map { (_, rows) ->
val first = rows.first()
SequenceCitation(
source = CitationSource(
sourceDOI = first[SeqSetCitationSourceTable.sourceDOI],
title = first[SeqSetCitationSourceTable.title],
year = first[SeqSetCitationSourceTable.year],
contributors = first[SeqSetCitationSourceTable.contributors],
),
seqSets = rows.map {
SeqSetCitingSequence(
seqSetAccessionVersion = AccessionVersion(
it[SeqSetsTable.seqSetId],
it[SeqSetsTable.seqSetVersion],
).displayAccessionVersion(),
sequenceAccession = it[SeqSetRecordsTable.accession],
)
},
)
}
}

fun validateSeqSetRecords(seqSetRecords: List<SubmittedSeqSetRecord>) {
if (seqSetRecords.isEmpty()) {
throw UnprocessableEntityException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.jayway.jsonpath.JsonPath
import com.ninjasquad.springmockk.MockkBean
import io.mockk.every
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.Matchers.containsInAnyOrder
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
Expand Down Expand Up @@ -192,6 +193,41 @@ class CitationEndpointsTest(
)
}

@ParameterizedTest(name = "{0}")
@MethodSource("sequenceCitationVersionCases")
fun `WHEN getting sequence citations`(case: SequenceCitationVersionCase) {
val citations = case.citationsMap.map { (accession, citingSourceDOI) ->
val seqSetRecords = """[{ "accession": "$accession", "type": "loculus" }]"""
val result = client.createSeqSet(seqSetRecords = seqSetRecords).andExpect(status().isOk).andReturn()
val seqSetId = JsonPath.read<String>(result.response.contentAsString, "$.seqSetId")
val seqSetVersion = JsonPath.read<Int>(result.response.contentAsString, "$.seqSetVersion").toLong()
client.createSeqSetDOI(seqSetId = seqSetId, seqSetVersion = seqSetVersion).andExpect(status().isOk)
SeqSetCitationSource(
CitationSource(
sourceDOI = citingSourceDOI,
title = "A paper citing $accession",
year = 2024,
contributors = listOf(CitationContributor(givenName = "Jane", surname = "Doe")),
),
seqSetDOIs = setOf("$MOCK_DOI_PREFIX/$seqSetId.$seqSetVersion"),
)
}

every { crossRefService.isActive } returns true
every { crossRefService.getCrossRefCitedBy(MOCK_DOI_PREFIX) } returns
CrossRefCitedByResult(citations, emptyList())
seqSetCrossRefCitationsTask.task()

client.getSequenceCitations(accession = case.accession, version = case.version)
.andExpect(status().isOk)
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("\$").isArray)
.andExpect(jsonPath("\$.length()").value(case.expectedCitingSourceDOIs.size))
.andExpect(
jsonPath("\$[*].source.sourceDOI", containsInAnyOrder(*case.expectedCitingSourceDOIs.toTypedArray())),
)
}

@Test
fun `WHEN multiple crossref citation runs link the same citation source THEN all citations are recorded`() {
fun createSeqSetWithDOI(): Triple<String, Long, String> {
Expand Down Expand Up @@ -250,9 +286,52 @@ class CitationEndpointsTest(
val isModifying: Boolean,
)

data class SequenceCitationVersionCase(
val description: String,
val citationsMap: Map<String, String>,
val accession: String,
val version: Long?,
val expectedCitingSourceDOIs: List<String>,
) {
override fun toString() = description
}

@JvmStatic
fun authorizationTestCases(): List<Scenario> = listOf(
Comment thread
tombch marked this conversation as resolved.
Comment thread
tombch marked this conversation as resolved.
Scenario({ jwt, client -> client.getUserCitedBySeqSet(jwt = jwt) }, false),
)

@JvmStatic
fun sequenceCitationVersionCases(): List<SequenceCitationVersionCase> {
// Map of test accessions to citation source DOIs
val testAccessions = listOf(
MOCK_SEQ_ACCESSION,
"$MOCK_SEQ_ACCESSION.1",
"$MOCK_SEQ_ACCESSION.2",
"$MOCK_SEQ_ACCESSION-other.1",
)
val citationsMap = testAccessions.associateWith { "10.5678/paper-citing-$it" }

return listOf(
SequenceCitationVersionCase(
description = "accession and version THEN returns citations for the exact accession version",
citationsMap = citationsMap,
accession = MOCK_SEQ_ACCESSION,
version = 1L,
expectedCitingSourceDOIs = listOf("10.5678/paper-citing-${MOCK_SEQ_ACCESSION}.1"),
),
SequenceCitationVersionCase(
description = "accession only THEN returns citations for all accession versions + unversioned",
citationsMap = citationsMap,
accession = MOCK_SEQ_ACCESSION,
version = null,
expectedCitingSourceDOIs = listOf(
"10.5678/paper-citing-${MOCK_SEQ_ACCESSION}",
"10.5678/paper-citing-${MOCK_SEQ_ACCESSION}.1",
"10.5678/paper-citing-${MOCK_SEQ_ACCESSION}.2",
),
),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ class SeqSetCitationsControllerClient(private val mockMvc: MockMvc) {
.param("version", seqSetVersion.toString()),
)

fun getSequenceCitations(accession: String = MOCK_SEQ_ACCESSION, version: Long? = MOCK_SEQ_VERSION): ResultActions =
mockMvc.perform(
get("/get-sequence-citations")
.param("accession", accession)
.apply { version?.let { param("version", it.toString()) } },
)

fun getAuthor(username: String): ResultActions = mockMvc.perform(
get("/get-author")
.param("username", username),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,9 @@ class CrossRefServiceTest(
val description: String,
val xml: String,
val expectedReasonContains: String,
)
) {
override fun toString() = description
}

@JvmStatic
fun crossRefValidationErrorCases(): List<CrossRefValidationErrorCase> = listOf(
Expand Down
56 changes: 56 additions & 0 deletions website/src/components/SeqSetCitations/CitationList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useState, type FC } from 'react';

import { CitationDetails, CitationTable } from './CitationTable';
import type { SeqSetCitation, SequenceCitation } from '../../types/seqSetCitation';
import { BaseDialog } from '../common/BaseDialog';
import { Button } from '../common/Button';

interface CitationListProps {
citations: SeqSetCitation[] | SequenceCitation[];
maxDisplayedCitations?: number;
modalTitle?: string;
}

const CitationList: FC<CitationListProps> = ({ citations, maxDisplayedCitations, modalTitle }) => {
const [isOpen, setIsOpen] = useState(false);
const displayCitationsModalButton = maxDisplayedCitations !== undefined && citations.length > maxDisplayedCitations;

return (
<div className='space-y-2'>
<BaseDialog
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title={modalTitle ?? 'Citations'}
fullWidth={false}
className='min-h-[60vh]'
>
<CitationTable isLoading={false} error={null} citations={citations} />
</BaseDialog>
{citations.length > 0 ? (
<div className='space-y-2'>
<ul className='space-y-4'>
{(maxDisplayedCitations !== undefined
? citations.slice(0, maxDisplayedCitations)
: citations
).map((citation: SeqSetCitation | SequenceCitation) => (
<li key={citation.source.sourceDOI}>
<CitationDetails citation={citation} className='text-sm' displayYear />
</li>
))}
</ul>
{displayCitationsModalButton && (
<Button className='text-sm hover:underline' onClick={() => setIsOpen(true)}>
View all citations ({citations.length})...
</Button>
)}
</div>
) : (
<div className='py-8 text-center'>
<span>No citations found.</span>
</div>
)}
</div>
);
};

export default CitationList;
Loading
Loading