diff --git a/data/homaroma_attribute_definitions.json b/data/homaroma_attribute_definitions.json new file mode 100644 index 0000000..7af22a4 --- /dev/null +++ b/data/homaroma_attribute_definitions.json @@ -0,0 +1,324 @@ +[ + { + "code": "scent_family", + "label": "กลุ่มโทนกลิ่น", + "description": "การจัดกลุ่มโทนกลิ่นหลักของเครื่องหอม เพื่อให้ค้นหาตามสไตล์กลิ่นที่ชอบได้ง่าย", + "type": "SELECT", + "unit": null, + "options": [ + { + "value": "floral", + "label": "Floral (ดอกไม้)", + "label_th": "กลิ่นแนวดอกไม้", + "label_en": "Floral" + }, + { + "value": "fresh_herbal", + "label": "Fresh & Herbal (สดชื่น/สมุนไพร)", + "label_th": "กลิ่นสดชื่นและสมุนไพร", + "label_en": "Fresh & Herbal" + }, + { + "value": "fruity", + "label": "Fruity (ผลไม้)", + "label_th": "กลิ่นแนวผลไม้", + "label_en": "Fruity" + }, + { + "value": "spa_relaxing", + "label": "Spa & Relaxing (สปา/ผ่อนคลาย)", + "label_th": "กลิ่นแนวสปาและผ่อนคลาย", + "label_en": "Spa & Relaxing" + }, + { + "value": "sweet_warm", + "label": "Sweet & Warm (หวานละมุน/อบอุ่น)", + "label_th": "กลิ่นแนวหวานละมุนและอบอุ่น", + "label_en": "Sweet & Warm" + }, + { + "value": "aquatic_ozone", + "label": "Aquatic & Ozone (อากาศ/ทะเล)", + "label_th": "กลิ่นแนวปลอดโปร่งและทะเล", + "label_en": "Aquatic & Ozone" + } + ], + "isRequired": false, + "isFilterable": true, + "sortOrder": 1, + "categoryId": null, + "translations": { + "th": { + "label": "กลุ่มโทนกลิ่น", + "description": "การจัดกลุ่มโทนกลิ่นหลักของเครื่องหอม" + }, + "en": { + "label": "Fragrance Family", + "description": "Primary olfactory classification for fragrances" + } + } + }, + { + "code": "mood_benefit", + "label": "อารมณ์และคุณประโยชน์", + "description": "ความรู้สึกหรือบรรยากาศที่ได้รับจากกลิ่นหอมนี้", + "type": "SELECT", + "unit": null, + "options": [ + { + "value": "relax_sleep", + "label": "Relax & Sleep (ผ่อนคลาย หลับสบาย)", + "label_th": "ผ่อนคลาย หลับสบาย", + "label_en": "Relax & Sleep" + }, + { + "value": "refresh_energize", + "label": "Refresh & Energize (สดชื่น เติมพลัง)", + "label_th": "สดชื่น เติมพลัง", + "label_en": "Refresh & Energize" + }, + { + "value": "focus_mind", + "label": "Focus & Mind (สร้างสมาธิ กระตุ้นไอเดีย)", + "label_th": "สร้างสมาธิ กระตุ้นไอเดีย", + "label_en": "Focus & Mind" + }, + { + "value": "odor_eliminator", + "label": "Odor Eliminator (ดับกลิ่นอับ ปรับอากาศ)", + "label_th": "ดับกลิ่นอับ ปรับอากาศ", + "label_en": "Odor Eliminator" + }, + { + "value": "romantic_cozy", + "label": "Romantic & Cozy (อบอุ่น โรแมนติก)", + "label_th": "อบอุ่น โรแมนติก", + "label_en": "Romantic & Cozy" + } + ], + "isRequired": false, + "isFilterable": true, + "sortOrder": 2, + "categoryId": null, + "translations": { + "th": { + "label": "อารมณ์และคุณประโยชน์", + "description": "บรรยากาศหรือความรู้สึกที่ได้จากกลิ่น" + }, + "en": { + "label": "Mood & Benefit", + "description": "Atmosphere and emotional benefits" + } + } + }, + { + "code": "scent_intensity", + "label": "ระดับความเข้มข้นของกลิ่น", + "description": "ระดับการกระจายตัวและความชัดของกลิ่นหอม", + "type": "SELECT", + "unit": null, + "options": [ + { + "value": "light", + "label": "Light / Subtle (หอมละมุน บางเบา)", + "label_th": "หอมละมุน บางเบา", + "label_en": "Light / Subtle" + }, + { + "value": "medium", + "label": "Medium (หอมปานกลาง กำลังพอดี)", + "label_th": "หอมปานกลาง กำลังพอดี", + "label_en": "Medium" + }, + { + "value": "strong", + "label": "Strong / Rich (หอมชัดเจน เข้มข้น)", + "label_th": "หอมชัดเจน เข้มข้น", + "label_en": "Strong / Rich" + } + ], + "isRequired": false, + "isFilterable": true, + "sortOrder": 3, + "categoryId": null, + "translations": { + "th": { + "label": "ระดับความเข้มข้นของกลิ่น", + "description": "ระดับการกระจายตัวและความชัดของกลิ่น" + }, + "en": { + "label": "Scent Intensity", + "description": "Diffusion strength and fragrance throw" + } + } + }, + { + "code": "room_placement", + "label": "พื้นที่ใช้งานที่แนะนำ", + "description": "ห้องหรือบริเวณที่เหมาะสมที่สุดสำหรับการกระจายกลิ่น", + "type": "SELECT", + "unit": null, + "options": [ + { + "value": "bedroom", + "label": "ห้องนอน (Bedroom)", + "label_th": "ห้องนอน", + "label_en": "Bedroom" + }, + { + "value": "living_room", + "label": "ห้องนั่งเล่น / โถงกลาง (Living Room)", + "label_th": "ห้องนั่งเล่น / โถงกลาง", + "label_en": "Living Room" + }, + { + "value": "workspace", + "label": "ห้องทำงาน / โต๊ะทำงาน (Workspace)", + "label_th": "ห้องทำงาน / โต๊ะทำงาน", + "label_en": "Workspace" + }, + { + "value": "bathroom", + "label": "ห้องน้ำ (Bathroom)", + "label_th": "ห้องน้ำ", + "label_en": "Bathroom" + }, + { + "value": "car", + "label": "ห้องโดยสารรถยนต์ (Car Interior)", + "label_th": "ห้องโดยสารรถยนต์", + "label_en": "Car Interior" + }, + { + "value": "commercial_spa", + "label": "ร้านสปา / โรงแรม / คาเฟ่ (Commercial / Spa)", + "label_th": "ร้านสปา / โรงแรม / คาเฟ่", + "label_en": "Commercial / Spa" + } + ], + "isRequired": false, + "isFilterable": true, + "sortOrder": 4, + "categoryId": null, + "translations": { + "th": { + "label": "พื้นที่ใช้งานที่แนะนำ", + "description": "ห้องหรือบริเวณที่เหมาะสมสำหรับการกระจายกลิ่น" + }, + "en": { + "label": "Recommended Space", + "description": "Ideal placement areas for optimal aroma diffusion" + } + } + }, + { + "code": "compatible_device", + "label": "อุปกรณ์ที่รองรับ", + "description": "ประเภทเครื่องพ่นหรืออุปกรณ์กระจายกลิ่นที่สามารถใช้ร่วมกันได้", + "type": "TEXT", + "unit": null, + "options": null, + "isRequired": false, + "isFilterable": false, + "sortOrder": 5, + "categoryId": null, + "translations": { + "th": { + "label": "อุปกรณ์ที่รองรับ", + "description": "อุปกรณ์หรือเครื่องพ่นที่สามารถใช้ร่วมกันได้" + }, + "en": { + "label": "Compatible Devices", + "description": "Supported diffusers and diffusion equipment" + } + } + }, + { + "code": "coverage_area", + "label": "ขนาดพื้นที่ครอบคลุม", + "description": "ขนาดพื้นที่ห้องที่กลิ่นสามารถกระจายตัวได้อย่างมีประสิทธิภาพ", + "type": "TEXT", + "unit": "ตร.ม.", + "options": null, + "isRequired": false, + "isFilterable": false, + "sortOrder": 6, + "categoryId": null, + "translations": { + "th": { + "label": "ขนาดพื้นที่ครอบคลุม", + "description": "ขนาดพื้นที่ห้องที่กลิ่นกระจายตัวได้ดี" + }, + "en": { + "label": "Coverage Area", + "description": "Effective room coverage area in square meters" + } + } + }, + { + "code": "scent_duration", + "label": "ระยะเวลาการใช้งาน", + "description": "อายุการใช้งานหรือระยะเวลาที่กลิ่นหอมกระจายตัวต่อเนื่อง", + "type": "TEXT", + "unit": null, + "options": null, + "isRequired": false, + "isFilterable": false, + "sortOrder": 7, + "categoryId": null, + "translations": { + "th": { + "label": "ระยะเวลาการใช้งาน", + "description": "ระยะเวลาที่กลิ่นกระจายตัวต่อเนื่อง" + }, + "en": { + "label": "Estimated Duration", + "description": "Approximate lifespan and continuous scent duration" + } + } + }, + { + "code": "formula_base", + "label": "ประเภทเบสและส่วนผสม", + "description": "ชนิดของเบสน้ำมันหรือส่วนประกอบในการกระจายกลิ่น", + "type": "TEXT", + "unit": null, + "options": null, + "isRequired": false, + "isFilterable": false, + "sortOrder": 8, + "categoryId": null, + "translations": { + "th": { + "label": "ประเภทเบสและส่วนผสม", + "description": "ชนิดของเบสที่ใช้ในการผสมและกระจายกลิ่น" + }, + "en": { + "label": "Formula Base", + "description": "Base formula and carrier type" + } + } + }, + { + "code": "usage_instructions", + "label": "วิธีใช้งานและคำแนะนำ", + "description": "ขั้นตอนการใช้งานที่ถูกต้องเพื่อประสิทธิภาพสูงสุดในการกระจายกลิ่น", + "type": "TEXT", + "unit": null, + "options": null, + "isRequired": false, + "isFilterable": false, + "sortOrder": 9, + "categoryId": null, + "translations": { + "th": { + "label": "วิธีใช้งานและคำแนะนำ", + "description": "ขั้นตอนการใช้งานเพื่อประสิทธิภาพสูงสุด" + }, + "en": { + "label": "Usage Instructions", + "description": "Recommended steps for best diffusion experience" + } + } + } +] diff --git a/scripts/import_attribute_definitions_via_api.ts b/scripts/import_attribute_definitions_via_api.ts new file mode 100644 index 0000000..8b0be4b --- /dev/null +++ b/scripts/import_attribute_definitions_via_api.ts @@ -0,0 +1,75 @@ +import * as fs from "fs"; +import * as path from "path"; + +const API_BASE = process.env.API_BASE || "https://api.homaroma.shop/api"; +const ADMIN_USER = process.env.ADMIN_USER || "admin"; +const ADMIN_PASS = process.env.ADMIN_PASS || "homaroma123"; + +async function main() { + console.log(`=== Importing Attribute Definitions via API (${API_BASE}) ===`); + + // 1. Read JSON definitions + const jsonPath = path.resolve(__dirname, "../data/homaroma_attribute_definitions.json"); + if (!fs.existsSync(jsonPath)) { + throw new Error(`File not found: ${jsonPath}`); + } + const definitions = JSON.parse(fs.readFileSync(jsonPath, "utf-8")); + console.log(`✓ Loaded ${definitions.length} definitions from data/homaroma_attribute_definitions.json`); + + // 2. Login to get JWT Token + console.log(`2. Logging in as '${ADMIN_USER}' to obtain JWT token...`); + const loginRes = await fetch(`${API_BASE}/auth/login`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ username: ADMIN_USER, password: ADMIN_PASS }), + }); + + if (!loginRes.ok) { + const errorText = await loginRes.text(); + throw new Error(`Login failed (${loginRes.status}): ${errorText}`); + } + + const loginData = (await loginRes.json()) as { success: boolean; token: string }; + if (!loginData.token) { + throw new Error("No token returned from login API"); + } + const token = loginData.token; + console.log("✓ Successfully authenticated. JWT token acquired."); + + // 3. Post bulk-import + console.log("3. Sending bulk-import payload to /product-attributes/definitions/bulk-import..."); + const importRes = await fetch(`${API_BASE}/product-attributes/definitions/bulk-import`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify(definitions), + }); + + if (!importRes.ok) { + const errorText = await importRes.text(); + throw new Error(`Bulk import failed (${importRes.status}): ${errorText}`); + } + + const importResult = await importRes.json(); + console.log("✓ Bulk import response:", JSON.stringify(importResult, null, 2)); + + // 4. Fetch all definitions to verify + console.log("4. Verifying active attribute definitions from API..."); + const listRes = await fetch(`${API_BASE}/product-attributes/definitions?lang=th`); + const listData = await listRes.json(); + console.log(`✓ Total active attribute definitions on API: ${Array.isArray(listData) ? listData.length : 'N/A'}`); + + return importResult; +} + +main() + .then(() => { + console.log("\n✅ Completed successfully."); + process.exit(0); + }) + .catch((err) => { + console.error("\n❌ Error:", err.message || err); + process.exit(1); + });