diff --git a/TODO.md b/TODO.md
index c1c92c1..3c5f2eb 100644
--- a/TODO.md
+++ b/TODO.md
@@ -140,7 +140,7 @@ This file is the single source of truth for roadmap and delivery progress.
- [ ] [P1] Page builder with reusable content blocks (hero, rich text, gallery, CTA, forms, price cards)
- [~] [P1] Navigation management (menus, nested items, order, visibility)
- [~] [P1] Media library (upload, browse, replace, delete) with media-type classification (artwork, banner, promo, generic, video/gif)
-- [ ] [P1] Media enrichment metadata (alt text, copyright, author, source, tags, licensing, usage context)
+- [x] [P1] Media enrichment metadata (alt text, copyright, author, source, tags, licensing, usage context)
- [ ] [P1] Portfolio grouping primitives (galleries, albums, categories, tags) with ordering/visibility controls
- [ ] [P1] Artwork refinement fields (medium, dimensions, year, framing, availability, price visibility)
- [ ] [P1] Artwork rendition management (thumbnail, card, full, retina/custom sizes)
@@ -343,6 +343,7 @@ This file is the single source of truth for roadmap and delivery progress.
- [2026-02-12] Upload storage is now provider-based (`local` + `s3`) via `CMS_MEDIA_STORAGE_PROVIDER`; admin-side GUI toggle remains a later MVP item.
- [2026-02-12] Media storage keys now use asset-centric layout (`tenant/
+ {t("fields.licenseType")}: {primaryMedia?.licenseType || "-"} +
++ {t("fields.licenseUrl")}: {primaryMedia?.licenseUrl || "-"} +
++ {t("fields.usageContext")}: {primaryMedia?.usageContext || "-"} +
++ {t("fields.location")}: {primaryMedia?.location || "-"} +
++ {t("fields.capturedAt")}:{" "} + {primaryMedia?.capturedAt ? primaryMedia.capturedAt.toLocaleDateString("en-US") : "-"} +
diff --git a/apps/web/src/messages/de.json b/apps/web/src/messages/de.json index 0e9c9b3..1f8c217 100644 --- a/apps/web/src/messages/de.json +++ b/apps/web/src/messages/de.json @@ -87,7 +87,12 @@ "galleries": "Galerien", "albums": "Alben", "categories": "Kategorien", - "tags": "Tags" + "tags": "Tags", + "licenseType": "Lizenztyp", + "licenseUrl": "Lizenz-URL", + "usageContext": "Nutzungskontext", + "location": "Ort", + "capturedAt": "Aufgenommen am" } } } diff --git a/apps/web/src/messages/en.json b/apps/web/src/messages/en.json index 29a1e2f..097b1f4 100644 --- a/apps/web/src/messages/en.json +++ b/apps/web/src/messages/en.json @@ -87,7 +87,12 @@ "galleries": "Galleries", "albums": "Albums", "categories": "Categories", - "tags": "Tags" + "tags": "Tags", + "licenseType": "License type", + "licenseUrl": "License URL", + "usageContext": "Usage context", + "location": "Location", + "capturedAt": "Captured at" } } } diff --git a/apps/web/src/messages/es.json b/apps/web/src/messages/es.json index ec9df7b..48afc05 100644 --- a/apps/web/src/messages/es.json +++ b/apps/web/src/messages/es.json @@ -87,7 +87,12 @@ "galleries": "Galerías", "albums": "Álbumes", "categories": "Categorías", - "tags": "Etiquetas" + "tags": "Etiquetas", + "licenseType": "Tipo de licencia", + "licenseUrl": "URL de licencia", + "usageContext": "Contexto de uso", + "location": "Ubicación", + "capturedAt": "Capturado el" } } } diff --git a/apps/web/src/messages/fr.json b/apps/web/src/messages/fr.json index 909ab53..c2be966 100644 --- a/apps/web/src/messages/fr.json +++ b/apps/web/src/messages/fr.json @@ -87,7 +87,12 @@ "galleries": "Galeries", "albums": "Albums", "categories": "Catégories", - "tags": "Tags" + "tags": "Tags", + "licenseType": "Type de licence", + "licenseUrl": "URL de licence", + "usageContext": "Contexte d'utilisation", + "location": "Lieu", + "capturedAt": "Capturé le" } } } diff --git a/packages/content/src/media.test.ts b/packages/content/src/media.test.ts index 4d23183..bdde640 100644 --- a/packages/content/src/media.test.ts +++ b/packages/content/src/media.test.ts @@ -12,10 +12,14 @@ describe("media schemas", () => { const parsed = createMediaAssetInputSchema.parse({ type: "artwork", title: "Artwork", + licenseType: "CC BY", + usageContext: "homepage hero", + capturedAt: new Date("2026-01-02T10:30:00.000Z"), tags: ["tag-a"], }) expect(parsed.type).toBe("artwork") + expect(parsed.licenseType).toBe("CC BY") expect(parsed.tags).toEqual(["tag-a"]) }) diff --git a/packages/content/src/media.ts b/packages/content/src/media.ts index 5dfcc20..ba724f8 100644 --- a/packages/content/src/media.ts +++ b/packages/content/src/media.ts @@ -20,6 +20,11 @@ export const createMediaAssetInputSchema = z.object({ source: z.string().max(500).optional(), copyright: z.string().max(500).optional(), author: z.string().max(180).optional(), + licenseType: z.string().max(120).optional(), + licenseUrl: z.string().max(500).optional(), + usageContext: z.string().max(300).optional(), + location: z.string().max(180).optional(), + capturedAt: z.date().optional(), tags: z.array(z.string().min(1).max(100)).default([]), storageKey: z.string().max(500).optional(), mimeType: z.string().max(180).optional(), @@ -38,6 +43,11 @@ export const updateMediaAssetInputSchema = z.object({ source: z.string().max(500).nullable().optional(), copyright: z.string().max(500).nullable().optional(), author: z.string().max(180).nullable().optional(), + licenseType: z.string().max(120).nullable().optional(), + licenseUrl: z.string().max(500).nullable().optional(), + usageContext: z.string().max(300).nullable().optional(), + location: z.string().max(180).nullable().optional(), + capturedAt: z.date().nullable().optional(), tags: z.array(z.string().min(1).max(100)).optional(), mimeType: z.string().max(180).nullable().optional(), width: z.number().int().positive().nullable().optional(), diff --git a/packages/db/prisma/migrations/20260212234000_media_enrichment_metadata/migration.sql b/packages/db/prisma/migrations/20260212234000_media_enrichment_metadata/migration.sql new file mode 100644 index 0000000..1ff0182 --- /dev/null +++ b/packages/db/prisma/migrations/20260212234000_media_enrichment_metadata/migration.sql @@ -0,0 +1,6 @@ +ALTER TABLE "MediaAsset" + ADD COLUMN "licenseType" TEXT, + ADD COLUMN "licenseUrl" TEXT, + ADD COLUMN "usageContext" TEXT, + ADD COLUMN "location" TEXT, + ADD COLUMN "capturedAt" TIMESTAMP(3); diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index c7d46b3..c1c9fc3 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -123,6 +123,11 @@ model MediaAsset { source String? copyright String? author String? + licenseType String? + licenseUrl String? + usageContext String? + location String? + capturedAt DateTime? tags String[] storageKey String? @unique mimeType String? diff --git a/packages/db/src/media-foundation.ts b/packages/db/src/media-foundation.ts index 11831bc..cfaf22f 100644 --- a/packages/db/src/media-foundation.ts +++ b/packages/db/src/media-foundation.ts @@ -484,6 +484,11 @@ export async function getPublishedArtworkBySlug(slug: string) { source: true, author: true, copyright: true, + licenseType: true, + licenseUrl: true, + usageContext: true, + location: true, + capturedAt: true, tags: true, }, },