feat(team): public Team page + HQ CMS panel
New "Team" section — a LinkedIn-style minimal profile page for the FLUX team, fully editable from the HQ Command Center. Data model: - New TeamMember model (name, role, bio, photoUrl, optional social links: email/linkedin/x/website, order, isActive, translationsJson). - Additive migration 20260602120000_add_team_member (IF NOT EXISTS guards). - Name stays as written; role + bio are translatable via the AI engine. HQ panel (/hq-command/dashboard/team): - Drag-to-reorder (same HTML5 pattern as the Hero panel). - Inline auto-save for name/role/visibility; expandable editor for photo upload, bio, social links, and AI auto-translate to IT/VEC/ES/DE. - Photo upload reuses /api/assets with a new flat "team" scope -> /public/team/. - Dashboard tile added. Public page (/[locale]/team): - Responsive card grid (framer-motion stagger), portrait + name + role + bio + social icons (only the links that exist render). - Per-member Person JSON-LD + breadcrumb for SEO. - Localized via getLocalizedData; new TeamPage namespace in all 5 locales. - NavBar item "Team" inserted before "Spare Parts" (translated 5 locales). - Added to sitemap. Infra: - "team" scope registered in /api/assets (SCOPE_ROOTS + FLAT_SCOPES + buildPublicUrl) and revalidate.ts (RevalidateScope + path). - Nginx serves /team/ from disk; docker-compose mounts public/team in both app and nginx (rw + ro). Verified: production build compiles, all 5 /[locale]/team routes + the HQ panel render; TypeScript clean; 5 message files valid JSON. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,10 +43,12 @@ const SCOPE_ROOTS: Record<string, string> = {
|
||||
footage: path.join(process.cwd(), "public", "footage", "main"),
|
||||
// 🔥 NUEVO: Site-wide brand assets (favicon, logo, OG image)
|
||||
branding: path.join(process.cwd(), "public", "branding"),
|
||||
// 🔥 NUEVO: Team member portraits (flat folder, slug ignored)
|
||||
team: path.join(process.cwd(), "public", "team"),
|
||||
};
|
||||
|
||||
// Scopes that ignore the `slug` parameter and write directly under their root.
|
||||
const FLAT_SCOPES = new Set(["footage", "branding"]);
|
||||
const FLAT_SCOPES = new Set(["footage", "branding", "team"]);
|
||||
|
||||
const MEDIA_TYPES: Record<string, string[]> = {
|
||||
image: [".jpg", ".jpeg", ".png", ".webp", ".gif", ".svg", ".avif"],
|
||||
@@ -104,6 +106,7 @@ function buildSafePath(scope: string, slug: string, subPath?: string): string |
|
||||
function buildPublicUrl(scope: string, slug: string, rel: string): string {
|
||||
if (scope === "footage") return `/footage/main/${rel}`;
|
||||
if (scope === "branding") return `/branding/${rel}`;
|
||||
if (scope === "team") return `/team/${rel}`;
|
||||
return `/${scope}/${slug}/${rel}`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user