diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index d57148c..00bd247 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -467,6 +467,30 @@ export function audioProcess( }); } +// ============================================================================= +// Publisering / Forside-slots +// ============================================================================= + +export interface SetSlotRequest { + edge_id: string; + slot: string | null; + slot_order?: number; + pinned?: boolean; +} + +export interface SetSlotResponse { + edge_id: string; + displaced: string[]; +} + +/** Sett slot-metadata (hero/featured/strøm) på en belongs_to-edge. */ +export function setSlot( + accessToken: string, + data: SetSlotRequest +): Promise { + return post(accessToken, '/intentions/set_slot', data); +} + /** Hent metadata om lydfil (ffprobe). */ export async function audioInfo(accessToken: string, hash: string): Promise { const res = await fetch(`${BASE_URL}/query/audio_info?hash=${encodeURIComponent(hash)}`, { diff --git a/frontend/src/lib/components/traits/PublishingTrait.svelte b/frontend/src/lib/components/traits/PublishingTrait.svelte index b27be1d..f26a6c2 100644 --- a/frontend/src/lib/components/traits/PublishingTrait.svelte +++ b/frontend/src/lib/components/traits/PublishingTrait.svelte @@ -12,6 +12,8 @@ const slug = $derived((config.slug as string) ?? ''); const theme = $derived((config.theme as string) ?? 'default'); const customDomain = $derived((config.custom_domain as string) ?? ''); + const indexMode = $derived((config.index_mode as string) ?? 'dynamic'); + const featuredMax = $derived((config.featured_max as number) ?? 4); @@ -33,6 +35,29 @@
{customDomain}
{/if} +
+
Forsidemodus
+
{indexMode === 'static' ? 'Statisk' : 'Dynamisk'} · {featuredMax} fremhevede
+
+ +
+ + Rediger forside + + {#if slug} + + Se publisert + + {/if} +
{/snippet}
diff --git a/frontend/src/routes/collection/[id]/forside/+page.svelte b/frontend/src/routes/collection/[id]/forside/+page.svelte new file mode 100644 index 0000000..7e25b97 --- /dev/null +++ b/frontend/src/routes/collection/[id]/forside/+page.svelte @@ -0,0 +1,519 @@ + + +
+ +
+
+
+ ← Samling +

+ Forside-admin +

+ {#if collectionNode} + {collectionNode.title} + {/if} +
+
+ {#if connected} + Tilkoblet + {:else} + {connectionState.current} + {/if} + {articles.length} artikler + Tema: {theme} + {#if previewUrl} + + {/if} +
+
+
+ +
+ {#if lastError} +
+ {lastError} + +
+ {/if} + + {#if isUpdating} +
+ Oppdaterer… +
+ {/if} + + {#if !connected} +

Venter på tilkobling…

+ {:else if !collectionNode} +
+ Samling ikke funnet. + Tilbake +
+ {:else if !slug} +
+ Denne samlingen har ikke publishing-trait med slug konfigurert. +
+ {:else} +
+ +
+

Hero (maks 1)

+
handleDragOver(e, 'hero')} + ondragleave={handleDragLeave} + ondrop={(e: DragEvent) => handleDrop(e, 'hero')} + role="region" + aria-label="Hero-sone" + > + {#if heroArticle} +
handleDragStart(e, heroArticle)} + ondragend={handleDragEnd} + > +
+
+

{articleTitle(heroArticle.node)}

+ {#if articleExcerpt(heroArticle.node)} +

{articleExcerpt(heroArticle.node)}

+ {/if} + {#if heroArticle.publishAt} +

{formatDate(heroArticle.publishAt)}

+ {/if} +
+
+ + +
+
+
+ {:else} +
+ Dra en artikkel hit for å sette som hero +
+ {/if} +
+
+ + +
+

+ Fremhevet ({featuredArticles.length}/{featuredMax}) +

+
handleDragOver(e, 'featured')} + ondragleave={handleDragLeave} + ondrop={(e: DragEvent) => handleDrop(e, 'featured')} + role="region" + aria-label="Fremhevet-sone" + > + {#if featuredArticles.length > 0} +
+ {#each featuredArticles as article (article.edge.id)} +
handleDragStart(e, article)} + ondragend={handleDragEnd} + > +
+
+

{articleTitle(article.node)}

+ {#if article.publishAt} +

{formatDate(article.publishAt)}

+ {/if} +
+
+ + + +
+
+
+ {/each} +
+ {:else} +
+ Dra artikler hit for fremheving +
+ {/if} +
+
+ + +
+

+ Strøm ({streamArticles.length}) +

+
handleDragOver(e, 'stream')} + ondragleave={handleDragLeave} + ondrop={(e: DragEvent) => handleDrop(e, 'stream')} + role="region" + aria-label="Strøm-sone" + > + {#if streamArticles.length > 0} +
+ {#each streamArticles as article (article.edge.id)} +
handleDragStart(e, article)} + ondragend={handleDragEnd} + > +
+

{articleTitle(article.node)}

+ {#if articleExcerpt(article.node)} +

{articleExcerpt(article.node)}

+ {/if} +
+
+ {#if article.publishAt} + {formatDate(article.publishAt)} + {/if} + + + +
+
+ {/each} +
+ {:else} +
+ Ingen artikler i strømmen +
+ {/if} +
+
+
+ + + {#if showPreview && previewUrl} +
+
+
+ Forhåndsvisning + + Åpne i ny fane + +
+ +
+
+ {/if} + {/if} +
+
+ + diff --git a/tasks.md b/tasks.md index b46ca84..1df68ba 100644 --- a/tasks.md +++ b/tasks.md @@ -145,8 +145,7 @@ Uavhengige faser kan fortsatt plukkes. - [x] 14.3 Forside-rendering: maskinrommet spør PG for hero/featured/strøm (tre indekserte spørringer), appliserer tema-template, rendrer til CAS (statisk modus) eller serverer med in-memory cache (dynamisk modus). `index_mode` og `index_cache_ttl` i trait-konfig. - [x] 14.4 Caddy-ruting for synops.no/pub: Caddy reverse-proxyer til maskinrommet som gjør slug→hash-oppslag og streamer CAS-fil. `Cache-Control: immutable` for artikler. Kategori/arkiv/søk serveres dynamisk av maskinrommet med kortere cache-TTL. - [x] 14.5 Slot-håndtering i maskinrommet: `slot` og `slot_order` i `belongs_to`-edge metadata. Ved ny hero → gammel hero flyttes til strøm. Ved featured over `featured_max` → FIFO tilbake til strøm. `pinned`-flagg forhindrer automatisk fjerning. -- [~] 14.6 Forside-admin i frontend: visuell editor for hero/featured/strøm. Drag-and-drop mellom plasser. Pin-knapp. Forhåndsvisning. Oppdaterer edge-metadata via maskinrommet. - > Påbegynt: 2026-03-18T01:17 +- [x] 14.6 Forside-admin i frontend: visuell editor for hero/featured/strøm. Drag-and-drop mellom plasser. Pin-knapp. Forhåndsvisning. Oppdaterer edge-metadata via maskinrommet. - [ ] 14.7 Publiseringsflyt i frontend (personlig): publiseringsknapp på noder i samlinger med `publishing`-trait der `require_approval: false`. Forhåndsvisning, slug-editor, bekreftelse. Avpublisering ved fjerning av edge. - [ ] 14.8 RSS/Atom-feed: samling med `rss`-trait genererer feed automatisk ved publisering/avpublisering. `synops.no/pub/{slug}/feed.xml`. Maks `rss_max_items` (default 50). - [ ] 14.9 Custom domains: bruker registrerer domene i `publishing`-trait. Maskinrommet validerer DNS, Caddy on-demand TLS med validerings-callback. Re-rendring med riktig canonical URL.