feat: add event narrative and evidence workflow
This commit is contained in:
@@ -62,7 +62,7 @@ export function createLevelRepository(pool: Pool, editingEnabled: boolean): Leve
|
||||
async function assembleLevel(slug: string): Promise<CaseState | null> {
|
||||
const level = await findLevel(pool, slug)
|
||||
if (!level) return null
|
||||
const [exhibitsResult, blocksResult, regionsResult, membershipsResult, connectionsResult, metadataResult] = await Promise.all([
|
||||
const [exhibitsResult, blocksResult, regionsResult, membershipsResult, connectionsResult, metadataResult, eventEvidenceResult] = await Promise.all([
|
||||
pool.query<ExhibitRow>(`SELECT e.id, e.exhibit_type_id, e.xpos, e.ypos, e.width, e.hidden,
|
||||
COALESCE(f.title, d.title, n.title, ev.title, '') AS title,
|
||||
COALESCE(f.label_text, n.note_text, ev.narrative_text, '') AS content,
|
||||
@@ -93,6 +93,9 @@ export function createLevelRepository(pool: Pool, editingEnabled: boolean): Leve
|
||||
pool.query<{ exhibit_id: string; field_key: string; value: string }>(
|
||||
`SELECT v.exhibit_id, f.field_key, v.value FROM osint.exhibit_metadata_text_values v
|
||||
JOIN osint.metadata_fields f ON f.id = v.field_id WHERE f.board_id = $1 ORDER BY f.field_key`, [level.board_id]),
|
||||
pool.query<{ event_exhibit_id: string; evidence_exhibit_id: string }>(
|
||||
`SELECT event_exhibit_id,evidence_exhibit_id FROM osint.event_evidence WHERE board_id=$1
|
||||
ORDER BY event_exhibit_id,sort_order,evidence_exhibit_id`, [level.board_id]),
|
||||
])
|
||||
|
||||
const blocks = new Map<string, string[]>()
|
||||
@@ -104,6 +107,8 @@ export function createLevelRepository(pool: Pool, editingEnabled: boolean): Leve
|
||||
const metadata = new Map<string, Record<string, string>>()
|
||||
for (const row of metadataResult.rows) metadata.set(row.exhibit_id, { ...(metadata.get(row.exhibit_id) || {}), [row.field_key]: row.value })
|
||||
const contained = new Map<string, string[]>()
|
||||
const eventEvidence = new Map<string, string[]>()
|
||||
for (const row of eventEvidenceResult.rows) eventEvidence.set(row.event_exhibit_id, [...(eventEvidence.get(row.event_exhibit_id) || []), row.evidence_exhibit_id])
|
||||
const relations: WidgetRelation[] = membershipsResult.rows.map(row => {
|
||||
contained.set(row.folder_exhibit_id, [...(contained.get(row.folder_exhibit_id) || []), row.child_exhibit_id])
|
||||
return { id: `contains:${row.folder_exhibit_id}:${row.child_exhibit_id}`, fromWidgetId: row.folder_exhibit_id,
|
||||
@@ -121,6 +126,7 @@ export function createLevelRepository(pool: Pool, editingEnabled: boolean): Leve
|
||||
id: row.id, type: row.exhibit_type_id as Evidence['type'], title: row.title, content: row.content,
|
||||
sourceDocumentId: row.source_document_id || undefined, sourceRegionId: row.source_region_key || undefined,
|
||||
eventDate: row.occurred_at?.toISOString(), x: row.xpos, y: row.ypos, width: row.width,
|
||||
supportingEvidenceIds: eventEvidence.get(row.id) || [],
|
||||
config: row.exhibit_type_id === 'folder' ? { open: Boolean(row.is_open) } : {}, containedDocumentIds: contained.get(row.id) || [],
|
||||
}))
|
||||
return { id: level.slug, title: level.title, subtitle: level.subtitle, documents, evidence, relations,
|
||||
@@ -208,6 +214,14 @@ export function createLevelRepository(pool: Pool, editingEnabled: boolean): Leve
|
||||
await client.query(`INSERT INTO osint.folder_memberships (board_id,folder_exhibit_id,child_exhibit_id,sort_order)
|
||||
VALUES ($1,$2,$3,$4)`, [level.board_id, relation.fromWidgetId, relation.toWidgetId, relation.sortOrder || 0])
|
||||
}
|
||||
for (const event of state.evidence.filter(item => item.type === 'event')) {
|
||||
for (const [sortOrder, evidenceId] of (event.supportingEvidenceIds || []).entries()) {
|
||||
if (evidenceId === event.id || !allIds.includes(evidenceId)) throw new Error('Event evidence references an unknown or identical exhibit')
|
||||
await client.query(`INSERT INTO osint.event_evidence
|
||||
(board_id,event_exhibit_id,evidence_exhibit_id,sort_order) VALUES ($1,$2,$3,$4)`,
|
||||
[level.board_id, event.id, evidenceId, sortOrder])
|
||||
}
|
||||
}
|
||||
for (const connection of state.connections) {
|
||||
requireUuid(connection.id, 'Connection id')
|
||||
if (!evidenceIds.has(connection.fromEvidenceId) || !evidenceIds.has(connection.toEvidenceId)) throw new Error('Connection references an unknown exhibit')
|
||||
|
||||
Reference in New Issue
Block a user