feat: hero carousel CMS + responsive mobile/iPad fix + flat-scope assets
Replaces the filesystem-scan hero (fs.readdirSync of /public/footage/main) with a fully CMS-driven HeroSlide model. Editors can now drag-drop reorder, toggle slides on/off, set focal points for proper mobile cropping, and auto-translate per-slide captions. NEW SCHEMA (additive — does not touch existing tables) - HeroSlide: mediaUrl, mediaType, altText, order, isActive, focalPointX, focalPointY, translationsJson, timestamps - SiteSetting: key-value JSON store for site-wide config (favicon, logo, footer, OG image) — wired up in next commit - Migration 20260504120000_add_hero_slides_and_site_settings/migration.sql uses CREATE TABLE IF NOT EXISTS, additive only HERO REEL REFACTOR (Bug #4 — responsive mobile/iPad) - Switches from `images: string[]` to `slides: HeroSlideData[]` while keeping a backwards-compat path so legacy callers still work - w-screen → w-full max-w-[100vw] (no horizontal scroll on iOS) - h-[100vh] → h-[100svh] so iOS Safari URL bar doesn't push content - Reduces title font sizes on small viewports (text-3xl → text-4xl → text-5xl → text-[5.5rem]) so the headline stays inside the canvas - objectPosition driven by focal-point fields per slide - Native <video> support for video slides HQ COMMAND — /hq-command/dashboard/hero - Drag-drop reorder, click-to-set-focal-point, inline alt-text editing - Auto-save with "Saving…" / "Saved ✓" indicators - Per-slide caption overrides (title, subtitle, descriptions) - Optional one-click AI translation to IT, VEC, ES, DE - Drop-zone uploader → /api/assets (scope=footage, flat folder) API — /api/assets - New flat scopes: "footage" (writes to /public/footage/main) and "branding" (writes to /public/branding) — slug-less for site-wide assets - New buildPublicUrl helper centralises the URL convention - Revalidate helper expanded with branding + settings scopes HOME PAGE - Reads hero slides from DB first; falls back to filesystem scan when HeroSlide table is empty (so production keeps working immediately after migration runs but before the editor populates rows) DEPLOY NOTES - After git pull on VPS, run the migration ONCE: docker compose exec app npx prisma migrate deploy Then: docker compose up -d --build app Existing data (AdminUser w/ 2FA, ClientUser, GlobalNode, Application, TimelineEvent, NewsArticle, HeritageSection, SparePart, OperationsSignal, NotificationRoute, PageContent) is NOT touched. Migration only creates two new tables.
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
-- ─────────────────────────────────────────────────────────────────────────
|
||||
-- ADDITIVE MIGRATION — only adds new tables, never modifies or drops.
|
||||
-- Existing data (AdminUser, ClientUser w/ 2FA, GlobalNode, etc.) untouched.
|
||||
-- ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
-- HeroSlide: carousel slides shown on the home hero section.
|
||||
-- Replaces filesystem-scan of /public/footage/main with CMS control.
|
||||
CREATE TABLE IF NOT EXISTS "HeroSlide" (
|
||||
"id" TEXT NOT NULL,
|
||||
"mediaUrl" TEXT NOT NULL,
|
||||
"mediaType" TEXT NOT NULL DEFAULT 'image',
|
||||
"altText" TEXT,
|
||||
"order" INTEGER NOT NULL DEFAULT 0,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"focalPointX" DOUBLE PRECISION NOT NULL DEFAULT 0.5,
|
||||
"focalPointY" DOUBLE PRECISION NOT NULL DEFAULT 0.5,
|
||||
"translationsJson" TEXT DEFAULT '{}',
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "HeroSlide_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- SiteSetting: key-value config for favicon, logo, footer, OG image, etc.
|
||||
CREATE TABLE IF NOT EXISTS "SiteSetting" (
|
||||
"id" TEXT NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"valueJson" TEXT NOT NULL DEFAULT '{}',
|
||||
"translationsJson" TEXT DEFAULT '{}',
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "SiteSetting_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "SiteSetting_key_key" ON "SiteSetting"("key");
|
||||
+47
-1
@@ -248,7 +248,53 @@ model PageContent {
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// 11. CLIENT PORTAL (Usuarios B2B Aprobados) 🔥 NUEVO
|
||||
// 11. HERO REEL (Carrusel principal del Home)
|
||||
// ------------------------------------------------------
|
||||
// Manages the rotating images/videos shown in the home hero section.
|
||||
// Replaces the previous filesystem-scan approach (fs.readdirSync of
|
||||
// /public/footage/main) with full CMS control: ordering, on/off toggle,
|
||||
// focal-point per slide for proper responsive cropping on mobile/tablet,
|
||||
// and per-slide alt text for SEO.
|
||||
model HeroSlide {
|
||||
id String @id @default(cuid())
|
||||
mediaUrl String // Public path, e.g. "/footage/main/01_tifas.png"
|
||||
mediaType String @default("image") // "image" | "video"
|
||||
altText String? // For accessibility + SEO; falls back to title if null
|
||||
|
||||
order Int @default(0)
|
||||
isActive Boolean @default(true)
|
||||
|
||||
// Focal point for object-position on mobile/tablet crops (0–1 range).
|
||||
// Lets the editor pick "what should stay visible when the image is cropped".
|
||||
focalPointX Float @default(0.5)
|
||||
focalPointY Float @default(0.5)
|
||||
|
||||
// Optional per-slide caption that overrides the global Hero text.
|
||||
// Stored as JSON keyed by locale: {"en":{"title":"...","subtitle":"..."}}
|
||||
translationsJson String? @default("{}")
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// 12. SITE SETTINGS (Favicon, Footer, Branding global)
|
||||
// ------------------------------------------------------
|
||||
// Single-row pattern (key-value) for global site config that doesn't
|
||||
// fit any other model: favicon, logos, footer, OG image, social links.
|
||||
model SiteSetting {
|
||||
id String @id @default(cuid())
|
||||
key String @unique // e.g. "favicon", "footer", "logo", "og_image", "hero_text"
|
||||
valueJson String @default("{}") // Flexible JSON payload per key
|
||||
|
||||
// 🌍 Translation engine (used for things like footer link labels)
|
||||
translationsJson String? @default("{}")
|
||||
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// 13. CLIENT PORTAL (Usuarios B2B Aprobados)
|
||||
// ------------------------------------------------------
|
||||
model ClientUser {
|
||||
id String @id @default(cuid())
|
||||
|
||||
Reference in New Issue
Block a user