feat(admin): music library admin + fix CRM analytics UTC
Build backend images / build content-svc (push) Failing after 1m7s
Build backend images / build file-svc (push) Failing after 50s
Build backend images / build gateway (push) Failing after 59s
Build backend images / build identity-svc (push) Failing after 56s
Build backend images / build notification-svc (push) Failing after 1m0s
Build backend images / build render-svc (push) Failing after 1m0s
Build backend images / build studio-svc (push) Failing after 56s

- /admin/music: list / upload / delete studio audio tracks (content-svc
  GET/POST/DELETE /v1/music) — fills the legacy music-library gap
- fix: CRM analytics coerced query-bound dates to UTC (Npgsql timestamptz
  rejects Kind=Unspecified) — endpoint was returning 400

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-02 22:17:14 +03:30
parent 2c961b123b
commit 3acd366fda
6 changed files with 44 additions and 4 deletions
+1
View File
@@ -22,6 +22,7 @@ export default async function AdminLayout({
{ href: "/admin/ranking", label: t("ranking") },
{ href: "/admin/tags", label: t("tags") },
{ href: "/admin/fonts", label: t("fonts") },
{ href: "/admin/music", label: t("music") },
{ href: "/admin/blogs", label: t("blogs") },
{ href: "/admin/slides", label: t("slides") },
{ href: "/admin/files", label: t("media") },
+8
View File
@@ -0,0 +1,8 @@
"use client";
import { AdminResource } from "@/components/admin/AdminResource";
import { musicConfig } from "@/components/admin/admin-resources";
export default function Page() {
return <AdminResource config={musicConfig} />;
}
+28
View File
@@ -109,6 +109,34 @@ export const fontsConfig: ResourceConfig = {
],
};
export const musicConfig: ResourceConfig = {
title: "Music",
description: "Audio tracks available in the studio music library.",
basePath: "music",
listKey: "items",
canCreate: true,
canEdit: false,
canDelete: true,
columns: [
{ key: "name", label: "Name" },
{ key: "genre", label: "Genre" },
{ key: "mood", label: "Mood" },
{ key: "duration_sec", label: "Duration (s)" },
{ key: "is_premium", label: "Premium", render: (r) => badge(!!r.is_premium, "premium", "free") },
],
fields: [
{ key: "name", label: "Name", required: true },
{ key: "caption", label: "Caption" },
{ key: "keywords", label: "Keywords" },
{ key: "url", label: "Audio file", type: "file", required: true },
{ key: "duration_sec", label: "Duration (seconds)", type: "number", required: true },
{ key: "bpm", label: "BPM", type: "number" },
{ key: "genre", label: "Genre" },
{ key: "mood", label: "Mood" },
{ key: "is_premium", label: "Premium", type: "checkbox" },
],
};
export const blogsConfig: ResourceConfig = {
title: "Blog Posts",
description: "CMS articles (also created by the AI SEO generator).",