From f8e11d5568bc4350b3a428876d1c06b4495de60a Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 8 Sep 2026 06:20:52 +0700 Subject: [PATCH] x --- apps/admin/src/locales/en/translation.json | 82 ++- apps/admin/src/locales/ja/translation.json | 82 ++- apps/admin/src/locales/th/translation.json | 82 ++- apps/admin/src/locales/zh/translation.json | 82 ++- apps/admin/src/pages/CmsMockupPage.tsx | 255 ++++--- .../pages/tools/MarketplaceProductsPage.tsx | 631 +++++++++++++++--- 6 files changed, 1024 insertions(+), 190 deletions(-) diff --git a/apps/admin/src/locales/en/translation.json b/apps/admin/src/locales/en/translation.json index 5cbb4e2..54249a9 100644 --- a/apps/admin/src/locales/en/translation.json +++ b/apps/admin/src/locales/en/translation.json @@ -361,7 +361,34 @@ "soldLabel": "Sold:", "revenueLabel": "Revenue:", "avgPriceLabel": "Avg Price:", - "modalSearchPlaceholder": "Type to search master product, SKU code, or variant option...", + "modalSearchPlaceholder": "Search by product name, SKU, barcode, variation, brand...", + "filterBySku": "SKU / Barcode", + "filterBySkuPlaceholder": "Search SKU or barcode...", + "filterByName": "Master Product Name", + "filterByNamePlaceholder": "Search product name...", + "filterByOption": "Variation / Option", + "filterByOptionPlaceholder": "e.g. 50ml, 100ml, scent...", + "filterCategory": "Category", + "allCategories": "All Categories", + "filterBrand": "Brand", + "allBrands": "All Brands", + "bomStatus": "BOM Status", + "allBom": "All BOM Status", + "hasBom": "Has BOM Recipe", + "noBomOnly": "No BOM Recipe", + "clearFilters": "Clear Filters", + "advancedFilters": "Detailed Filters", + "hideAdvancedFilters": "Hide Detailed Filters", + "quickSearchByMpSku": "🔍 Search by MP SKU: {{sku}}", + "quickSearchByMpName": "🔍 Search by MP Title", + "selectedMaster": "Selected Master Variant:", + "noSelection": "No master variant selected", + "resultsCount": "Showing {{count}} of {{total}} items", + "stockLabel": "Stock:", + "sortPriceAsc": "Price: Low -> High", + "sortPriceDesc": "Price: High -> Low", + "sortStock": "Highest Stock", + "sortSku": "SKU Code (A-Z)", "noMasterFound": "No matching master catalog products found", "bomCostLabel": "BOM Cost:", "unlink": "Unlink SKU", @@ -1305,9 +1332,62 @@ "alertUrlCopied": "Image URL copied to clipboard!", "alertUploadFailed": "Upload failed: {{error}}", "alertUploadGeneric": "Failed to upload media file." + }, + "cmsMockup": { + "title": "CMS Blocks Showcase", + "subtitle": "Visual preview and template schema payloads for all available Page Builder blocks.", + "searchPlaceholder": "Search blocks by name, type, or description...", + "showingBlocks": "Showing {{count}} of {{total}} blocks", + "noBlocksFound": "No blocks found matching \"{{search}}\"", + "viewPayload": "View Payload", + "hidePayload": "Hide Payload", + "copyPayload": "Copy Payload", + "copied": "Copied", + "categories": { + "all": "All Blocks", + "hero": "Banner & Hero", + "products": "Products & Shop", + "editorial": "Story & Editorial", + "articles": "Article Elements", + "forms": "Forms & Info" + }, + "blocks": { + "ANNOUNCEMENT_BAR": "Top notice bar with custom styling and scrolling announcements.", + "HERO": "Premium full-screen cover with headline, background image, and CTA.", + "HERO_V2": "Split Layout Hero featuring text on one side and a premium image card on the other.", + "HERO_VIDEO": "Cinema-quality full background video banner with animated overlay typography and dual CTA buttons.", + "USP_BAR": "Unique Selling Proposition Bar highlighting product perks (Icons & Labels).", + "BANNER": "Clickable promotional block with headline overlay, ideal for campaigns.", + "CATEGORIES": "Visual category grid showcase to guide users to specific collections.", + "PRODUCTS": "Showcase grid or carousel for featured products, brand items, or a customized list with Quick Shop option.", + "PRODUCT_CAROUSEL": "Swipeable horizontal card carousel displaying specific product collections.", + "RELATED_PRODUCTS": "Dynamic block recommending items from the same category for the product page layout.", + "EDITORIAL_TILE": "Stunning editorial grid block presenting storytelling text alongside a curated campaign photo.", + "FEATURED_TILES": "Side-by-side campaign grid featuring square or tall images with centered titles and outline CTA buttons (Jo Malone aesthetic).", + "EDITORIAL_CAROUSEL": "Stories and values slider displaying image slideshows side-by-side with localized editorial statements.", + "SCENT_NOTES_BREAKDOWN": "Fragrance profile breakdowns containing Top, Heart/Middle, and Base notes lists.", + "QUOTE_CALLOUT": "Elegant text banner presenting callout quotations, ideal for customer testimonials.", + "TEXT": "Plain text component for headers, centered paragraphs, or brand catchphrases.", + "RICH_TEXT": "Styled markdown block designed for longer stories or HTML embedding.", + "HERO_ARTICLE": "Editorial article header with background blur, date, author name, and title overlays.", + "ARTICLE_HEADER": "Introductory page header for standard text articles.", + "ARTICLE_MEDIA": "Article paragraph paired with a side image (left/right alignment).", + "ARTICLE_GALLERY": "Photo collection grids to show off multiple illustrations or steps.", + "ARTICLE_FACTS": "Quick summaries or key takeaway bullets for editorial pieces.", + "ARTICLE_VIDEO": "Embedded video player wrapper for articles.", + "ARTICLE_REFERENCES": "Citation list or scientific reference cards.", + "ARTICLE_TABLE": "Comparison table displaying technical parameters or specs.", + "ARTICLE_DOWNLOAD": "File attachments download cards (PDF guides, catalogues).", + "ARTICLE_CTA": "Call-to-action blocks prompting next-step user clicks.", + "NEWSLETTER": "Email subscription block to attract newsletter sign-ups.", + "ACCORDION": "Question-answer accordions for FAQs, divided by custom categories.", + "CONTACT_INFO": "Beautiful information box showing address, telephone, and social details.", + "CONTACT_FORM": "General support email inquiry contact form." + } } } + diff --git a/apps/admin/src/locales/ja/translation.json b/apps/admin/src/locales/ja/translation.json index 5a88cfb..94e16b1 100644 --- a/apps/admin/src/locales/ja/translation.json +++ b/apps/admin/src/locales/ja/translation.json @@ -359,7 +359,34 @@ "soldLabel": "販売数:", "revenueLabel": "売上:", "avgPriceLabel": "平均単価:", - "modalSearchPlaceholder": "マスター商品名、SKUコード、容量(例: 50ml)を入力して検索...", + "modalSearchPlaceholder": "一括検索 (商品名、SKU、バーコード、容量、ブランド)...", + "filterBySku": "SKUコード / バーコード", + "filterBySkuPlaceholder": "SKUコードまたはバーコードを入力...", + "filterByName": "マスター商品名", + "filterByNamePlaceholder": "商品名を入力...", + "filterByOption": "規格 / バリエーション", + "filterByOptionPlaceholder": "例: 50ml, 100ml, 香り...", + "filterCategory": "カテゴリ", + "allCategories": "全カテゴリ", + "filterBrand": "ブランド", + "allBrands": "全ブランド", + "bomStatus": "BOMステータス", + "allBom": "すべてのBOM", + "hasBom": "BOM設定済", + "noBomOnly": "BOM未設定", + "clearFilters": "フィルター解除", + "advancedFilters": "詳細フィルター", + "hideAdvancedFilters": "詳細フィルターを閉じる", + "quickSearchByMpSku": "🔍 モールSKUで検索: {{sku}}", + "quickSearchByMpName": "🔍 モール商品名で検索", + "selectedMaster": "選択中のマスター規格:", + "noSelection": "マスター商品が選択されていません", + "resultsCount": "{{count}} / {{total}} 件を表示中", + "stockLabel": "在庫数:", + "sortPriceAsc": "価格が安い順", + "sortPriceDesc": "価格が高い順", + "sortStock": "在庫数が多い順", + "sortSku": "SKUコード順 (A-Z)", "noMasterFound": "該当するマスター商品が見つかりません", "bomCostLabel": "BOM原価:", "unlink": "紐付け解除", @@ -1302,9 +1329,62 @@ "alertUrlCopied": "画像URLをクリップボードにコピーしました!", "alertUploadFailed": "アップロードに失敗しました: {{error}}", "alertUploadGeneric": "メディアファイルのアップロードに失敗しました。" + }, + "cmsMockup": { + "title": "CMSブロック見本 (Showcase)", + "subtitle": "ページビルダーで利用可能なすべてのブロックのプレビューとJSONテンプレートスキーマ", + "searchPlaceholder": "名前、タイプ、説明でブロックを検索...", + "showingBlocks": "{{total}}件中 {{count}}件のブロックを表示", + "noBlocksFound": "「{{search}}」に一致するブロックは見つかりませんでした", + "viewPayload": "Payloadを表示", + "hidePayload": "Payloadを非表示", + "copyPayload": "Payloadをコピー", + "copied": "コピー完了", + "categories": { + "all": "すべてのブロック", + "hero": "バナー & Hero", + "products": "商品 & ショップ", + "editorial": "ストーリー & 記事", + "articles": "記事要素", + "forms": "フォーム & 情報" + }, + "blocks": { + "ANNOUNCEMENT_BAR": "カスタムスタイルとスクロールお知らせ付きのトップ告知バー", + "HERO": "見出し、背景画像、CTAボタンを備えたプレミアムフルスクリーンカバー", + "HERO_V2": "片側にテキスト、もう片側に商品カードを配置したスプリットレイアウトHero", + "HERO_VIDEO": "シネマ品質のフル背景動画バナー(アニメーションタイポグラフィとデュアルCTA付き)", + "USP_BAR": "商品の強みや特典を強調するUSPバー(アイコンとラベル)", + "BANNER": "画像オーバーレイ付きのクリック可能なプロモーションバナー(キャンペーンに最適)", + "CATEGORIES": "特定のコレクションに誘導するビジュアルカテゴリグリッド", + "PRODUCTS": "おすすめ商品や注目アイテムを紹介するグリッド/カルーセル(クイック注文対応)", + "PRODUCT_CAROUSEL": "特定の商品コレクションを横スクロールで表示するスワイプ対応カルーセル", + "RELATED_PRODUCTS": "商品詳細ページ向けに関連商品を自動推薦する動的ブロック", + "EDITORIAL_TILE": "厳選されたキャンペーン写真とともにストーリーを伝えるエディトリアルグリッド", + "FEATURED_TILES": "正方形または縦型画像を並べたキャンペーングリッド(Jo Malone風スタイル)", + "EDITORIAL_CAROUSEL": "ブランドの価値やストーリーを画像スライドショーとともに伝えるカルーセル", + "SCENT_NOTES_BREAKDOWN": "Top、Heart/Middle、Baseノートを視覚的に分解した香りの構成表", + "QUOTE_CALLOUT": "お客様の声や引用文を上品に表示するテキストバナー", + "TEXT": "見出し、中央揃えテキスト、ブランドスローガン向けのプレーンテキストブロック", + "RICH_TEXT": "長文ストーリーやHTML埋め込みに対応した装飾付きリッチテキスト", + "HERO_ARTICLE": "背景ぼかし、日付、著者名、タイトルを表示する記事ヘッダー", + "ARTICLE_HEADER": "標準テキスト記事用の導入ヘッダー", + "ARTICLE_MEDIA": "横並び画像付きの記事パラグラフ(左右配置対応)", + "ARTICLE_GALLERY": "複数のイラストや手順を並べて表示するフォトギャラリーグリッド", + "ARTICLE_FACTS": "記事の重要ポイントや要約を箇条書きでまとめるファクトボックス", + "ARTICLE_VIDEO": "記事埋め込み用の動画プレイヤーラッパー", + "ARTICLE_REFERENCES": "学術引用や参考文献リンクを整理して表示するリスト", + "ARTICLE_TABLE": "仕様や技術パラメーターを分かりやすく比較するデータテーブル", + "ARTICLE_DOWNLOAD": "添付ファイルダウンロードカード(PDFガイド、カタログなど)", + "ARTICLE_CTA": "読者の次のアクションを促すCall to Actionブロック", + "NEWSLETTER": "限定特典や割引を提供するニュースレター購読フォーム", + "ACCORDION": "よくある質問(FAQ)をカテゴリごとに整理できるアコーディオン", + "CONTACT_INFO": "住所、電話番号、SNSなどを美しくまとめた問い合わせ先情報ボックス", + "CONTACT_FORM": "一般的な問い合わせを受け付けるメールフォーム" + } } } + diff --git a/apps/admin/src/locales/th/translation.json b/apps/admin/src/locales/th/translation.json index 15b182a..adc68ba 100644 --- a/apps/admin/src/locales/th/translation.json +++ b/apps/admin/src/locales/th/translation.json @@ -361,7 +361,34 @@ "soldLabel": "ขายแล้ว:", "revenueLabel": "ยอดขาย:", "avgPriceLabel": "ราคาเฉลี่ย:", - "modalSearchPlaceholder": "พิมพ์ค้นหาชื่อสินค้าหลัก, รหัส SKU, หรือขนาด (เช่น 50ml, 120ml)...", + "modalSearchPlaceholder": "ค้นหาทั่วไป (ชื่อสินค้า, SKU, บาร์โค้ด, ขนาด, แบรนด์)...", + "filterBySku": "รหัส SKU / Barcode", + "filterBySkuPlaceholder": "ระบุรหัส SKU หรือ บาร์โค้ด...", + "filterByName": "ชื่อสินค้า Master", + "filterByNamePlaceholder": "ระบุชื่อสินค้า...", + "filterByOption": "ขนาด / ตัวเลือก", + "filterByOptionPlaceholder": "เช่น 50ml, 100ml, กลิ่น...", + "filterCategory": "หมวดหมู่", + "allCategories": "ทุกหมวดหมู่", + "filterBrand": "แบรนด์", + "allBrands": "ทุกแบรนด์", + "bomStatus": "สถานะ BOM", + "allBom": "ทุกสถานะ BOM", + "hasBom": "มีสูตร BOM แล้ว", + "noBomOnly": "ยังไม่มีสูตร BOM", + "clearFilters": "ล้างตัวกรอง", + "advancedFilters": "ตัวกรองละเอียด", + "hideAdvancedFilters": "ซ่อนตัวกรองละเอียด", + "quickSearchByMpSku": "🔍 ค้นหาด้วย SKU Marketplace: {{sku}}", + "quickSearchByMpName": "🔍 ค้นหาด้วยชื่อสินค้านี้", + "selectedMaster": "รายการ Master ที่เลือก:", + "noSelection": "ยังไม่ได้เลือกรายการใด", + "resultsCount": "พบ {{count}} จาก {{total}} รายการ", + "stockLabel": "คงเหลือ:", + "sortPriceAsc": "ราคา: ต่ำ -> สูง", + "sortPriceDesc": "ราคา: สูง -> ต่ำ", + "sortStock": "สต็อกคงเหลือสูงสุด", + "sortSku": "รหัส SKU (A-Z)", "noMasterFound": "ไม่พบรายการสินค้า Master ในระบบที่ตรงกับคำค้นหา", "bomCostLabel": "ต้นทุน BOM:", "unlink": "ยกเลิกการผูก", @@ -1304,9 +1331,62 @@ "alertUrlCopied": "คัดลอก URL รูปภาพลงคลิปบอร์ดแล้ว!", "alertUploadFailed": "อัปโหลดไม่สำเร็จ: {{error}}", "alertUploadGeneric": "ไม่สามารถอัปโหลดไฟล์สื่อได้" + }, + "cmsMockup": { + "title": "คลังตัวอย่าง CMS Blocks (Showcase)", + "subtitle": "ตัวอย่างการแสดงผลจริงและโครงสร้างข้อมูล JSON Template สำหรับบล็อกทั้งหมดในระบบ Page Builder", + "searchPlaceholder": "ค้นหาบล็อกตามชื่อ ประเภท หรือคำอธิบาย...", + "showingBlocks": "แสดง {{count}} จากทั้งหมด {{total}} บล็อก", + "noBlocksFound": "ไม่พบบล็อกที่ตรงกับ \"{{search}}\"", + "viewPayload": "ดู Payload", + "hidePayload": "ซ่อน Payload", + "copyPayload": "คัดลอก Payload", + "copied": "คัดลอกแล้ว", + "categories": { + "all": "ทุกบล็อก", + "hero": "แบนเนอร์ & Hero", + "products": "สินค้า & ร้านค้า", + "editorial": "เรื่องราว & บทความ", + "articles": "องค์ประกอบบทความ", + "forms": "ฟอร์ม & ข้อมูลติดต่อ" + }, + "blocks": { + "ANNOUNCEMENT_BAR": "แถบประกาศด้านบนสุด พร้อมข้อความวิ่งและการจัดสไตล์ที่กำหนดเองได้", + "HERO": "แบนเนอร์เต็มจอระดับพรีเมียม พร้อมหัวข้อ รูปพื้นหลัง และปุ่ม Call-to-Action", + "HERO_V2": "แบนเนอร์ Hero แบบแยกสองฝั่ง จัดวางข้อความเด่นฝั่งหนึ่งและการ์ดภาพสินค้าอีกฝั่ง", + "HERO_VIDEO": "แบนเนอร์วิดีโอพื้นหลังคุณภาพระดับภาพยนตร์ พร้อมตัวอักษรและปุ่ม CTA คู่", + "USP_BAR": "แถบจุดเด่นสินค้า (USP) แสดงไอคอนและข้อความการันตีคุณภาพ", + "BANNER": "แบนเนอร์โปรโมชันแบบคลิกได้ พร้อมข้อความทับบนรูปภาพ เหมาะสำหรับแคมเปญ", + "CATEGORIES": "กริดแสดงหมวดหมู่สินค้าเพื่อนำทางลูกค้าไปยังคอลเลกชันที่ต้องการ", + "PRODUCTS": "บล็อกแสดงสินค้าแบบกริดหรือสไลด์สำหรับสินค้าแนะนำ หรือเลือกสินค้าพร้อมระบบสั่งด่วน", + "PRODUCT_CAROUSEL": "สไลด์การ์ดสินค้าแนวนอน เลื่อนดูสินค้าในคอลเลกชันได้อย่างลื่นไหล", + "RELATED_PRODUCTS": "บล็อกแนะนำสินค้าที่เกี่ยวข้องจากหมวดหมู่เดียวกัน สำหรับหน้ารายละเอียดสินค้า", + "EDITORIAL_TILE": "บล็อกเล่าเรื่องสไตล์ Editorial จัดวางเนื้อหาคู่กับภาพถ่ายแคมเปญ", + "FEATURED_TILES": "กริดแคมเปญภาพคู่แบบจัตุรัสหรือแนวตั้ง พร้อมหัวข้อกึ่งกลางและปุ่มเรียบหรู (Jo Malone Style)", + "EDITORIAL_CAROUSEL": "สไลด์เรื่องราวและคุณค่าของแบรนด์ แสดงภาพคู่กับข้อความบรรยายอย่างลงตัว", + "SCENT_NOTES_BREAKDOWN": "โครงสร้างจำแนกระดับกลิ่นหอม แสดงโน้ต Top, Heart/Middle และ Base", + "QUOTE_CALLOUT": "กล่องโควทข้อความและคำคมระดับพรีเมียม เหมาะสำหรับรีวิวและคำนิยม", + "TEXT": "คอมโพเนนต์ข้อความทั่วไป สำหรับหัวข้อ ย่อหน้า หรือสโลแกนของแบรนด์", + "RICH_TEXT": "บล็อกข้อความจัดรูปแบบ Markdown/HTML สำหรับบทความยาวหรือเนื้อหาเชิงลึก", + "HERO_ARTICLE": "ส่วนหัวบทความสไตล์นิตยสาร พร้อมพื้นหลังเบลอ วันที่ ชื่อผู้เขียน และหัวข้อ", + "ARTICLE_HEADER": "ส่วนหัวบทความมาตรฐานสำหรับบทความทั่วไป", + "ARTICLE_MEDIA": "ย่อหน้าเนื้อหาบทความจับคู่กับรูปภาพประกอบ (จัดชิดซ้ายหรือขวา)", + "ARTICLE_GALLERY": "แกลเลอรีภาพถ่ายสำหรับแสดงภาพขั้นตอนหรือรูปภาพประกอบหลายภาพ", + "ARTICLE_FACTS": "กล่องสรุปข้อเท็จจริง สาระสำคัญ หรือประเด็นสำคัญของบทความ", + "ARTICLE_VIDEO": "ตัวเล่นวิดีโอฝังสำหรับบทความ (รองรับ YouTube/วิดีโอ)", + "ARTICLE_REFERENCES": "รายการเอกสารอ้างอิง แหล่งข้อมูลทางวิชาการ หรือลิงก์ที่เกี่ยวข้อง", + "ARTICLE_TABLE": "ตารางเปรียบเทียบข้อมูลจำเพาะหรือพารามิเตอร์ทางเทคนิค", + "ARTICLE_DOWNLOAD": "การ์ดดาวน์โหลดไฟล์แนบ (คู่มือ PDF แคตตาล็อก ฯลฯ)", + "ARTICLE_CTA": "บล็อกกระตุ้นการตัดสินใจ (Call to Action) สำหรับชวนผู้อ่านคลิกทำรายการต่อ", + "NEWSLETTER": "ฟอร์มรับสมัครจดหมายข่าว เพื่อรับส่วนลดและข่าวสารโปรโมชัน", + "ACCORDION": "แถบพับเปิด-ปิดสำหรับคำถามที่พบบ่อย (FAQs) แยกตามหมวดหมู่", + "CONTACT_INFO": "กล่องข้อมูลติดต่อ แสดงที่อยู่ เบอร์โทร อีเมล และโซเชียลมีเดียอย่างสวยงาม", + "CONTACT_FORM": "ฟอร์มส่งข้อความติดต่อสอบถามทั่วไปทางอีเมล" + } } } + diff --git a/apps/admin/src/locales/zh/translation.json b/apps/admin/src/locales/zh/translation.json index 0a851e2..73c97d5 100644 --- a/apps/admin/src/locales/zh/translation.json +++ b/apps/admin/src/locales/zh/translation.json @@ -359,7 +359,34 @@ "soldLabel": "销量:", "revenueLabel": "销售额:", "avgPriceLabel": "均价:", - "modalSearchPlaceholder": "输入搜索主商品名、SKU 编码或容量规格(如 50ml)...", + "modalSearchPlaceholder": "通用搜索(商品名、SKU、条形码、容量、品牌)...", + "filterBySku": "SKU 编码 / 条形码", + "filterBySkuPlaceholder": "输入 SKU 编码或条码...", + "filterByName": "主商品名称", + "filterByNamePlaceholder": "输入主商品名称...", + "filterByOption": "规格 / 选项", + "filterByOptionPlaceholder": "例如 50ml, 100ml...", + "filterCategory": "分类", + "allCategories": "全部分类", + "filterBrand": "品牌", + "allBrands": "全部品牌", + "bomStatus": "BOM 状态", + "allBom": "全部 BOM 状态", + "hasBom": "已有 BOM 配方", + "noBomOnly": "尚无 BOM 配方", + "clearFilters": "重置筛选", + "advancedFilters": "详细筛选", + "hideAdvancedFilters": "隐藏详细筛选", + "quickSearchByMpSku": "🔍 按平台 SKU 搜索: {{sku}}", + "quickSearchByMpName": "🔍 按平台品名搜索", + "selectedMaster": "当前选中的主商品规格:", + "noSelection": "未选择任何主商品规格", + "resultsCount": "共找到 {{count}} / {{total}} 项", + "stockLabel": "库存:", + "sortPriceAsc": "价格: 低 -> 高", + "sortPriceDesc": "价格: 高 -> 低", + "sortStock": "按库存高到低", + "sortSku": "SKU 编码 (A-Z)", "noMasterFound": "未找到匹配的主商品记录", "bomCostLabel": "BOM 成本:", "unlink": "取消关联", @@ -1303,9 +1330,62 @@ "alertUrlCopied": "图片 URL 已复制到剪贴板!", "alertUploadFailed": "上传失败:{{error}}", "alertUploadGeneric": "上传媒体文件失败。" + }, + "cmsMockup": { + "title": "CMS 组件样例 (Showcase)", + "subtitle": "页面构建器中所有可用区块的实时预览及 JSON 模板 Schema 数据", + "searchPlaceholder": "按名称、类型或描述搜索区块...", + "showingBlocks": "显示 {{total}} 个区块中的 {{count}} 个", + "noBlocksFound": "未找到与“{{search}}”匹配的区块", + "viewPayload": "查看 Payload", + "hidePayload": "隐藏 Payload", + "copyPayload": "复制 Payload", + "copied": "已复制", + "categories": { + "all": "全部区块", + "hero": "横幅 & Hero", + "products": "商品 & 商店", + "editorial": "故事 & 社论", + "articles": "文章元素", + "forms": "表单 & 信息" + }, + "blocks": { + "ANNOUNCEMENT_BAR": "顶部通知栏,支持自定义样式与滚动通知内容", + "HERO": "高端全屏封面,包含大标题、背景图片与 CTA 按钮", + "HERO_V2": "双栏分割式 Hero,一侧为文字排版,另一侧为商品卡片", + "HERO_VIDEO": "电影级全景背景视频横幅,带有动效文字排版与双 CTA 按钮", + "USP_BAR": "独特卖点栏(USP),展示图标与品质承诺标签", + "BANNER": "可点击的促销横幅,带有文字覆盖层,适合各类营销活动", + "CATEGORIES": "视觉化分类网格,引导用户浏览特定产品系列", + "PRODUCTS": "精选商品或定制列表的网格/轮播展示,支持快捷购买", + "PRODUCT_CAROUSEL": "可滑动的横向商品卡片轮播,流畅展示指定系列商品", + "RELATED_PRODUCTS": "商品详情页中的动态相关商品推荐模块", + "EDITORIAL_TILE": "图文社论区块,将品牌故事与精美宣传照片优雅结合", + "FEATURED_TILES": "并排活动网格,方形或长条画幅搭配居中标题与线条按钮(祖·玛珑风格)", + "EDITORIAL_CAROUSEL": "品牌故事与理念轮播,配图展示深度社论内容", + "SCENT_NOTES_BREAKDOWN": "香氛金字塔解析,清晰展示前调、中调与后调成分", + "QUOTE_CALLOUT": "高雅名言引言横幅,非常适合展示品牌箴言或客户好评", + "TEXT": "基础文本组件,用于标题、居中段落或品牌标语", + "RICH_TEXT": "支持 Markdown/HTML 的富文本排版区块,适合长篇报道", + "HERO_ARTICLE": "杂志风格文章封面,具备背景虚化、发布日期、作者与标题排版", + "ARTICLE_HEADER": "标准文本文章的导读标题区块", + "ARTICLE_MEDIA": "图文配对文章段落(支持左图或右图对齐)", + "ARTICLE_GALLERY": "多图相册网格,用于呈现步骤解说或多角度插图", + "ARTICLE_FACTS": "重点速览与核心要点摘要,适合知识性科普内容", + "ARTICLE_VIDEO": "文章内嵌视频播放包装组件", + "ARTICLE_REFERENCES": "学术文献引用列表与参考来源卡片", + "ARTICLE_TABLE": "对比数据表格,清晰陈列规格参数或技术指标", + "ARTICLE_DOWNLOAD": "附件下载卡片(PDF 指南、产品手册等)", + "ARTICLE_CTA": "行动号召(Call to Action)模块,引导用户点击进入下一步", + "NEWSLETTER": "邮件订阅模块,用于吸引用户订阅并获取首单优惠", + "ACCORDION": "常见问题(FAQ)折叠面板,支持按类别分组", + "CONTACT_INFO": "精美联系方式展示框,汇集地址、电话、邮箱及社交媒体", + "CONTACT_FORM": "通用客户咨询与支持邮件表单" + } } } + diff --git a/apps/admin/src/pages/CmsMockupPage.tsx b/apps/admin/src/pages/CmsMockupPage.tsx index d89fb49..3e17cc6 100644 --- a/apps/admin/src/pages/CmsMockupPage.tsx +++ b/apps/admin/src/pages/CmsMockupPage.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { Layers, Copy, @@ -13,17 +14,19 @@ import { Play, Download, Info, - ChevronDown + ChevronDown, + Search, + X } from 'lucide-react'; // Define categories for easy filtering -const CATEGORIES = [ - { id: 'all', name: 'All Blocks' }, - { id: 'hero', name: 'Banner & Hero' }, - { id: 'products', name: 'Products & Shop' }, - { id: 'editorial', name: 'Story & Editorial' }, - { id: 'articles', name: 'Article Elements' }, - { id: 'forms', name: 'Forms & Info' } +const CATEGORY_KEYS = [ + { id: 'all', key: 'cmsMockup.categories.all', fallback: 'All Blocks' }, + { id: 'hero', key: 'cmsMockup.categories.hero', fallback: 'Banner & Hero' }, + { id: 'products', key: 'cmsMockup.categories.products', fallback: 'Products & Shop' }, + { id: 'editorial', key: 'cmsMockup.categories.editorial', fallback: 'Story & Editorial' }, + { id: 'articles', key: 'cmsMockup.categories.articles', fallback: 'Article Elements' }, + { id: 'forms', key: 'cmsMockup.categories.forms', fallback: 'Forms & Info' } ]; interface MockBlock { @@ -35,7 +38,9 @@ interface MockBlock { } export default function CmsMockupPage() { + const { t } = useTranslation(); const [activeCategory, setActiveCategory] = useState('all'); + const [searchQuery, setSearchQuery] = useState(''); const [copiedType, setCopiedType] = useState(null); const [showJsonFor, setShowJsonFor] = useState(null); @@ -996,105 +1001,183 @@ export default function CmsMockupPage() { } ]; - const filteredBlocks = activeCategory === 'all' - ? mockBlocks - : mockBlocks.filter(b => b.category === activeCategory); + const filteredBlocks = mockBlocks.filter((block) => { + const matchesCategory = activeCategory === 'all' || block.category === activeCategory; + if (!matchesCategory) return false; + + if (!searchQuery.trim()) return true; + + const q = searchQuery.toLowerCase().trim(); + const localizedDesc = t(`cmsMockup.blocks.${block.type}`, block.description).toLowerCase(); + const originalDesc = block.description.toLowerCase(); + const blockType = block.type.toLowerCase(); + const categoryName = t(`cmsMockup.categories.${block.category}`, block.category).toLowerCase(); + + return ( + blockType.includes(q) || + localizedDesc.includes(q) || + originalDesc.includes(q) || + categoryName.includes(q) + ); + }); return (
{/* Page Header */} -
+

- CMS Blocks Showcase + {t('cmsMockup.title', 'CMS Blocks Showcase')}

- Visual preview and template schema payloads for all available Page Builder blocks. + {t('cmsMockup.subtitle', 'Visual preview and template schema payloads for all available Page Builder blocks.')}

+ + {/* Search Input Bar */} +
+ + setSearchQuery(e.target.value)} + className="w-full bg-night-900/80 border border-white/10 text-white rounded-2xl pl-10 pr-9 py-2.5 text-xs focus:outline-none focus:border-cyan-glow/50 focus:ring-1 focus:ring-cyan-glow/50 transition-all placeholder:text-slate-500" + /> + {searchQuery && ( + + )} +
- {/* Categories Filter Tabs */} -
- {CATEGORIES.map((cat) => ( - - ))} + {/* Categories Filter Tabs & Stats */} +
+
+ {CATEGORY_KEYS.map((cat) => ( + + ))} +
+ +
+ {t('cmsMockup.showingBlocks', { + count: filteredBlocks.length, + total: mockBlocks.length, + defaultValue: `Showing ${filteredBlocks.length} of ${mockBlocks.length} blocks` + })} +
{/* Blocks Listing Grid */} -
- {filteredBlocks.map((block) => ( -
+ +

+ {t('cmsMockup.noBlocksFound', { + search: searchQuery, + defaultValue: `No blocks found matching "${searchQuery}"` + })} +

+ +
+ ) : ( +
+ {filteredBlocks.map((block) => { + const localizedDesc = t(`cmsMockup.blocks.${block.type}`, block.description); + const localizedCategory = t(`cmsMockup.categories.${block.category}`, block.category); + + return ( +
+ {/* Block Header Info */} +
+
+
+ + {block.type} + + + {localizedCategory} + +
+

{localizedDesc}

+
+ + {/* Action buttons */} +
+ + +
-

{block.description}

-
- {/* Action buttons */} -
- - -
-
+ {/* JSON Code view */} + {showJsonFor === block.type && ( +
+
{JSON.stringify(block.payload, null, 2)}
+
+ )} - {/* JSON Code view */} - {showJsonFor === block.type && ( -
-
{JSON.stringify(block.payload, null, 2)}
+ {/* Live Visual Preview Frame */} +
+
+ {block.renderPreview()} +
+
- )} - - {/* Live Visual Preview Frame */} -
-
- {block.renderPreview()} -
-
-
- ))} -
+ ); + })} +
+ )}
); } diff --git a/apps/admin/src/pages/tools/MarketplaceProductsPage.tsx b/apps/admin/src/pages/tools/MarketplaceProductsPage.tsx index e566837..da12336 100644 --- a/apps/admin/src/pages/tools/MarketplaceProductsPage.tsx +++ b/apps/admin/src/pages/tools/MarketplaceProductsPage.tsx @@ -16,6 +16,14 @@ import { ChevronLeft, ChevronRight, Loader2, + SlidersHorizontal, + RotateCcw, + Tag, + Barcode, + Check, + Building2, + Layers, + Sparkle, } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { API_BASE } from '@homaroma/types'; @@ -57,10 +65,20 @@ export default function MarketplaceProductsPage() { // Master variants for mapping picker const [masterVariants, setMasterVariants] = useState([]); const [mappingModalItem, setMappingModalItem] = useState(null); - const [variantSearch, setVariantSearch] = useState(''); const [selectedVariantId, setSelectedVariantId] = useState(null); const [savingMapping, setSavingMapping] = useState(false); + // Modal Advanced Filter States + const [modalSearch, setModalSearch] = useState(''); + const [modalSkuSearch, setModalSkuSearch] = useState(''); + const [modalNameSearch, setModalNameSearch] = useState(''); + const [modalOptionSearch, setModalOptionSearch] = useState(''); + const [modalCategory, setModalCategory] = useState('all'); + const [modalBrand, setModalBrand] = useState('all'); + const [modalBomFilter, setModalBomFilter] = useState<'all' | 'has_bom' | 'no_bom'>('all'); + const [modalSortBy, setModalSortBy] = useState<'name' | 'sku' | 'price_asc' | 'price_desc' | 'stock'>('name'); + const [showAdvancedFilters, setShowAdvancedFilters] = useState(true); + // Toast / notification message const [toast, setToast] = useState<{ type: 'success' | 'info' | 'error'; message: string } | null>(null); @@ -174,6 +192,8 @@ export default function MarketplaceProductsPage() { 'success' ); fetchData(); + } else { + showToast(t('marketplaceProducts.toastAutoMatchError', 'เกิดข้อผิดพลาดในการ Auto-Match'), 'error'); } } catch (err) { console.error(err); @@ -214,19 +234,136 @@ export default function MarketplaceProductsPage() { } }; + // Categories and brands extracted dynamically from Master Variants + const masterCategories = useMemo(() => { + const set = new Set(); + masterVariants.forEach((v) => { + if (v.categoryName?.trim()) set.add(v.categoryName.trim()); + }); + return Array.from(set).sort(); + }, [masterVariants]); + + const masterBrands = useMemo(() => { + const set = new Set(); + masterVariants.forEach((v) => { + if (v.brandName?.trim()) set.add(v.brandName.trim()); + }); + return Array.from(set).sort(); + }, [masterVariants]); + + const hasActiveModalFilters = Boolean( + modalSearch.trim() || + modalSkuSearch.trim() || + modalNameSearch.trim() || + modalOptionSearch.trim() || + modalCategory !== 'all' || + modalBrand !== 'all' || + modalBomFilter !== 'all' + ); + + const resetModalFilters = () => { + setModalSearch(''); + setModalSkuSearch(''); + setModalNameSearch(''); + setModalOptionSearch(''); + setModalCategory('all'); + setModalBrand('all'); + setModalBomFilter('all'); + setModalSortBy('name'); + }; + + const openMappingModal = (item: any) => { + setMappingModalItem(item); + setSelectedVariantId(item.masterVariantId || null); + resetModalFilters(); + setShowAdvancedFilters(true); + }; + // Filter master variants inside modal const filteredMasterVariants = useMemo(() => { - if (!variantSearch.trim()) return masterVariants; - const q = variantSearch.toLowerCase(); - return masterVariants.filter( - (v) => - v.productName.toLowerCase().includes(q) || - v.sku.toLowerCase().includes(q) || - v.barcode.toLowerCase().includes(q) || - v.optionsStr.toLowerCase().includes(q) || - v.categoryName.toLowerCase().includes(q) - ); - }, [masterVariants, variantSearch]); + const qGlobal = modalSearch.toLowerCase().trim(); + const qSku = modalSkuSearch.toLowerCase().trim(); + const qName = modalNameSearch.toLowerCase().trim(); + const qOption = modalOptionSearch.toLowerCase().trim(); + + return masterVariants + .filter((v) => { + // 1. Global search + if (qGlobal) { + const match = + v.productName.toLowerCase().includes(qGlobal) || + v.sku.toLowerCase().includes(qGlobal) || + v.barcode.toLowerCase().includes(qGlobal) || + v.optionsStr.toLowerCase().includes(qGlobal) || + v.categoryName.toLowerCase().includes(qGlobal) || + v.brandName.toLowerCase().includes(qGlobal); + if (!match) return false; + } + + // 2. Specific SKU / Barcode search + if (qSku) { + const match = v.sku.toLowerCase().includes(qSku) || v.barcode.toLowerCase().includes(qSku); + if (!match) return false; + } + + // 3. Specific Product Name search + if (qName) { + if (!v.productName.toLowerCase().includes(qName)) return false; + } + + // 4. Specific Option / Variation search + if (qOption) { + if (!v.optionsStr.toLowerCase().includes(qOption)) return false; + } + + // 5. Category filter + if (modalCategory !== 'all' && v.categoryName !== modalCategory) { + return false; + } + + // 6. Brand filter + if (modalBrand !== 'all' && v.brandName !== modalBrand) { + return false; + } + + // 7. BOM filter + if (modalBomFilter === 'has_bom' && v.bomCost <= 0) return false; + if (modalBomFilter === 'no_bom' && v.bomCost > 0) return false; + + return true; + }) + .sort((a, b) => { + if (modalSortBy === 'sku') return a.sku.localeCompare(b.sku); + if (modalSortBy === 'price_asc') return a.price - b.price; + if (modalSortBy === 'price_desc') return b.price - a.price; + if (modalSortBy === 'stock') return b.stock - a.stock; + return a.productName.localeCompare(b.productName); + }); + }, [ + masterVariants, + modalSearch, + modalSkuSearch, + modalNameSearch, + modalOptionSearch, + modalCategory, + modalBrand, + modalBomFilter, + modalSortBy, + ]); + + const selectedVariant = useMemo(() => { + if (!selectedVariantId) return null; + return masterVariants.find((v) => v.id === selectedVariantId) || null; + }, [masterVariants, selectedVariantId]); + + const activeAdvancedFilterCount = [ + Boolean(modalSkuSearch.trim()), + Boolean(modalNameSearch.trim()), + Boolean(modalOptionSearch.trim()), + modalCategory !== 'all', + modalBrand !== 'all', + modalBomFilter !== 'all', + ].filter(Boolean).length; const kpi = data?.kpi; const items = data?.items || []; @@ -790,11 +927,7 @@ export default function MarketplaceProductsPage() { {/* Actions */} - {/* Target Marketplace Item Summary */} -
-
- - {t('marketplaceProducts.productOn', 'สินค้าบน')} {mappingModalItem.channel.toUpperCase()} ({mappingModalItem.shop}): - - {mappingModalItem.marketplaceSku && ( - - SKU: {mappingModalItem.marketplaceSku} + {/* Target Marketplace Item Banner */} +
+
+
+ + {mappingModalItem.channel} + ร้าน: {mappingModalItem.shop} + {mappingModalItem.marketplaceSku && ( + + SKU: {mappingModalItem.marketplaceSku} + + )} +
+ +
+ + {t('marketplaceProducts.soldLabel', 'ขายแล้ว:')}{' '} + {fmtNum(mappingModalItem.totalSoldQty)}{' '} + {t('marketplaceProducts.unitsSold', 'ชิ้น')} + + + {t('marketplaceProducts.revenueLabel', 'ยอดขาย:')}{' '} + {fmtBaht(mappingModalItem.totalRevenue)} + + + {t('marketplaceProducts.avgPriceLabel', 'ราคาเฉลี่ย:')}{' '} + {fmtBaht(mappingModalItem.avgPrice)} + +
+
+ +
+

{mappingModalItem.productName}

+ {mappingModalItem.variation && ( +

+ {t('marketplaceProducts.variationPrefix', 'ตัวเลือก:')} {mappingModalItem.variation} +

)}
-

{mappingModalItem.productName}

- {mappingModalItem.variation && ( -

- {t('marketplaceProducts.variationPrefix', 'ตัวเลือก:')} {mappingModalItem.variation} -

- )} -
- - {t('marketplaceProducts.soldLabel', 'ขายแล้ว:')}{' '} - {fmtNum(mappingModalItem.totalSoldQty)}{' '} - {t('marketplaceProducts.unitsSold', 'ชิ้น')} - - - {t('marketplaceProducts.revenueLabel', 'ยอดขาย:')}{' '} - {fmtBaht(mappingModalItem.totalRevenue)} - - - {t('marketplaceProducts.avgPriceLabel', 'ราคาเฉลี่ย:')}{' '} - {fmtBaht(mappingModalItem.avgPrice)} + + {/* Quick Search Suggestions */} +
+ + + ตัวช่วยค้นหาเร็ว: + {mappingModalItem.marketplaceSku && ( + + )} +
- {/* Search & Variants List */} -
-
- - + {/* Primary Search Bar */} +
+
+ + setModalSearch(e.target.value)} + className="w-full pl-9 pr-8 py-2 rounded-xl bg-black/50 border border-white/10 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-amber-500/50 transition-colors" + autoFocus + /> + {modalSearch && ( + )} - value={variantSearch} - onChange={(e) => setVariantSearch(e.target.value)} - className="w-full pl-9 pr-4 py-2 rounded-xl bg-black/40 border border-white/10 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-amber-500/40" - autoFocus - /> +
+ + {/* Toggle Detailed Filters */} + + + {/* Sort selector */} +
+ +
+ + {/* Clear all filters */} + {hasActiveModalFilters && ( + + )}
-
+ {/* Advanced Filters Expandable Grid */} + {showAdvancedFilters && ( +
+ {/* 1. SKU Filter */} +
+ + setModalSkuSearch(e.target.value)} + className="w-full px-2.5 py-1.5 rounded-lg bg-black/40 border border-white/10 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-cyan-500/40" + /> +
+ + {/* 2. Product Name Filter */} +
+ + setModalNameSearch(e.target.value)} + className="w-full px-2.5 py-1.5 rounded-lg bg-black/40 border border-white/10 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-amber-500/40" + /> +
+ + {/* 3. Option / Size Filter */} +
+ + setModalOptionSearch(e.target.value)} + className="w-full px-2.5 py-1.5 rounded-lg bg-black/40 border border-white/10 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-rose-500/40" + /> +
+ + {/* 4. Category Dropdown */} +
+ + +
+ + {/* 5. Brand Dropdown */} +
+ + +
+ + {/* 6. BOM Status Filter */} +
+ + +
+
+ )} +
+ + {/* Results Count & Variants Grid */} +
+
+

