diff --git a/docs/concepts/publisering.md b/docs/concepts/publisering.md index 533cc2a..a34742d 100644 --- a/docs/concepts/publisering.md +++ b/docs/concepts/publisering.md @@ -248,6 +248,13 @@ samtale, ikke en workflow-tilstand. ## Presentasjonselementer er noder +> **Status:** Implementert i oppgave 14.16. Backend: query-endpoint +> (`/query/presentation_elements`), rendering bruker presentasjonselementer +> (title, subtitle, summary, og_image) med fallback til artikkelnoden. +> Frontend: PresentationEditor-komponent integrert i PublishDialog. +> A/B-testing (automatisk rotasjon, impression-logging) er spesifisert +> men ikke implementert (planlagt oppgave 14.17). + En ingress er en tekst. En overskrift er en tekst. Et forsidebilde er et bilde. Alt som vises *om* en artikkel på forsiden er en *ting med egen forfatter, eget tidspunkt, og potensielt flere varianter*. Det diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 1aab86b..1a23373 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -567,6 +567,51 @@ export async function audioInfo(accessToken: string, hash: string): Promise; + edge_metadata: Record; + created_at: string; +} + +export interface PresentationElementsResponse { + article_id: string; + elements: PresentationElement[]; +} + +/** Hent presentasjonselementer (tittel, undertittel, ingress, OG-bilde) for en artikkel. */ +export async function fetchPresentationElements( + accessToken: string, + articleId: string +): Promise { + const res = await fetch( + `${BASE_URL}/query/presentation_elements?article_id=${encodeURIComponent(articleId)}`, + { headers: { Authorization: `Bearer ${accessToken}` } } + ); + if (!res.ok) { + const body = await res.text(); + throw new Error(`presentation_elements failed (${res.status}): ${body}`); + } + return res.json(); +} + +/** Slett en node (brukes for å fjerne presentasjonselementer). */ +export function deleteNode( + accessToken: string, + nodeId: string +): Promise<{ deleted: boolean }> { + return post(accessToken, '/intentions/delete_node', { node_id: nodeId }); +} + /** Anvend brukerens per-segment-valg etter re-transkripsjon. */ export function resolveRetranscription( accessToken: string, diff --git a/frontend/src/lib/components/PresentationEditor.svelte b/frontend/src/lib/components/PresentationEditor.svelte new file mode 100644 index 0000000..a3e48a3 --- /dev/null +++ b/frontend/src/lib/components/PresentationEditor.svelte @@ -0,0 +1,349 @@ + + +
+ {#if loading} +

Laster presentasjonselementer...

+ {:else} + {#if error} +
+ {error} +
+ {/if} + + +
+ + +
+ + +
+ Publisert tittel + {#each titles as el} +
+ {getDisplayValue(el)} + {getVariant(el)} + {#if getAbStatus(el)} + {getAbStatus(el)} + {/if} + +
+ {/each} +
+ + +
+
+ + +
+ Undertittel + {#each subtitles as el} +
+ {getDisplayValue(el)} + {getVariant(el)} + +
+ {/each} +
+ + +
+
+ + +
+ Ingress + {#each summaries as el} +
+ {getDisplayValue(el)} + {getVariant(el)} + +
+ {/each} +
+ + +
+
+ + +
+ OG-bilde (forside/deling) + {#each ogImages as el} +
+ {#if el.metadata?.cas_hash} + OG-bilde + {/if} + {getVariant(el)} + +
+ {/each} +
+ +
+
+ + {#if elements.length > 1} +

+ Flere varianter av samme type aktiverer automatisk A/B-testing. +

+ {/if} + {/if} +
diff --git a/frontend/src/lib/components/PublishDialog.svelte b/frontend/src/lib/components/PublishDialog.svelte index f7596d4..19e8b0a 100644 --- a/frontend/src/lib/components/PublishDialog.svelte +++ b/frontend/src/lib/components/PublishDialog.svelte @@ -1,6 +1,7 @@