From 1b1784f4eed0c5691388b01242efcacdc821539e Mon Sep 17 00:00:00 2001 From: Justin Visser Date: Thu, 26 Mar 2026 15:35:19 +0100 Subject: [PATCH] Added source segment --- .gitignore | 5 +- backend/app/models.py | 1 + .../src/components/admin/AddSegmentModal.vue | 94 +++++++++++- .../src/components/admin/SegmentEditor.vue | 137 +++++++++++++++++- .../components/segments/SegmentRenderer.vue | 2 + .../components/segments/SourcesSegment.vue | 30 ++++ frontend/src/types/segment.ts | 11 +- frontend/src/utils/formatApa.ts | 37 +++++ 8 files changed, 311 insertions(+), 6 deletions(-) create mode 100644 frontend/src/components/segments/SourcesSegment.vue create mode 100644 frontend/src/utils/formatApa.ts diff --git a/.gitignore b/.gitignore index 0830dd4..6a590d7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,7 @@ data/ Thumbs.db .claude/ -.claude \ No newline at end of file +.claude + +# Content drafts +drafts/ \ No newline at end of file diff --git a/backend/app/models.py b/backend/app/models.py index 43aa10d..b31f518 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -13,6 +13,7 @@ class SegmentType(StrEnum): IFRAME = "iframe" GALLERY = "gallery" TEAM = "team" + SOURCES = "sources" class Segment(BaseModel): diff --git a/frontend/src/components/admin/AddSegmentModal.vue b/frontend/src/components/admin/AddSegmentModal.vue index 7c5d4c4..b758725 100644 --- a/frontend/src/components/admin/AddSegmentModal.vue +++ b/frontend/src/components/admin/AddSegmentModal.vue @@ -1,8 +1,9 @@ + + diff --git a/frontend/src/types/segment.ts b/frontend/src/types/segment.ts index 8edc4f0..ac2b132 100644 --- a/frontend/src/types/segment.ts +++ b/frontend/src/types/segment.ts @@ -1,4 +1,13 @@ -export type SegmentType = "markdown" | "pdf" | "video" | "audio" | "iframe" | "gallery" | "team"; +export type SegmentType = "markdown" | "pdf" | "video" | "audio" | "iframe" | "gallery" | "team" | "sources"; + +export interface Source { + id: string; + authors: string; + year: string; + title: string; + source: string; + url: string; +} export interface Segment { id: string; diff --git a/frontend/src/utils/formatApa.ts b/frontend/src/utils/formatApa.ts new file mode 100644 index 0000000..0ca9f54 --- /dev/null +++ b/frontend/src/utils/formatApa.ts @@ -0,0 +1,37 @@ +import type { Source } from "@/types/segment"; + +function escapeHtml(text: string): string { + return text.replace(/&/g, "&").replace(//g, ">"); +} + +function formatUrl(url: string): string { + const escaped = escapeHtml(url); + return `${escaped}`; +} + +export function formatApaCitation(source: Source): string { + const parts: string[] = []; + + if (source.authors) { + const authors = escapeHtml(source.authors); + parts.push(authors.endsWith(".") ? authors : `${authors}.`); + } + + parts.push(`(${escapeHtml(source.year || "n.d.")}).`); + + if (source.title) { + const title = escapeHtml(source.title); + parts.push(title.endsWith(".") ? `${title}` : `${title}.`); + } + + if (source.source) { + const src = escapeHtml(source.source); + parts.push(src.endsWith(".") ? `${src}` : `${src}.`); + } + + if (source.url) { + parts.push(formatUrl(source.url)); + } + + return parts.join(" ").replace(/\.\./g, "."); +}