+ {t('marketplaceProducts.resultsCount', { + count: fmtNum(filteredMasterVariants.length), + total: fmtNum(masterVariants.length), + defaultValue: `พบ ${fmtNum(filteredMasterVariants.length)} จากทั้งหมด ${fmtNum(masterVariants.length)} รายการ`, + })} +

+ {selectedVariant && ( +

+ {t('marketplaceProducts.selectedMaster', 'รายการ Master ที่เลือก:')}{' '} + {selectedVariant.productName} + {selectedVariant.optionsStr && ({selectedVariant.optionsStr})} +

+ )} +
+ +
{filteredMasterVariants.length === 0 ? ( -
- {t('marketplaceProducts.noMasterFound', 'ไม่พบรายการสินค้า Master ในระบบที่ตรงกับคำค้นหา')} +
+

{t('marketplaceProducts.noMasterFound', 'ไม่พบรายการสินค้า Master ในระบบที่ตรงกับคำค้นหา')}

+ {hasActiveModalFilters && ( + + )}
) : ( - filteredMasterVariants.map((variant) => { - const isSelected = selectedVariantId === variant.id; - return ( -
setSelectedVariantId(variant.id)} - className={`p-3 rounded-xl border transition-all cursor-pointer flex items-center justify-between gap-3 ${ - isSelected - ? 'bg-amber-500/20 border-amber-500/40 shadow-sm' - : 'bg-white/[0.02] border-white/5 hover:bg-white/5 hover:border-white/10' - }`} - > -
-

{variant.productName}

-
- SKU: {variant.sku || '-'} - {variant.optionsStr && • {variant.optionsStr}} - {variant.categoryName && • {variant.categoryName}} +
+ {filteredMasterVariants.map((variant) => { + const isSelected = selectedVariantId === variant.id; + const marginPercent = + variant.price > 0 && variant.bomCost > 0 + ? ((variant.price - variant.bomCost) / variant.price) * 100 + : 0; + + return ( +
setSelectedVariantId(variant.id)} + className={`p-3.5 rounded-2xl border transition-all cursor-pointer flex flex-col justify-between gap-3 ${ + isSelected + ? 'bg-amber-500/15 border-amber-500/60 ring-1 ring-amber-500/40 shadow-lg shadow-amber-500/10' + : 'bg-white/[0.02] border-white/5 hover:bg-white/5 hover:border-white/15' + }`} + > +
+
+
+ +
+
+

+ {variant.productName} +

+
+ + SKU: {variant.sku || '-'} + + {variant.optionsStr && ( + + {variant.optionsStr} + + )} + {variant.categoryName && ( + + • {variant.categoryName} + + )} +
+
+
+ +
+

{fmtBaht(variant.price)}

+ + {t('marketplaceProducts.stockLabel', 'คงเหลือ:')}{' '} + 0 ? 'text-white' : 'text-rose-400'}> + {fmtNum(variant.stock)} + + +
+
+ +
+
+ {t('marketplaceProducts.bomCostLabel', 'ต้นทุน BOM:')} + 0 ? 'text-amber-300 font-bold' : 'text-slate-500'}> + {variant.bomCost > 0 ? fmtBaht(variant.bomCost) : t('marketplaceProducts.noBom', 'ยังไม่ตั้ง BOM')} + +
+ {variant.bomCost > 0 && ( +
+ Margin ~{marginPercent.toFixed(1)}% +
+ )}
- -
-

{fmtBaht(variant.price)}

-

- {t('marketplaceProducts.bomCostLabel', 'ต้นทุน BOM:')}{' '} - {fmtBaht(variant.bomCost)} -

-
-
- ); - }) + ); + })} +
)}
{/* Modal Footer */} -
+
{mappingModalItem.masterVariantId ? ( - ) :
} + ) : ( +
+ )}