diff --git a/.project/concepts/app-architecture/_debt.md b/.project/concepts/app-architecture/_debt.md new file mode 100644 index 0000000..59c4409 --- /dev/null +++ b/.project/concepts/app-architecture/_debt.md @@ -0,0 +1,7 @@ +# Doc-debt — app-architecture + +> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file. +> One block = one "big change": `why` + `files` + `verify` (leaves to review). + +## Raw markers (consolidate into blocks, then delete) +- TOUCHED src/shared/context/FestipodDataContext.tsx @2026-08-17 (session 0b064e8b-1717-421f-a20e-a4318ad217b1) diff --git a/.project/concepts/data-layer/_debt.md b/.project/concepts/data-layer/_debt.md new file mode 100644 index 0000000..f4ad808 --- /dev/null +++ b/.project/concepts/data-layer/_debt.md @@ -0,0 +1,9 @@ +# Doc-debt — data-layer + +> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file. +> One block = one "big change": `why` + `files` + `verify` (leaves to review). + +## Raw markers (consolidate into blocks, then delete) +- TOUCHED src/shared/shapes/shex/festipodShapes.shex @2026-08-17 (session 0b064e8b-1717-421f-a20e-a4318ad217b1) +- TOUCHED src/shared/context/FestipodDataContext.tsx @2026-08-17 (session 0b064e8b-1717-421f-a20e-a4318ad217b1) +- TOUCHED src/shared/data/shapeAdapters.ts @2026-08-17 (session 0b064e8b-1717-421f-a20e-a4318ad217b1) diff --git a/.project/concepts/data-layer/contract_polyfill-surface.md b/.project/concepts/data-layer/polyfill-surface/contract_polyfill-surface.md similarity index 100% rename from .project/concepts/data-layer/contract_polyfill-surface.md rename to .project/concepts/data-layer/polyfill-surface/contract_polyfill-surface.md diff --git a/.project/concepts/tech-stack/_debt.md b/.project/concepts/tech-stack/_debt.md new file mode 100644 index 0000000..941b81d --- /dev/null +++ b/.project/concepts/tech-stack/_debt.md @@ -0,0 +1,7 @@ +# Doc-debt — tech-stack + +> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file. +> One block = one "big change": `why` + `files` + `verify` (leaves to review). + +## Raw markers (consolidate into blocks, then delete) +- TOUCHED package.json @2026-08-17 (session 0b064e8b-1717-421f-a20e-a4318ad217b1) diff --git a/src/shared/context/FestipodDataContext.tsx b/src/shared/context/FestipodDataContext.tsx index 3c5aecf..a9f17d8 100644 --- a/src/shared/context/FestipodDataContext.tsx +++ b/src/shared/context/FestipodDataContext.tsx @@ -1178,6 +1178,12 @@ function useNgData(): FestipodDataContextValue { await openDocumentInbox(eventGraph); const eventId = await writeEntity(eventGraph, ENTITY_TYPE.event, { title: str(event.title), description: str(event.description), date: str(event.date), + // `date` is the human LABEL the form composed; these four are the machine + // values it collected, written UNALTERED as the form's own ISO forms + // (`YYYY-MM-DD`, `HH:MM`). They are stored alongside the label, never + // derived from it — the label is display text and cannot be parsed back. + startDate: str(event.startDate), endDate: str(event.endDate), + startTime: str(event.startTime), endTime: str(event.endTime), location: str(event.location), distance: flt(event.distance), // No host notion: the creator merely SIGNALS a public event and is NOT // obliged to participate, so the count starts at 0 (the owner-materializer @@ -1231,6 +1237,14 @@ function useNgData(): FestipodDataContextValue { if (updates.title !== undefined) persists.push(updateEntityField(graph, id, 'title', str(updates.title))); if (updates.description !== undefined) persists.push(updateEntityField(graph, id, 'description', str(updates.description))); if (updates.date !== undefined) persists.push(updateEntityField(graph, id, 'date', str(updates.date))); + // The machine dates/times travel with the label, not instead of it. A field + // arriving as `''` (the form's empty date input) CLEARS the triple rather than + // leaving a stale value behind — that is `updateEntityField`'s empty handling, + // and it is the right reading here: the user emptied the input. + if (updates.startDate !== undefined) persists.push(updateEntityField(graph, id, 'startDate', str(updates.startDate))); + if (updates.endDate !== undefined) persists.push(updateEntityField(graph, id, 'endDate', str(updates.endDate))); + if (updates.startTime !== undefined) persists.push(updateEntityField(graph, id, 'startTime', str(updates.startTime))); + if (updates.endTime !== undefined) persists.push(updateEntityField(graph, id, 'endTime', str(updates.endTime))); if (updates.location !== undefined) persists.push(updateEntityField(graph, id, 'location', str(updates.location))); if (updates.distance !== undefined) persists.push(updateEntityField(graph, id, 'distance', flt(updates.distance))); await Promise.all(persists).catch(err => console.error(`${logPrefix} persist event update failed:`, err)); diff --git a/src/shared/data/shapeAdapters.ts b/src/shared/data/shapeAdapters.ts index f1cbf6d..24f9d82 100644 --- a/src/shared/data/shapeAdapters.ts +++ b/src/shared/data/shapeAdapters.ts @@ -37,6 +37,15 @@ export function adaptEvent(s: UnionSubject): FpEventData { title: one(s, 'title'), description: one(s, 'description'), date: one(s, 'date'), + // The four machine values, read back exactly as stored (`YYYY-MM-DD`, `HH:MM`). + // `undefined` — not `''` — when the triple is absent: the app type declares them + // optional and the screens branch on their PRESENCE (`event.startTime && …`), so + // an event written before these fields existed reads as an event without them + // rather than one whose dates are blank strings. + startDate: one(s, 'startDate') || undefined, + endDate: one(s, 'endDate') || undefined, + startTime: one(s, 'startTime') || undefined, + endTime: one(s, 'endTime') || undefined, location: one(s, 'location'), distance: s.props[`${FP}distance`] ? num(s, 'distance') : undefined, participantCount: num(s, 'participantCount'), diff --git a/src/shared/shapes/orm/festipodShapes.schema.ts b/src/shared/shapes/orm/festipodShapes.schema.ts index cdbcdc8..bc1e313 100644 --- a/src/shared/shapes/orm/festipodShapes.schema.ts +++ b/src/shared/shapes/orm/festipodShapes.schema.ts @@ -54,6 +54,50 @@ export const festipodShapesSchema = { iri: "http://festipod.org/date", readablePredicate: "date", }, + { + dataTypes: [ + { + valType: "string", + }, + ], + maxCardinality: 1, + minCardinality: 0, + iri: "http://festipod.org/startDate", + readablePredicate: "startDate", + }, + { + dataTypes: [ + { + valType: "string", + }, + ], + maxCardinality: 1, + minCardinality: 0, + iri: "http://festipod.org/endDate", + readablePredicate: "endDate", + }, + { + dataTypes: [ + { + valType: "string", + }, + ], + maxCardinality: 1, + minCardinality: 0, + iri: "http://festipod.org/startTime", + readablePredicate: "startTime", + }, + { + dataTypes: [ + { + valType: "string", + }, + ], + maxCardinality: 1, + minCardinality: 0, + iri: "http://festipod.org/endTime", + readablePredicate: "endTime", + }, { dataTypes: [ { diff --git a/src/shared/shapes/orm/festipodShapes.typings.ts b/src/shared/shapes/orm/festipodShapes.typings.ts index c449347..20f09f5 100644 --- a/src/shared/shapes/orm/festipodShapes.typings.ts +++ b/src/shared/shapes/orm/festipodShapes.typings.ts @@ -40,6 +40,30 @@ export interface Event { * Original IRI: http://festipod.org/date */ date: string; + /** + * The day the event starts, as the ISO calendar date the form collects (YYYY-MM-DD) — stored unaltered, never derived from fp:date + * + * Original IRI: http://festipod.org/startDate + */ + startDate?: string; + /** + * The day the event ends, as an ISO calendar date (YYYY-MM-DD); absent for a single-day event + * + * Original IRI: http://festipod.org/endDate + */ + endDate?: string; + /** + * The time of day the event starts, as the form collects it (HH:MM) + * + * Original IRI: http://festipod.org/startTime + */ + startTime?: string; + /** + * The time of day the event ends (HH:MM) + * + * Original IRI: http://festipod.org/endTime + */ + endTime?: string; /** * The location of the event * diff --git a/src/shared/shapes/shex/festipodShapes.shex b/src/shared/shapes/shex/festipodShapes.shex index 5f94c65..9a92d9b 100644 --- a/src/shared/shapes/shex/festipodShapes.shex +++ b/src/shared/shapes/shex/festipodShapes.shex @@ -10,6 +10,14 @@ fp:Event { // rdfs:comment "A description of the event" ; fp:date xsd:string // rdfs:comment "The display date of the event (e.g. 'Lun. 16 - Ven. 20 fév.')" ; + fp:startDate xsd:string ? + // rdfs:comment "The day the event starts, as the ISO calendar date the form collects (YYYY-MM-DD) — stored unaltered, never derived from fp:date" ; + fp:endDate xsd:string ? + // rdfs:comment "The day the event ends, as an ISO calendar date (YYYY-MM-DD); absent for a single-day event" ; + fp:startTime xsd:string ? + // rdfs:comment "The time of day the event starts, as the form collects it (HH:MM)" ; + fp:endTime xsd:string ? + // rdfs:comment "The time of day the event ends (HH:MM)" ; fp:location xsd:string // rdfs:comment "The location of the event" ; fp:distance xsd:float ?