diff --git a/apps/admin/src/components/products/ProductStoryScrollForm.tsx b/apps/admin/src/components/products/ProductStoryScrollForm.tsx new file mode 100644 index 0000000..a56900f --- /dev/null +++ b/apps/admin/src/components/products/ProductStoryScrollForm.tsx @@ -0,0 +1,390 @@ +import { useState, useEffect } from 'react' +import { + Sparkles, + Upload, + Image as ImageIcon, + X, + Layers, + CheckCircle, + RotateCcw, +} from 'lucide-react' + +export interface StorySceneItem { + id: string + kicker: string + title: string + description: string + metric?: string + chips?: string[] + theme: string + imageUrl?: string +} + +const DEFAULT_SCENES: StorySceneItem[] = [ + { + id: 'hero', + kicker: '01 / PRODUCT STORY', + title: 'STOP WITH\nCONFIDENCE.', + description: 'พัฒนาขึ้นสำหรับการแข่งขันและการขับขี่สมรรถนะสูง ให้สินค้ากลายเป็นพระเอกก่อนพาลงลึกไปทีละมิติของประสิทธิภาพ', + theme: 'hero', + }, + { + id: 'material', + kicker: '02 / MATERIAL', + title: 'CERAMIC\nCOMPOUND.', + description: 'สูตรผสมเนื้อเซรามิกเกรดพรีเมียม สึกหรอน้อย เสียงเบียบเงียบ ไร้แรงสั่นสะเทือน และลดฝุ่นผงจับจานเบรกอย่างเห็นได้ชัด', + chips: ['Low Noise', 'Low Vibration', 'Cleaner Braking'], + theme: 'material', + }, + { + id: 'heat', + kicker: '03 / HEAT CONTROL', + title: 'HIGH-HEAT\nCONTROL.', + metric: '300°C', + description: 'ทนต่อความร้อนสูงสะสม ป้องกันอาการเบรกเฟด (Brake Fade) ให้ระยะหยุดสม่ำเสมอแม้ใช้งานต่อเนื่องบนสนามแข่งหรือทางลาดชัน', + theme: 'heat', + }, + { + id: 'friction', + kicker: '04 / PERFORMANCE', + title: 'STABLE\nFRICTION.', + description: 'ค่าสัมประสิทธิ์แรงเสียดทานคงที่ในทุกช่วงอุณหภูมิ ให้การจับตัวนุ่มนวลแต่แน่นหนา มั่นใจในทุกโค้ง', + theme: 'friction', + }, + { + id: 'control', + kicker: '05 / CONTROL', + title: 'SMOOTH\nENGAGEMENT.', + description: 'โครงสร้างแม่นยำมาตรฐานสูง สัมผัสก้านเบรกตอบสนองเป็นเส้นตรง (Linear Modulation) คุมน้ำหนักเบรกได้ละเอียดดั่งใจ', + theme: 'control', + }, + { + id: 'durability', + kicker: '06 / DURABILITY', + title: 'BUILT\nTO LAST.', + description: 'เนื้อผ้าเบรกถนอมจานเบรก ไม่กินจาน ยืดอายุการใช้งานยาวนาน คุ้มค่าสำหรับการขับขี่ทั้งในชีวิตประจำวันและทริปเดินทางไกล', + chips: ['Durable', 'Rotor Friendly', 'Everyday Use'], + theme: 'durability', + }, + { + id: 'fitment', + kicker: '07 / FITMENT', + title: 'MADE FOR\nYOUR BIKE.', + description: 'ออกแบบตรงรุ่นพร้อมติดตั้งแบบ Plug & Play ครอบคลุมรถจักรยานยนต์ยอดนิยมทุกค่าย', + theme: 'fitment', + }, +] + +interface Props { + payload: Record + onChange: (nextPayload: Record) => void + onBrowseMedia: (onSelect: (url: string) => void) => void +} + +export default function ProductStoryScrollForm({ payload, onChange, onBrowseMedia }: Props) { + const [selectedSceneIndex, setSelectedSceneIndex] = useState(0) + + const heroImage = (payload.storyHeroImage as string) || '' + const endCtaTitle = (payload.endCtaTitle as string) || '' + const endCtaButtonText = (payload.endCtaButtonText as string) || '' + + // Auto-seed default mockup scenes on first open if empty + useEffect(() => { + if (!payload.storyScenes || !Array.isArray(payload.storyScenes) || payload.storyScenes.length === 0) { + onChange({ + ...payload, + storyScenes: DEFAULT_SCENES, + endCtaTitle: payload.endCtaTitle || 'SELECT YOUR MODEL.', + endCtaButtonText: payload.endCtaButtonText || 'Choose Your Bike', + }) + } + }, []) + + const rawScenes = payload.storyScenes + const scenes: StorySceneItem[] = Array.isArray(rawScenes) && rawScenes.length > 0 + ? (rawScenes as any[]).map((s, idx) => ({ + ...DEFAULT_SCENES[idx % DEFAULT_SCENES.length], + ...s, + })) + : DEFAULT_SCENES + + const updateScenes = (newScenes: StorySceneItem[]) => { + onChange({ + ...payload, + storyScenes: newScenes, + }) + } + + const handleResetToMockup = () => { + onChange({ + ...payload, + storyScenes: DEFAULT_SCENES, + endCtaTitle: 'SELECT YOUR MODEL.', + endCtaButtonText: 'Choose Your Bike', + }) + } + + const updateSelectedScene = (field: keyof StorySceneItem, val: any) => { + const updated = [...scenes] + updated[selectedSceneIndex] = { + ...updated[selectedSceneIndex], + [field]: val, + } + updateScenes(updated) + } + + const currentScene = scenes[selectedSceneIndex] || scenes[0] + + return ( +
+ {/* Banner Intro */} +
+
+ +
+

+ โหมดนำเสนอพิเศษ: Cinematic Story Scroll (Apple Style) +

+

+ เมื่อเปิดโหมดนี้ หน้าสินค้าบนจอ PC Wide จะแสดงส่วน Scrollytelling เต็มจอ 100vh เล่าเรื่องเทคโนโลยี 7 ฉากแบบภาพยนตร์ไฮเทค พร้อมปุ่มสั่งซื้อที่ด้านล่างสุด +

+
+
+ +
+ + {/* 1. Core Hero Showcase Image */} +
+
+
+

+ + ภาพสินค้าหลักสำหรับโชว์ใน Story Stage +

+

+ แนะนำใช้ภาพสินค้าไดคัทพื้นหลังโปร่งใส (Transparent PNG หรือ WebP) เพื่อให้หมุนและเรืองแสงได้เต็มตา (หากไม่ระบุ จะดึงภาพแรกของสินค้าให้อัตโนมัติ) +

+
+
+ +
+
+ {heroImage ? ( + Story Hero + ) : ( + + ใช้ภาพแรกอัตโนมัติ + + )} +
+ +
+
+ + {heroImage && ( + + )} +
+

+ {heroImage ? `URL: ${heroImage.slice(0, 50)}...` : '* ดึงภาพสินค้าที่มีอยู่แล้วมาแสดงอัตโนมัติ'} +

+
+
+
+ + {/* 2. Story Scenes Manager (7 Scenes Tabs) */} +
+
+

+ + ปรับแต่งฉาก Story Scenes ทั้ง 7 ตอน +

+

+ คลิกเลือกตอนที่ต้องการปรับเปลี่ยนข้อความ ตัวเลข หรือใส่รูปภาพเฉพาะฉาก +

+
+ + {/* Scene Tabs */} +
+ {scenes.map((s, idx) => ( + + ))} +
+ + {/* Selected Scene Editor */} + {currentScene && ( +
+
+ + แก้ไขฉากที่ {selectedSceneIndex + 1} ({currentScene.id.toUpperCase()}) + + + ธีมแสง: {currentScene.theme} + +
+ + {/* Custom Image for this Scene */} +
+ +
+
+ {currentScene.imageUrl ? ( + {currentScene.title} + ) : ( + ไม่มีรูป + )} +
+
+ + {currentScene.imageUrl && ( + + )} +
+
+
+ + {/* Kicker & Title */} +
+
+ + updateSelectedScene('kicker', e.target.value)} + className="w-full px-3 py-2 rounded-xl bg-night-950 border border-white/10 text-xs text-white outline-none focus:border-cyan-glow/40" + placeholder="e.g. 02 / MATERIAL" + /> +
+ +
+ + updateSelectedScene('metric', e.target.value)} + className="w-full px-3 py-2 rounded-xl bg-night-950 border border-white/10 text-xs text-white outline-none focus:border-cyan-glow/40" + placeholder="e.g. 300°C หรือ 100%" + /> +
+
+ + {/* Headline */} +
+ +