x
This commit is contained in:
1 parent
7fd3c40ae2
commit
230716933e
2 files changed
+620
-4
No files matched your search
@@ -0,0 +1,496 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import {
|
||||||
|
Sparkles,
|
||||||
|
Copy,
|
||||||
|
Check,
|
||||||
|
X,
|
||||||
|
BookOpen,
|
||||||
|
Flower2,
|
||||||
|
Shirt,
|
||||||
|
Sparkle,
|
||||||
|
Compass,
|
||||||
|
Code2,
|
||||||
|
Layers,
|
||||||
|
Globe2,
|
||||||
|
Upload,
|
||||||
|
} from 'lucide-react'
|
||||||
|
|
||||||
|
interface MenuJsonGuideModalProps {
|
||||||
|
isOpen: boolean
|
||||||
|
onClose: () => void
|
||||||
|
onOpenPasteJson?: () => void
|
||||||
|
onOpenImportFile?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const GUIDE_OVERVIEW_MARKDOWN = `# คู่มือการใช้ AI/LLM สร้าง JSON สำหรับ Main Navigation & Mega Menu
|
||||||
|
|
||||||
|
ระบบเมนูหลัก (Main Navigation) รองรับการนำเข้าโครงสร้างผ่าน **JSON Import** โดยสามารถส่งข้อมูลเข้าสู่ระบบได้ทั้งแบบไฟล์ \`.json\` หรือนำ JSON ไปวางในระบบได้ทันที
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ ข้อกำหนดทางเทคนิคสากล (Mega Menu Schema Rules)
|
||||||
|
|
||||||
|
โครงสร้างเมนูหลักของ DBK Platform / HomAroma ประกอบด้วยลำดับชั้นแบบ 3 ระดับ (3-Tier Hierarchy) ดังนี้:
|
||||||
|
|
||||||
|
### 1. Root Items (เมนูแถวบนสุด - Level 1)
|
||||||
|
- **label**: ออบเจกต์ข้อความรองรับ 8 ภาษา \`{ th, en, ja, zh, vi, ko, lo, it }\`
|
||||||
|
- **url**: ลิงก์ปลายทาง เช่น \`/category/reed-diffuser\` หรือ \`/collections/all\`
|
||||||
|
- **rootImageUrl**: URL รูปภาพหลักฝั่งซ้ายสุดของเมกะเมนู (อัตราส่วน 1:1 จัตุรัส ขนาดแนะนำ 800x800)
|
||||||
|
- **columns**: อาร์เรย์ของคอลัมน์เมนูย่อย (Level 2)
|
||||||
|
- **featured**: การ์ดสินค้า/ไฮไลต์โปรโมตพิเศษที่แสดงฝั่งขวาสุดของเมกะเมนู
|
||||||
|
|
||||||
|
### 2. Columns (คอลัมน์เมนูย่อย - Level 2)
|
||||||
|
- **title**: ออบเจกต์หัวข้อคอลัมน์ 8 ภาษา \`{ th, en, ... }\`
|
||||||
|
- **iconUrl**: (ไม่บังคับ) รูปไอคอนประจำคอลัมน์ (1:1)
|
||||||
|
- **icon**: (ไม่บังคับ) ชื่อไอคอนจาก Lucide Registry เช่น \`Sparkles\`, \`Flame\`, \`Flower\`, \`Heart\`
|
||||||
|
- **links**: อาร์เรย์ของลิงก์ภายในคอลัมน์ (Level 3)
|
||||||
|
|
||||||
|
### 3. Links (ลิงก์รายการสินค้าหรือหมวด - Level 3)
|
||||||
|
- **label**: ออบเจกต์ชื่อลิงก์ 8 ภาษา \`{ th, en, ... }\`
|
||||||
|
- **url**: URL ปลายทาง เช่น \`/products/amber-bloom\` หรือ \`/category/candles\`
|
||||||
|
- **imageUrl**: (ไม่บังคับ) รูปภาพพรีวิวที่จะแสดงฝั่งขวาเมื่อผู้ใช้นำเมาส์ไปชี้ (Hover Image, 1:1)
|
||||||
|
- **links**: (ไม่บังคับ) ลิงก์ย่อยระดับที่ 4 สำหรับโครงสร้างแบบซ้อนลึก
|
||||||
|
|
||||||
|
### 4. Featured Card (การ์ดไฮไลต์แนะนำ)
|
||||||
|
- **enabled**: \`true\` หรือ \`false\`
|
||||||
|
- **sectionTitle**: เช่น \`{ th: "สินค้าแนะนำ", en: "Featured" }\`
|
||||||
|
- **badge**: เช่น \`{ th: "ขายดี", en: "Bestseller" }\`
|
||||||
|
- **title**: ชื่อสินค้าหรือคอลเลกชันไฮไลต์
|
||||||
|
- **subtitle**: คำโปรยสั้นๆ อธิบายความโดดเด่น
|
||||||
|
- **ctaLabel**: ข้อความปุ่ม เช่น \`{ th: "ดูรายละเอียด", en: "Explore" }\`
|
||||||
|
- **imageUrl**: URL รูปภาพความละเอียดสูงสำหรับแบนเนอร์การ์ด (อัตราส่วน 4:3 หรือ 1:1)
|
||||||
|
- **url**: ลิงก์ไปยังหน้าสินค้านั้น
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 รหัสภาษามาตรฐานทั้ง 8 ภาษา (Standard Language Codes)
|
||||||
|
1. \`th\`: ภาษาไทย (ภาษาหลัก)
|
||||||
|
2. \`en\`: English (ภาษาอังกฤษสากล)
|
||||||
|
3. \`ja\`: 日本語 (ภาษาญี่ปุ่น)
|
||||||
|
4. \`zh\`: 简体中文 (ภาษาจีนตัวย่อ)
|
||||||
|
5. \`vi\`: Tiếng Việt (ภาษาเวียดนาม)
|
||||||
|
6. \`ko\`: 한국어 (ภาษาเกาหลี)
|
||||||
|
7. \`lo\`: ພາສາລາວ (ภาษาลาว)
|
||||||
|
8. \`it\`: Italiano (ภาษาอิตาลี)`
|
||||||
|
|
||||||
|
const FRAGRANCE_PROMPT = `คุณคือ Senior E-commerce Information Architect และ Luxury Brand Director เชี่ยวชาญการออกแบบสถาปัตยกรรม Navigation & Mega Menu สำหรับแบรนด์ Luxury Lifestyle, Fine Fragrance, และ Aromatherapy (เทียบเคียงกับ Panpuri, Jo Malone London, Diptyque, Aesop, Byredo)
|
||||||
|
|
||||||
|
[ภารกิจของคุณ]:
|
||||||
|
สร้างโครงสร้างเมนูหลัก (Main Navigation Mega Menu) สำหรับนำเข้าสู่ระบบ E-commerce ในรูปแบบ **Valid JSON Array** ตาม Schema ของระบบอย่างเคร่งครัด
|
||||||
|
|
||||||
|
[กฎและข้อกำหนดทางเทคนิค]:
|
||||||
|
1. ผลลัพธ์ต้องเป็น Valid JSON Array ล้วนๆ เริ่มต้นด้วย [ และจบด้วย ] เท่านั้น (ห้ามมีคำเกริ่นนำหรือคำลงท้าย)
|
||||||
|
2. โครงสร้างเมนูต้องประกอบด้วยเมนูหลัก 4-6 กลุ่ม (Root Items) เช่น:
|
||||||
|
- "Home Ambience" (ก้านไม้หอม, เทียนหอม, สเปรย์ปรับอากาศ)
|
||||||
|
- "Personal Scents" (น้ำหอม Perfume Oil, Solid Perfume, Hair & Body Mist)
|
||||||
|
- "Bath & Body Sanctuary" (ครีมอาบน้ำ, โลชั่นบำรุงผิว, สครับขัดผิว, น้ำมันนวด)
|
||||||
|
- "Gifts & Sets" (เซ็ตของขวัญพรีเมียม, Corporate Gifting, Travel Sets)
|
||||||
|
- "The Scent Stories" (เรื่องราวคอลเลกชันกลิ่นหอม, Aromachology Journal)
|
||||||
|
3. แต่ละ Root Item ต้องมี:
|
||||||
|
- "label": รองรับครบ 8 ภาษา { "th", "en", "ja", "zh", "vi", "ko", "lo", "it" }
|
||||||
|
- "url": ลิงก์หมวดหมู่ เช่น "/category/home-ambience"
|
||||||
|
- "rootImageUrl": รูปภาพ 1:1 ความละเอียดสูงจาก Unsplash เกี่ยวกับเทียนหอม ก้านไม้หอม หรือขวดน้ำหอมหรู
|
||||||
|
- "featured": บัตรแนะนำสินค้าขายดี พร้อม sectionTitle, badge, title, subtitle, ctaLabel, imageUrl, url
|
||||||
|
- "columns": มี 2-4 คอลัมน์ย่อย แต่ละคอลัมน์มี title (8 ภาษา) และ links 3-5 รายการ (พร้อม hover imageUrl)
|
||||||
|
4. ภาษาต้องแปลให้สละสลวย ถ่ายทอดอารมณ์ความหรูหรา และตรงกับบริบทสากล
|
||||||
|
|
||||||
|
[ตัวอย่างรูปแบบ JSON ที่ต้องการ]:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"label": {
|
||||||
|
"th": "เครื่องหอมสำหรับบ้าน",
|
||||||
|
"en": "Home Fragrance",
|
||||||
|
"ja": "ホームフレグランス",
|
||||||
|
"zh": "家居香氛",
|
||||||
|
"vi": "Hương thơm gia đình",
|
||||||
|
"ko": "홈 프래그런스",
|
||||||
|
"lo": "ເຄື່ອງຫອມສຳລັບບ້ານ",
|
||||||
|
"it": "Fragranze per la Casa"
|
||||||
|
},
|
||||||
|
"url": "/category/home-fragrance",
|
||||||
|
"rootImageUrl": "https://images.unsplash.com/photo-1608571423902-eed4a5ad8108?w=800",
|
||||||
|
"featured": {
|
||||||
|
"enabled": true,
|
||||||
|
"sectionTitle": { "th": "คอลเลกชันไฮไลต์", "en": "Featured Collection" },
|
||||||
|
"badge": { "th": "ยอดนิยม", "en": "Best Seller" },
|
||||||
|
"title": { "th": "Siamese Water Reed Diffuser", "en": "Siamese Water Reed Diffuser" },
|
||||||
|
"subtitle": { "th": "ก้านไม้หอมปรับอากาศกลิ่นมะลิหลวงและกระดังงา มอบความสงบผ่อนคลาย", "en": "Signature Jasmine & Ylang Ylang diffuser for pure tranquility" },
|
||||||
|
"ctaLabel": { "th": "เลือกซื้อตอนนี้", "en": "Shop Now" },
|
||||||
|
"imageUrl": "https://images.unsplash.com/photo-1617897903246-719242758050?w=800",
|
||||||
|
"url": "/products/siamese-water-diffuser"
|
||||||
|
},
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"title": {
|
||||||
|
"th": "ก้านไม้หอมปรับอากาศ",
|
||||||
|
"en": "Reed Diffusers",
|
||||||
|
"ja": "リードディフューザー",
|
||||||
|
"zh": "无火香薰",
|
||||||
|
"vi": "Tinh dầu cắm que",
|
||||||
|
"ko": "리드 디퓨저",
|
||||||
|
"lo": "ກ້ານໄມ້ຫອມ",
|
||||||
|
"it": "Diffusori a Midollino"
|
||||||
|
},
|
||||||
|
"icon": "Sparkles",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"label": { "th": "ก้านไม้หอมคลาสสิก 100ml", "en": "Classic Diffusers 100ml" },
|
||||||
|
"url": "/category/diffusers-classic",
|
||||||
|
"imageUrl": "https://images.unsplash.com/photo-1608571423902-eed4a5ad8108?w=800"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": { "th": "น้ำตรีฟิลก้านไม้หอม 200ml", "en": "Diffuser Refills 200ml" },
|
||||||
|
"url": "/category/diffuser-refills",
|
||||||
|
"imageUrl": "https://images.unsplash.com/photo-1605651202774-7d573fd3f12d?w=800"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": {
|
||||||
|
"th": "เทียนหอมอโรมา",
|
||||||
|
"en": "Scented Candles",
|
||||||
|
"ja": "アロマキャンドル",
|
||||||
|
"zh": "香薰蜡烛",
|
||||||
|
"vi": "Nến thơm",
|
||||||
|
"ko": "센티드 캔들",
|
||||||
|
"lo": "ທຽນຫອມ",
|
||||||
|
"it": "Candele Profumate"
|
||||||
|
},
|
||||||
|
"icon": "Flame",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"label": { "th": "เทียนหอมไขถั่วเหลืองธรรมชาติ", "en": "Natural Soy Wax Candles" },
|
||||||
|
"url": "/category/soy-candles",
|
||||||
|
"imageUrl": "https://images.unsplash.com/photo-1592945403244-b3fbafd7f539?w=800"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": { "th": "เทียนหอมขนาดพกพา Travel Tin", "en": "Travel Candle Tins" },
|
||||||
|
"url": "/category/travel-candles",
|
||||||
|
"imageUrl": "https://images.unsplash.com/photo-1543418219-44e30b057fea?w=800"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
[คำสั่ง]:
|
||||||
|
กรุณาสร้าง Mega Menu สำหรับร้านค้า Luxury Fragrance & Aromatherapy จำนวน 5 Root Items ตามโครงสร้างและสเปกด้านบนให้ครบทั้ง 8 ภาษา ส่งเฉพาะ Valid JSON Array เท่านั้น`
|
||||||
|
|
||||||
|
const BEAUTY_PROMPT = `คุณคือ Senior E-commerce Merchandiser และ UX Designer เชี่ยวชาญการออกแบบระบบ Navigation สำหรับเว็บไซต์บิวตี้และสกินแคร์ระดับแนวหน้า (เช่น Sephora, EVEANDBOY, Boots, Olive Young)
|
||||||
|
|
||||||
|
[ภารกิจของคุณ]:
|
||||||
|
สร้างโครงสร้างเมนูหลัก (Main Navigation Mega Menu) สำหรับนำเข้าสู่ระบบ E-commerce ในรูปแบบ **Valid JSON Array** ตาม Schema ของระบบอย่างเคร่งครัด
|
||||||
|
|
||||||
|
[กฎและข้อกำหนดทางเทคนิค]:
|
||||||
|
1. ผลลัพธ์ต้องเป็น Valid JSON Array ล้วนๆ เริ่มต้นด้วย [ และจบด้วย ] เท่านั้น
|
||||||
|
2. หมวดหมู่หลัก (Root Items) เช่น:
|
||||||
|
- "Skincare" (คลีนเซอร์, โทนเนอร์, เซรั่ม, มอยส์เจอไรเซอร์, ครีมกันแดด)
|
||||||
|
- "Makeup" (รองพื้น, แป้งพัฟ, ลิปสติก, อายแชโดว์, บลัชออน)
|
||||||
|
- "Hair & Body" (แชมพู, ทรีตเมนต์ผม, ครีมอาบน้ำ, บอดี้โลชั่น)
|
||||||
|
- "Brands" (แบรนด์ยอดนิยมระดับโลกและแบรนด์ดังในเอเชีย)
|
||||||
|
- "Special Offers" (โปรโมชั่นประจำเดือน, สินค้าซื้อ 1 แถม 1, สิทธิพิเศษสมาชิก)
|
||||||
|
3. แต่ละ Root Item ต้องมี:
|
||||||
|
- "label": รองรับครบ 8 ภาษา { "th", "en", "ja", "zh", "vi", "ko", "lo", "it" }
|
||||||
|
- "url": เช่น "/category/skincare"
|
||||||
|
- "rootImageUrl": รูปภาพความละเอียดสูงจาก Unsplash เกี่ยวกับสกินแคร์ เครื่องสำอาง หรือบิวตี้
|
||||||
|
- "featured": แบนเนอร์ไฮไลต์ผลิตภัณฑ์เด่นประจำหมวด
|
||||||
|
- "columns": 3-4 คอลัมน์แบ่งตามกลุ่มการใช้งานหรือปัญหาผิว (เช่น Cleanser, Treatment & Serum, Sun Protection)
|
||||||
|
|
||||||
|
[คำสั่ง]:
|
||||||
|
กรุณาสร้าง Mega Menu สำหรับร้านค้า Cosmetics & Skincare จำนวน 4-5 Root Items ตามโครงสร้างที่กำหนดให้ครบ 8 ภาษา ส่งเฉพาะ Valid JSON Array เท่านั้น`
|
||||||
|
|
||||||
|
const FASHION_PROMPT = `คุณคือ Senior Fashion Merchandiser และ Digital Platform Architect เชี่ยวชาญการออกแบบโครงสร้างเมนูสำหรับแบรนด์แฟชั่นระดับไฮเอนด์และมัลติแบรนด์สโตร์ (เช่น Zara, COS, Massimo Dutti, SSENSE, Farfetch)
|
||||||
|
|
||||||
|
[ภารกิจของคุณ]:
|
||||||
|
สร้างโครงสร้างเมนูหลัก (Main Navigation Mega Menu) สำหรับนำเข้าสู่ระบบ E-commerce ในรูปแบบ **Valid JSON Array** ตาม Schema ของระบบอย่างเคร่งครัด
|
||||||
|
|
||||||
|
[กฎและข้อกำหนดทางเทคนิค]:
|
||||||
|
1. ผลลัพธ์ต้องเป็น Valid JSON Array ล้วนๆ เริ่มต้นด้วย [ และจบด้วย ] เท่านั้น
|
||||||
|
2. หมวดหมู่หลัก (Root Items) เช่น:
|
||||||
|
- "Women" (เดรส, เสื้อท็อป, กางเกง, กระโปรง, แจ็กเก็ต)
|
||||||
|
- "Men" (เสื้อเชิ้ต, ทีเชิ้ต, สแล็ค, ยีนส์, เบลเซอร์)
|
||||||
|
- "Accessories & Bags" (กระเป๋าถือ, กระเป๋าสตางค์, เข็มขัด, หมวก, เครื่องประดับ)
|
||||||
|
- "Shoes" (ส้นสูง, สนีกเกอร์, โลฟเฟอร์, บูท)
|
||||||
|
- "New Arrivals & Editorial" (คอลเลกชันใหม่ล่าสุด, Lookbook, Sustainability Edit)
|
||||||
|
3. แต่ละ Root Item ต้องมี:
|
||||||
|
- "label": ครบ 8 ภาษา { "th", "en", "ja", "zh", "vi", "ko", "lo", "it" }
|
||||||
|
- "url": ลิงก์หมวด
|
||||||
|
- "rootImageUrl": ภาพ Lookbook หรือ Moodboard สไตล์โมเดิร์น
|
||||||
|
- "featured": ไฮไลต์ New Season Drop
|
||||||
|
- "columns": 3-4 คอลัมน์แบ่งตามประเภทย่อย พร้อม hover imageUrl
|
||||||
|
|
||||||
|
[คำสั่ง]:
|
||||||
|
กรุณาสร้าง Mega Menu สำหรับแบรนด์ Modern Fashion & Lifestyle จำนวน 5 Root Items ครบ 8 ภาษา ส่งเฉพาะ Valid JSON Array เท่านั้น`
|
||||||
|
|
||||||
|
const GENERIC_PROMPT = `คุณคือ Senior E-commerce Architect และ UI/UX Specialist เชี่ยวชาญการออกแบบ Navigation Tree สำหรับระบบ Multi-tenant E-commerce Platform
|
||||||
|
|
||||||
|
[ภารกิจของคุณ]:
|
||||||
|
สร้างโครงสร้างเมนูนำทาง (Main Navigation Mega Menu) สำหรับนำเข้าสู่ระบบ E-commerce ในรูปแบบ **Valid JSON Array**
|
||||||
|
|
||||||
|
[ข้อกำหนด Schema]:
|
||||||
|
- Root Array: [ { label: { th, en, ... }, url, rootImageUrl, featured: { ... }, columns: [ ... ] } ]
|
||||||
|
- Column: { title: { th, en, ... }, icon, links: [ { label: { th, en, ... }, url, imageUrl } ] }
|
||||||
|
- Featured: { enabled: true, sectionTitle, badge, title, subtitle, ctaLabel, imageUrl, url }
|
||||||
|
- Multilingual: รองรับ 8 ภาษา (th, en, ja, zh, vi, ko, lo, it)
|
||||||
|
|
||||||
|
[คำสั่ง]:
|
||||||
|
กรุณาสร้าง Mega Menu ที่สมบูรณ์และสวยงามตาม Schema ด้านบน โดยให้คุณออกแบบหมวดหมู่ที่เหมาะสม สละสลวย และส่งผลลัพธ์เป็น Valid JSON Array เท่านั้น`
|
||||||
|
|
||||||
|
const TABS = [
|
||||||
|
{
|
||||||
|
id: 'overview',
|
||||||
|
label: 'Overview & Schema',
|
||||||
|
icon: BookOpen,
|
||||||
|
badge: 'Universal Structure',
|
||||||
|
theme: 'สถาปัตยกรรม 3 ระดับ + 8 ภาษา',
|
||||||
|
content: GUIDE_OVERVIEW_MARKDOWN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fragrance',
|
||||||
|
label: 'Luxury Fragrance (HomAroma)',
|
||||||
|
icon: Flower2,
|
||||||
|
badge: 'Luxury Wellness',
|
||||||
|
theme: 'เทียนหอม / ก้านไม้หอม / น้ำหอม',
|
||||||
|
content: FRAGRANCE_PROMPT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'beauty',
|
||||||
|
label: 'Cosmetics & Skincare',
|
||||||
|
icon: Sparkle,
|
||||||
|
badge: 'Beauty & Skincare',
|
||||||
|
theme: 'เครื่องสำอาง / สกินแคร์ / คลีนเซอร์',
|
||||||
|
content: BEAUTY_PROMPT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fashion',
|
||||||
|
label: 'Fashion & Lifestyle',
|
||||||
|
icon: Shirt,
|
||||||
|
badge: 'Modern Fashion',
|
||||||
|
theme: 'เสื้อผ้า / กระเป๋า / รองเท้า / แอคเซสเซอรี่',
|
||||||
|
content: FASHION_PROMPT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'generic',
|
||||||
|
label: 'Generic E-commerce',
|
||||||
|
icon: Compass,
|
||||||
|
badge: 'Custom Industry',
|
||||||
|
theme: 'ปรับใช้ได้กับทุกประเภทธุรกิจ',
|
||||||
|
content: GENERIC_PROMPT,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function MenuJsonGuideModal({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
onOpenPasteJson,
|
||||||
|
onOpenImportFile,
|
||||||
|
}: MenuJsonGuideModalProps) {
|
||||||
|
const [activeTab, setActiveTab] = useState('fragrance')
|
||||||
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
|
if (!isOpen) return null
|
||||||
|
|
||||||
|
const currentTabItem = TABS.find((t) => t.id === activeTab) || TABS[0]
|
||||||
|
|
||||||
|
const handleCopy = async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(currentTabItem.content)
|
||||||
|
setCopied(true)
|
||||||
|
setTimeout(() => setCopied(false), 2500)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to copy text:', err)
|
||||||
|
alert('ไม่สามารถ Copy ได้อัตโนมัติ กรุณาเลือกข้อความแล้วกด Ctrl+C')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 bg-night-950/85 backdrop-blur-md flex items-center justify-center p-3 sm:p-5 animate-fade-in">
|
||||||
|
<div className="w-full max-w-5xl bg-night-900 border border-white/10 rounded-3xl shadow-2xl flex flex-col max-h-[92vh] overflow-hidden animate-zoom-in">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="p-5 sm:p-6 border-b border-white/10 bg-white/[0.02] flex items-center justify-between gap-4">
|
||||||
|
<div className="flex items-center gap-3.5">
|
||||||
|
<div className="w-11 h-11 rounded-2xl bg-gradient-to-br from-purple-500/20 via-pink-500/10 to-cyan-500/20 border border-purple-500/30 flex items-center justify-center text-purple-300 shadow-inner">
|
||||||
|
<Sparkles className="w-6 h-6 text-purple-300 animate-pulse" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<h3 className="text-lg sm:text-xl font-bold text-white tracking-wide">
|
||||||
|
AI Navigation JSON Generator Guide
|
||||||
|
</h3>
|
||||||
|
<span className="px-2 py-0.5 rounded-full text-[10px] font-bold bg-purple-500/20 text-purple-300 border border-purple-500/30 uppercase tracking-wider">
|
||||||
|
8-Languages
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-slate-400 mt-0.5">
|
||||||
|
คู่มือและชุด Prompt สำหรับส่งให้ LLM (ChatGPT / Claude / Gemini) เพื่อสร้างข้อมูล JSON เมนู Main Navigation
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
onClick={handleCopy}
|
||||||
|
className={`flex items-center gap-1.5 px-4 py-2 rounded-xl text-xs font-bold transition-all cursor-pointer shadow-lg ${
|
||||||
|
copied
|
||||||
|
? 'bg-emerald-500 text-night-950 shadow-emerald-500/20'
|
||||||
|
: 'bg-gradient-to-r from-purple-500 to-indigo-500 hover:from-purple-400 hover:to-indigo-400 text-white shadow-purple-500/20'
|
||||||
|
}`}
|
||||||
|
title="คัดลอกข้อความในแท็บปัจจุบัน"
|
||||||
|
>
|
||||||
|
{copied ? (
|
||||||
|
<>
|
||||||
|
<Check className="w-4 h-4" />
|
||||||
|
<span>คัดลอกสำเร็จ!</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Copy className="w-4 h-4" />
|
||||||
|
<span>Copy {currentTabItem.id === 'overview' ? 'Guide' : 'Prompt'}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="p-2 rounded-xl text-slate-400 hover:text-white hover:bg-white/10 transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
<X className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tab Navigation */}
|
||||||
|
<div className="px-5 sm:px-6 pt-3 pb-2 border-b border-white/5 bg-night-950/40 flex items-center gap-2 overflow-x-auto scrollbar-hide">
|
||||||
|
{TABS.map((tab) => {
|
||||||
|
const Icon = tab.icon
|
||||||
|
const isActive = activeTab === tab.id
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={tab.id}
|
||||||
|
onClick={() => {
|
||||||
|
setActiveTab(tab.id)
|
||||||
|
setCopied(false)
|
||||||
|
}}
|
||||||
|
className={`flex items-center gap-2 px-3.5 py-2 rounded-xl text-xs font-semibold whitespace-nowrap transition-all cursor-pointer ${
|
||||||
|
isActive
|
||||||
|
? 'bg-white/10 text-white border border-white/15 shadow-md shadow-black/20'
|
||||||
|
: 'text-slate-400 hover:text-slate-200 hover:bg-white/5 border border-transparent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Icon className={`w-3.5 h-3.5 ${isActive ? 'text-cyan-glow' : 'text-slate-500'}`} />
|
||||||
|
<span>{tab.label}</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tab Content Header Info */}
|
||||||
|
<div className="px-5 sm:px-6 py-3 bg-white/[0.015] border-b border-white/5 flex flex-wrap items-center justify-between gap-3 text-xs">
|
||||||
|
<div className="flex items-center gap-2 text-slate-300">
|
||||||
|
<span className="font-semibold text-white">{currentTabItem.badge}:</span>
|
||||||
|
<span className="text-slate-400 truncate max-w-md">{currentTabItem.theme}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{onOpenPasteJson && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
onClose()
|
||||||
|
onOpenPasteJson()
|
||||||
|
}}
|
||||||
|
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-lg bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 text-xs font-bold hover:bg-emerald-500/30 transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
<Code2 className="w-3.5 h-3.5" />
|
||||||
|
<span>วาง JSON ผลลัพธ์ (Paste JSON)</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{onOpenImportFile && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
onClose()
|
||||||
|
onOpenImportFile()
|
||||||
|
}}
|
||||||
|
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-lg bg-white/5 text-slate-300 border border-white/10 text-xs font-bold hover:bg-white/10 transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
<Upload className="w-3.5 h-3.5" />
|
||||||
|
<span>เลือกไฟล์ .json</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<span className="text-[11px] text-slate-500 font-mono ml-2">
|
||||||
|
{currentTabItem.content.length.toLocaleString()} characters
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content Body */}
|
||||||
|
<div className="flex-1 overflow-y-auto p-5 sm:p-6 space-y-4 font-sans text-sm text-slate-200 leading-relaxed">
|
||||||
|
{/* Quick Tip Alert */}
|
||||||
|
{activeTab !== 'overview' ? (
|
||||||
|
<div className="p-3.5 sm:p-4 rounded-2xl bg-gradient-to-r from-purple-500/10 via-indigo-500/5 to-cyan-500/10 border border-purple-500/20 text-xs text-purple-200 flex items-start gap-3 shadow-inner">
|
||||||
|
<div className="p-1 rounded-lg bg-purple-500/20 text-purple-300 shrink-0 mt-0.5">
|
||||||
|
<Sparkles className="w-3.5 h-3.5" />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="font-semibold text-white">
|
||||||
|
คำแนะนำการนำไปใช้งาน:
|
||||||
|
</p>
|
||||||
|
<p className="text-slate-300 text-[11px] leading-relaxed">
|
||||||
|
กดปุ่ม <strong>"Copy Prompt"</strong> ด้านบน แล้วนำไปวางใน <strong>ChatGPT, Claude 3.5 Sonnet หรือ Google Gemini</strong> เมื่อได้ผลลัพธ์เป็น JSON Array กลับมา สามารถกดปุ่ม <strong>"วาง JSON ผลลัพธ์ (Paste JSON)"</strong> เพื่อนำเข้าเมนูได้ทันทีโดยไม่ต้องเซฟเป็นไฟล์
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="p-3.5 sm:p-4 rounded-2xl bg-cyan-glow/5 border border-cyan-glow/20 text-xs text-cyan-200 flex items-start gap-3 shadow-inner">
|
||||||
|
<div className="p-1 rounded-lg bg-cyan-glow/20 text-cyan-glow shrink-0 mt-0.5">
|
||||||
|
<Layers className="w-3.5 h-3.5" />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="font-semibold text-white">
|
||||||
|
สถาปัตยกรรม Mega Menu รองรับครบ 8 ภาษา:
|
||||||
|
</p>
|
||||||
|
<p className="text-slate-300 text-[11px] leading-relaxed">
|
||||||
|
โครงสร้างลำดับชั้น: Root Item (Level 1 พร้อม rootImageUrl + Featured Card) ➔ Columns (Level 2) ➔ Links (Level 3 พร้อม Hover Image)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Text Area / Code Display */}
|
||||||
|
<div className="relative rounded-2xl border border-white/10 bg-night-950 p-4 font-mono text-xs text-slate-300 overflow-x-auto selection:bg-purple-500/30">
|
||||||
|
<pre className="whitespace-pre-wrap leading-relaxed">{currentTabItem.content}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer Actions */}
|
||||||
|
<div className="p-4 sm:p-5 border-t border-white/10 bg-night-950/60 flex flex-wrap items-center justify-between gap-3">
|
||||||
|
<div className="flex items-center gap-2 text-xs text-slate-400">
|
||||||
|
<Globe2 className="w-4 h-4 text-cyan-glow" />
|
||||||
|
<span>Format: Valid JSON Array <code className="text-cyan-glow">[ ... ]</code></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2.5">
|
||||||
|
<button
|
||||||
|
onClick={handleCopy}
|
||||||
|
className={`flex items-center gap-1.5 px-4 py-2 rounded-xl text-xs font-bold transition-all cursor-pointer shadow-md ${
|
||||||
|
copied
|
||||||
|
? 'bg-emerald-500 text-night-950'
|
||||||
|
: 'bg-white/10 hover:bg-white/15 text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{copied ? <Check className="w-4 h-4" /> : <Copy className="w-4 h-4" />}
|
||||||
|
<span>{copied ? 'คัดลอกแล้ว!' : 'Copy Prompt'}</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-4 py-2 rounded-xl text-xs font-semibold text-slate-300 hover:text-white hover:bg-white/5 transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
ปิดหน้าต่าง
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
import { API_BASE, type Menu } from '@homaroma/types'
|
import { API_BASE, type Menu } from '@homaroma/types'
|
||||||
import { Save, Loader2, Plus, Trash2, ChevronUp, ChevronDown, Copy, Check, Download, Upload, Star, Pencil } from 'lucide-react'
|
import { Save, Loader2, Plus, Trash2, ChevronUp, ChevronDown, Copy, Check, Download, Upload, Star, Pencil, Sparkles, Code2, X } from 'lucide-react'
|
||||||
import { ICON_REGISTRY } from '../../utils/iconRegistry'
|
import { ICON_REGISTRY } from '../../utils/iconRegistry'
|
||||||
import FocalPointPicker from '../../components/page-builder/FocalPointPicker'
|
import FocalPointPicker from '../../components/page-builder/FocalPointPicker'
|
||||||
|
import MenuJsonGuideModal from '../../components/MenuJsonGuideModal'
|
||||||
|
|
||||||
const IconSelect = ({ value, onChange }: { value?: string, onChange: (val: string) => void }) => {
|
const IconSelect = ({ value, onChange }: { value?: string, onChange: (val: string) => void }) => {
|
||||||
return (
|
return (
|
||||||
@@ -400,6 +401,10 @@ export default function MenuEditorPage({ menuName, title, description }: MenuEdi
|
|||||||
const [pages, setPages] = useState<any[]>([])
|
const [pages, setPages] = useState<any[]>([])
|
||||||
const [copiedSlug, setCopiedSlug] = useState<string | null>(null)
|
const [copiedSlug, setCopiedSlug] = useState<string | null>(null)
|
||||||
const [selectedFeaturedIndex, setSelectedFeaturedIndex] = useState(0)
|
const [selectedFeaturedIndex, setSelectedFeaturedIndex] = useState(0)
|
||||||
|
const [showLlmModal, setShowLlmModal] = useState(false)
|
||||||
|
const [showPasteModal, setShowPasteModal] = useState(false)
|
||||||
|
const [pastedJsonText, setPastedJsonText] = useState('')
|
||||||
|
const [pasteError, setPasteError] = useState<string | null>(null)
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -524,6 +529,39 @@ export default function MenuEditorPage({ menuName, title, description }: MenuEdi
|
|||||||
reader.readAsText(file)
|
reader.readAsText(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleApplyPastedJson = () => {
|
||||||
|
if (!pastedJsonText.trim()) {
|
||||||
|
setPasteError('กรุณาวางโค้ด JSON ก่อนกดยืนยัน')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(pastedJsonText)
|
||||||
|
let newItems: any[] = []
|
||||||
|
let newPayload: any = []
|
||||||
|
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
newPayload = parsed
|
||||||
|
} else {
|
||||||
|
newItems = Array.isArray(parsed.items) ? parsed.items : []
|
||||||
|
newPayload = Array.isArray(parsed.payload) ? parsed.payload : (parsed.payload || [])
|
||||||
|
}
|
||||||
|
|
||||||
|
setMenus(menus.map(m => m.name === menuName ? { ...m, items: newItems, payload: newPayload } : m))
|
||||||
|
|
||||||
|
if (!menus.find(m => m.name === menuName)) {
|
||||||
|
setMenus([...menus, { id: Date.now(), name: menuName, items: newItems, payload: newPayload }])
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowPasteModal(false)
|
||||||
|
setPastedJsonText('')
|
||||||
|
setPasteError(null)
|
||||||
|
alert('โหลดข้อมูลเมนูจาก JSON สำเร็จ! กรุณากดปุ่ม "Save Menu" ด้านล่างเพื่อบันทึกลงระบบ')
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err)
|
||||||
|
setPasteError(`รูปแบบ JSON ไม่ถูกต้อง: ${err?.message || 'Syntax error'}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleSave = async (e: React.FormEvent) => {
|
const handleSave = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
@@ -568,7 +606,27 @@ export default function MenuEditorPage({ menuName, title, description }: MenuEdi
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end gap-2 border-b border-white/5 pb-4">
|
<div className="flex justify-end gap-2 border-b border-white/5 pb-4">
|
||||||
<div className="flex gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
{menuName === 'main' && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowLlmModal(true)}
|
||||||
|
className="flex items-center gap-1.5 px-3 py-2 bg-gradient-to-r from-purple-500/20 via-pink-500/20 to-indigo-500/20 hover:from-purple-500/30 hover:to-indigo-500/30 text-purple-200 border border-purple-500/30 rounded-lg text-xs font-bold transition-all shadow-sm cursor-pointer"
|
||||||
|
title="ดูคู่มือและ Prompt สำหรับให้ AI / LLM สร้าง JSON เมนู Main Navigation"
|
||||||
|
>
|
||||||
|
<Sparkles className="w-4 h-4 text-pink-400" />
|
||||||
|
<span>LLM Prompt</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPasteModal(true)}
|
||||||
|
className="flex items-center gap-1.5 px-3 py-2 bg-emerald-500/10 hover:bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 rounded-lg text-xs font-bold transition-colors cursor-pointer"
|
||||||
|
title="วางโค้ด JSON จาก LLM เพื่อนำเข้าเมนูทันที"
|
||||||
|
>
|
||||||
|
<Code2 className="w-4 h-4 text-emerald-400" />
|
||||||
|
<span>Paste JSON</span>
|
||||||
|
</button>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
accept=".json"
|
accept=".json"
|
||||||
@@ -579,14 +637,16 @@ export default function MenuEditorPage({ menuName, title, description }: MenuEdi
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
className="flex items-center gap-2 px-3 py-2 bg-white/5 hover:bg-white/10 text-slate-300 rounded-lg text-xs font-bold transition-colors"
|
className="flex items-center gap-2 px-3 py-2 bg-white/5 hover:bg-white/10 text-slate-300 rounded-lg text-xs font-bold transition-colors cursor-pointer"
|
||||||
|
title="เลือกไฟล์ .json จากคอมพิวเตอร์"
|
||||||
>
|
>
|
||||||
<Upload className="w-4 h-4" /> Import
|
<Upload className="w-4 h-4" /> Import
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
className="flex items-center gap-2 px-3 py-2 bg-white/5 hover:bg-white/10 text-slate-300 rounded-lg text-xs font-bold transition-colors"
|
className="flex items-center gap-2 px-3 py-2 bg-white/5 hover:bg-white/10 text-slate-300 rounded-lg text-xs font-bold transition-colors cursor-pointer"
|
||||||
|
title="ดาวน์โหลดโครงสร้างเมนูเป็นไฟล์ .json"
|
||||||
>
|
>
|
||||||
<Download className="w-4 h-4" /> Export
|
<Download className="w-4 h-4" /> Export
|
||||||
</button>
|
</button>
|
||||||
@@ -822,6 +882,66 @@ export default function MenuEditorPage({ menuName, title, description }: MenuEdi
|
|||||||
Copied URL: /{copiedSlug}
|
Copied URL: /{copiedSlug}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Paste JSON Modal */}
|
||||||
|
{showPasteModal && (
|
||||||
|
<div className="fixed inset-0 z-50 bg-night-950/85 backdrop-blur-md flex items-center justify-center p-4 animate-fade-in">
|
||||||
|
<div className="w-full max-w-2xl bg-night-900 border border-white/10 rounded-2xl shadow-2xl p-6 space-y-4 animate-zoom-in">
|
||||||
|
<div className="flex items-center justify-between border-b border-white/10 pb-3">
|
||||||
|
<div className="flex items-center gap-2.5">
|
||||||
|
<Code2 className="w-5 h-5 text-emerald-400" />
|
||||||
|
<h3 className="text-lg font-bold text-white">วางโค้ด JSON นำเข้าเมนู ({menuName})</h3>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => { setShowPasteModal(false); setPasteError(null); }}
|
||||||
|
className="text-slate-400 hover:text-white p-1 rounded-lg hover:bg-white/10 transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
<X className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-slate-400">
|
||||||
|
วางโค้ด JSON Array ที่ได้รับจาก LLM (ChatGPT / Claude / Gemini) ด้านล่าง จากนั้นกด "นำเข้าสู่เมนู"
|
||||||
|
</p>
|
||||||
|
<textarea
|
||||||
|
rows={12}
|
||||||
|
className="admin-input w-full font-mono text-xs p-3 resize-y bg-night-950 border border-white/10 rounded-xl text-slate-200 focus:border-emerald-500/50"
|
||||||
|
placeholder={`[\n {\n "label": { "th": "เครื่องหอมสำหรับบ้าน", "en": "Home Fragrance" },\n "url": "/category/home-fragrance",\n "rootImageUrl": "https://images.unsplash.com/...",\n "columns": [\n {\n "title": { "th": "ก้านไม้หอม", "en": "Reed Diffusers" },\n "links": [\n { "label": { "th": "คลาสสิก", "en": "Classic" }, "url": "/category/diffusers" }\n ]\n }\n ]\n }\n]`}
|
||||||
|
value={pastedJsonText}
|
||||||
|
onChange={(e) => { setPastedJsonText(e.target.value); setPasteError(null); }}
|
||||||
|
/>
|
||||||
|
{pasteError && (
|
||||||
|
<p className="text-xs text-rose-400 bg-rose-500/10 border border-rose-500/20 p-2.5 rounded-lg">
|
||||||
|
{pasteError}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center justify-end gap-2 pt-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => { setShowPasteModal(false); setPasteError(null); }}
|
||||||
|
className="px-4 py-2 rounded-xl text-xs font-semibold text-slate-300 hover:text-white hover:bg-white/5 transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
ยกเลิก
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleApplyPastedJson}
|
||||||
|
className="px-5 py-2 rounded-xl text-xs font-bold bg-emerald-500 hover:bg-emerald-400 text-night-950 transition-colors shadow-lg shadow-emerald-500/20 cursor-pointer"
|
||||||
|
>
|
||||||
|
นำเข้าสู่เมนู (Apply Menu)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* LLM Mega Menu Guide Modal */}
|
||||||
|
<MenuJsonGuideModal
|
||||||
|
isOpen={showLlmModal}
|
||||||
|
onClose={() => setShowLlmModal(false)}
|
||||||
|
onOpenPasteJson={() => setShowPasteModal(true)}
|
||||||
|
onOpenImportFile={() => fileInputRef.current?.click()}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user