Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99907308d3 | ||
|
|
296adb849c | ||
|
|
5686b3ea9c | ||
|
|
e7fc0e2848 | ||
|
|
1f7bc0e01c | ||
|
|
44d9f1f198 | ||
|
|
9107a64637 |
No files matched your search
@@ -44,7 +44,7 @@ export function resolveDefinitionLocalizedField<T extends { langCode: string; la
|
|||||||
export function getPrimaryOptionValue(value: any): string {
|
export function getPrimaryOptionValue(value: any): string {
|
||||||
if (typeof value === 'string') return value;
|
if (typeof value === 'string') return value;
|
||||||
if (typeof value === 'object' && value !== null) {
|
if (typeof value === 'object' && value !== null) {
|
||||||
return value.th || value.en || Object.values(value)[0] || '';
|
return value.value || value.key || value.id || value.th || value.en || Object.values(value)[0] || '';
|
||||||
}
|
}
|
||||||
return String(value ?? '');
|
return String(value ?? '');
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,17 @@ export function getPrimaryOptionValue(value: any): string {
|
|||||||
export function getLocalizedOptionLabel(value: any, lang: string): string {
|
export function getLocalizedOptionLabel(value: any, lang: string): string {
|
||||||
if (typeof value === 'string') return value;
|
if (typeof value === 'string') return value;
|
||||||
if (typeof value === 'object' && value !== null) {
|
if (typeof value === 'object' && value !== null) {
|
||||||
return value[lang] || value[lang.slice(0, 2)] || value.th || value.en || Object.values(value)[0] || '';
|
return (
|
||||||
|
value[`label_${lang}`] ||
|
||||||
|
value[`label_${lang.slice(0, 2)}`] ||
|
||||||
|
value[lang] ||
|
||||||
|
value[lang.slice(0, 2)] ||
|
||||||
|
value.label ||
|
||||||
|
value.th ||
|
||||||
|
value.en ||
|
||||||
|
Object.values(value)[0] ||
|
||||||
|
''
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return String(value ?? '');
|
return String(value ?? '');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,6 +248,9 @@ export const categoriesRoute = new Elysia({ prefix: "/categories" })
|
|||||||
descObj[t.langCode] = t.description;
|
descObj[t.langCode] = t.description;
|
||||||
detailsObj[t.langCode] = t.details;
|
detailsObj[t.langCode] = t.details;
|
||||||
});
|
});
|
||||||
|
if (!nameObj.th && p.name) nameObj.th = p.name;
|
||||||
|
if (!descObj.th && p.description) descObj.th = p.description;
|
||||||
|
if (!detailsObj.th && p.details) detailsObj.th = p.details;
|
||||||
return {
|
return {
|
||||||
...p,
|
...p,
|
||||||
name: nameObj,
|
name: nameObj,
|
||||||
@@ -259,9 +262,9 @@ export const categoriesRoute = new Elysia({ prefix: "/categories" })
|
|||||||
const trans = p.translations?.find((t: any) => t.langCode === lang) || p.translations?.find((t: any) => t.langCode === 'th') || p.translations?.[0];
|
const trans = p.translations?.find((t: any) => t.langCode === lang) || p.translations?.find((t: any) => t.langCode === 'th') || p.translations?.[0];
|
||||||
return {
|
return {
|
||||||
...p,
|
...p,
|
||||||
name: trans?.name || p.brand?.name || '',
|
name: trans?.name || p.name || p.brand?.name || '',
|
||||||
description: trans?.description,
|
description: trans?.description || p.description,
|
||||||
details: trans?.details,
|
details: trans?.details || p.details,
|
||||||
category: { ...p.category, name: resolveCatName(p.category, lang) },
|
category: { ...p.category, name: resolveCatName(p.category, lang) },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,10 @@ export const productAttributesRoute = new Elysia()
|
|||||||
|
|
||||||
if (!definition) return status(404, { message: "Attribute definition not found" });
|
if (!definition) return status(404, { message: "Attribute definition not found" });
|
||||||
return localizeAttributeDefinition(definition, lang);
|
return localizeAttributeDefinition(definition, lang);
|
||||||
|
} catch (err: any) {
|
||||||
|
return status(400, { message: err.message });
|
||||||
|
}
|
||||||
|
})
|
||||||
.get("/products/:id/attributes", async ({ params, query, status }) => {
|
.get("/products/:id/attributes", async ({ params, query, status }) => {
|
||||||
try {
|
try {
|
||||||
const productId = parseId(params.id, "product ID");
|
const productId = parseId(params.id, "product ID");
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,677 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"productId": 3,
|
||||||
|
"scentName": "ตะไคร้",
|
||||||
|
"scentFamily": "spa_relaxing",
|
||||||
|
"moodBenefit": "odor_eliminator",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "bathroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 63,
|
||||||
|
"scentName": "ลีลาวดี",
|
||||||
|
"scentFamily": "spa_relaxing",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "commercial_spa",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 138,
|
||||||
|
"scentName": "ดอกบัว",
|
||||||
|
"scentFamily": "spa_relaxing",
|
||||||
|
"moodBenefit": "focus_mind",
|
||||||
|
"scentIntensity": "light",
|
||||||
|
"roomPlacement": "commercial_spa",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 148,
|
||||||
|
"scentName": "ไทยสปา",
|
||||||
|
"scentFamily": "spa_relaxing",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "commercial_spa",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 8,
|
||||||
|
"scentName": "ลาเวนเดอร์",
|
||||||
|
"scentFamily": "fresh_herbal",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 28,
|
||||||
|
"scentName": "ยูคาลิปตัส",
|
||||||
|
"scentFamily": "fresh_herbal",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "workspace",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 33,
|
||||||
|
"scentName": "เฟรชลาเวนเดอร์",
|
||||||
|
"scentFamily": "fresh_herbal",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 38,
|
||||||
|
"scentName": "เปเปอร์มิ้น",
|
||||||
|
"scentFamily": "fresh_herbal",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "workspace",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 73,
|
||||||
|
"scentName": "มะนาว",
|
||||||
|
"scentFamily": "fresh_herbal",
|
||||||
|
"moodBenefit": "odor_eliminator",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "bathroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 98,
|
||||||
|
"scentName": "โรสเมรี่",
|
||||||
|
"scentFamily": "fresh_herbal",
|
||||||
|
"moodBenefit": "focus_mind",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "workspace",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 123,
|
||||||
|
"scentName": "มะกรูด",
|
||||||
|
"scentFamily": "fresh_herbal",
|
||||||
|
"moodBenefit": "odor_eliminator",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "bathroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 18,
|
||||||
|
"scentName": "ซากุระ",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "romantic_cozy",
|
||||||
|
"scentIntensity": "light",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 23,
|
||||||
|
"scentName": "มะลิ",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 53,
|
||||||
|
"scentName": "ดอกแก้ว",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 58,
|
||||||
|
"scentName": "ดอกโมก",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 93,
|
||||||
|
"scentName": "ดอกลิลลี่",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "romantic_cozy",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 103,
|
||||||
|
"scentName": "อัญชัน",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "light",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 108,
|
||||||
|
"scentName": "กุหลาบแดง",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "romantic_cozy",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 133,
|
||||||
|
"scentName": "ดอกปีบ",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 163,
|
||||||
|
"scentName": "ดอกชมนาด",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "romantic_cozy",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 173,
|
||||||
|
"scentName": "ดอกการะเวก",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 178,
|
||||||
|
"scentName": "ดอกจำปา",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "romantic_cozy",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 183,
|
||||||
|
"scentName": "ดอกพิกุล",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 188,
|
||||||
|
"scentName": "ดอกพุด",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 193,
|
||||||
|
"scentName": "ดอกราตรี",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "romantic_cozy",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 198,
|
||||||
|
"scentName": "ดอกลำดวน",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 213,
|
||||||
|
"scentName": "ดอกราชาวดี",
|
||||||
|
"scentFamily": "floral",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 68,
|
||||||
|
"scentName": "ส้ม",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 78,
|
||||||
|
"scentName": "แคนตาลูป",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 83,
|
||||||
|
"scentName": "แอปเปิ้ลแดง",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 88,
|
||||||
|
"scentName": "แอปเปิ้ลเขียว",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "workspace",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 118,
|
||||||
|
"scentName": "ผลไม้รวม",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 153,
|
||||||
|
"scentName": "กล้วย",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 203,
|
||||||
|
"scentName": "ทับทิม",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 208,
|
||||||
|
"scentName": "มะละกอสุก",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 218,
|
||||||
|
"scentName": "กลิ่นลูกพีช",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 223,
|
||||||
|
"scentName": "กลิ่นองุ่น",
|
||||||
|
"scentFamily": "fruity",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 13,
|
||||||
|
"scentName": "วานิลลา",
|
||||||
|
"scentFamily": "sweet_warm",
|
||||||
|
"moodBenefit": "romantic_cozy",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 113,
|
||||||
|
"scentName": "หมากฝรั่ง",
|
||||||
|
"scentFamily": "sweet_warm",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 128,
|
||||||
|
"scentName": "แซนด้าวูส",
|
||||||
|
"scentFamily": "sweet_warm",
|
||||||
|
"moodBenefit": "focus_mind",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "workspace",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 158,
|
||||||
|
"scentName": "กาแฟ",
|
||||||
|
"scentFamily": "sweet_warm",
|
||||||
|
"moodBenefit": "focus_mind",
|
||||||
|
"scentIntensity": "strong",
|
||||||
|
"roomPlacement": "workspace",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 168,
|
||||||
|
"scentName": "กลิ่นน้ำนม",
|
||||||
|
"scentFamily": "sweet_warm",
|
||||||
|
"moodBenefit": "relax_sleep",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "bedroom",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 43,
|
||||||
|
"scentName": "โอเชี่ยน",
|
||||||
|
"scentFamily": "aquatic_ozone",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 48,
|
||||||
|
"scentName": "เซเว่นซี",
|
||||||
|
"scentFamily": "aquatic_ozone",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 143,
|
||||||
|
"scentName": "โอโซน",
|
||||||
|
"scentFamily": "aquatic_ozone",
|
||||||
|
"moodBenefit": "refresh_energize",
|
||||||
|
"scentIntensity": "medium",
|
||||||
|
"roomPlacement": "living_room",
|
||||||
|
"specs": {
|
||||||
|
"compatible_device": "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
"coverage_area": "15 - 25",
|
||||||
|
"scent_duration": "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
"formula_base": "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
"usage_instructions": "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -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);
|
||||||
|
});
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
let prisma: any;
|
||||||
|
try {
|
||||||
|
const mod = await import("../apps/backend/src/lib/prisma");
|
||||||
|
prisma = mod.default;
|
||||||
|
} catch {
|
||||||
|
const mod = await import("./src/lib/prisma");
|
||||||
|
prisma = mod.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RunOptions {
|
||||||
|
deleteOldBrands?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function reassignProductsToHomAroma(options: RunOptions = {}) {
|
||||||
|
console.log("=== Reassign Products to 'Hom Aroma' Brand ===");
|
||||||
|
|
||||||
|
// 1. Find or create 'Hom Aroma' brand
|
||||||
|
let homAroma = await prisma.productBrand.findFirst({
|
||||||
|
where: { name: { equals: "Hom Aroma", mode: "insensitive" } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!homAroma) {
|
||||||
|
homAroma = await prisma.productBrand.create({
|
||||||
|
data: { name: "Hom Aroma" },
|
||||||
|
});
|
||||||
|
console.log(`✓ Created 'Hom Aroma' brand with ID: ${homAroma.id}`);
|
||||||
|
} else {
|
||||||
|
console.log(`✓ Found existing 'Hom Aroma' brand with ID: ${homAroma.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Ensure translations for Hom Aroma
|
||||||
|
const translations = [
|
||||||
|
{ langCode: "th", name: "Hom Aroma" },
|
||||||
|
{ langCode: "en", name: "Hom Aroma" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const tr of translations) {
|
||||||
|
await prisma.productBrandTranslation.upsert({
|
||||||
|
where: {
|
||||||
|
brandId_langCode: {
|
||||||
|
brandId: homAroma.id,
|
||||||
|
langCode: tr.langCode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
brandId: homAroma.id,
|
||||||
|
langCode: tr.langCode,
|
||||||
|
name: tr.name,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: tr.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("✓ Brand translations ensured for 'th' and 'en'.");
|
||||||
|
|
||||||
|
// 3. Reassign all products to Hom Aroma
|
||||||
|
const totalProducts = await prisma.product.count();
|
||||||
|
const updateResult = await prisma.product.updateMany({
|
||||||
|
data: {
|
||||||
|
brandId: homAroma.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log(`✓ Reassigned ${updateResult.count} of ${totalProducts} products to Brand '${homAroma.name}' (ID: ${homAroma.id}).`);
|
||||||
|
|
||||||
|
// 4. Handle old brands
|
||||||
|
const otherBrands = await prisma.productBrand.findMany({
|
||||||
|
where: { id: { not: homAroma.id } },
|
||||||
|
include: { _count: { select: { products: true } } },
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Found ${otherBrands.length} other brands in database.`);
|
||||||
|
|
||||||
|
if (options.deleteOldBrands && otherBrands.length > 0) {
|
||||||
|
console.log(`Deleting ${otherBrands.length} old scent brand records...`);
|
||||||
|
const delResult = await prisma.productBrand.deleteMany({
|
||||||
|
where: { id: { not: homAroma.id } },
|
||||||
|
});
|
||||||
|
console.log(`✓ Deleted ${delResult.count} old scent brands successfully.`);
|
||||||
|
} else if (!options.deleteOldBrands) {
|
||||||
|
console.log("Old brands preserved (deleteOldBrands is false).");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Final summary verification
|
||||||
|
const finalHomAromaProducts = await prisma.product.count({
|
||||||
|
where: { brandId: homAroma.id },
|
||||||
|
});
|
||||||
|
const remainingBrands = await prisma.productBrand.count();
|
||||||
|
|
||||||
|
console.log("\n=== Final Summary ===");
|
||||||
|
console.log(`Total Products linked to 'Hom Aroma': ${finalHomAromaProducts} / ${totalProducts}`);
|
||||||
|
console.log(`Total Brands remaining in database: ${remainingBrands}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
homAromaId: homAroma.id,
|
||||||
|
totalProducts,
|
||||||
|
reassignedCount: updateResult.count,
|
||||||
|
otherBrandsCount: otherBrands.length,
|
||||||
|
remainingBrandsCount: remainingBrands,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-run if executed directly
|
||||||
|
if (import.meta.main) {
|
||||||
|
const shouldDelete = process.argv.includes("--delete-old");
|
||||||
|
reassignProductsToHomAroma({ deleteOldBrands: shouldDelete })
|
||||||
|
.then((res) => {
|
||||||
|
console.log("\nExecution completed:", res);
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("\nExecution failed:", err);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(() => prisma.$disconnect());
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import prisma from "../apps/backend/src/lib/prisma";
|
||||||
|
|
||||||
|
const PROD_API = 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";
|
||||||
|
|
||||||
|
interface ScentMapping {
|
||||||
|
productId: number;
|
||||||
|
scentName: string;
|
||||||
|
scentFamily: "floral" | "fresh_herbal" | "fruity" | "spa_relaxing" | "sweet_warm" | "aquatic_ozone";
|
||||||
|
moodBenefit: "relax_sleep" | "refresh_energize" | "focus_mind" | "odor_eliminator" | "romantic_cozy";
|
||||||
|
scentIntensity: "light" | "medium" | "strong";
|
||||||
|
roomPlacement: "bedroom" | "living_room" | "workspace" | "bathroom" | "car" | "commercial_spa";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 45 Reed Diffuser products mapping
|
||||||
|
const REED_DIFFUSER_MAPPINGS: ScentMapping[] = [
|
||||||
|
// Spa & Relaxing
|
||||||
|
{ productId: 3, scentName: "ตะไคร้", scentFamily: "spa_relaxing", moodBenefit: "odor_eliminator", scentIntensity: "strong", roomPlacement: "bathroom" },
|
||||||
|
{ productId: 63, scentName: "ลีลาวดี", scentFamily: "spa_relaxing", moodBenefit: "relax_sleep", scentIntensity: "medium", roomPlacement: "commercial_spa" },
|
||||||
|
{ productId: 138, scentName: "ดอกบัว", scentFamily: "spa_relaxing", moodBenefit: "focus_mind", scentIntensity: "light", roomPlacement: "commercial_spa" },
|
||||||
|
{ productId: 148, scentName: "ไทยสปา", scentFamily: "spa_relaxing", moodBenefit: "relax_sleep", scentIntensity: "strong", roomPlacement: "commercial_spa" },
|
||||||
|
|
||||||
|
// Fresh & Herbal
|
||||||
|
{ productId: 8, scentName: "ลาเวนเดอร์", scentFamily: "fresh_herbal", moodBenefit: "relax_sleep", scentIntensity: "medium", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 28, scentName: "ยูคาลิปตัส", scentFamily: "fresh_herbal", moodBenefit: "refresh_energize", scentIntensity: "strong", roomPlacement: "workspace" },
|
||||||
|
{ productId: 33, scentName: "เฟรชลาเวนเดอร์", scentFamily: "fresh_herbal", moodBenefit: "relax_sleep", scentIntensity: "medium", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 38, scentName: "เปเปอร์มิ้น", scentFamily: "fresh_herbal", moodBenefit: "refresh_energize", scentIntensity: "strong", roomPlacement: "workspace" },
|
||||||
|
{ productId: 73, scentName: "มะนาว", scentFamily: "fresh_herbal", moodBenefit: "odor_eliminator", scentIntensity: "strong", roomPlacement: "bathroom" },
|
||||||
|
{ productId: 98, scentName: "โรสเมรี่", scentFamily: "fresh_herbal", moodBenefit: "focus_mind", scentIntensity: "medium", roomPlacement: "workspace" },
|
||||||
|
{ productId: 123, scentName: "มะกรูด", scentFamily: "fresh_herbal", moodBenefit: "odor_eliminator", scentIntensity: "strong", roomPlacement: "bathroom" },
|
||||||
|
|
||||||
|
// Floral
|
||||||
|
{ productId: 18, scentName: "ซากุระ", scentFamily: "floral", moodBenefit: "romantic_cozy", scentIntensity: "light", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 23, scentName: "มะลิ", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "strong", roomPlacement: "living_room" },
|
||||||
|
{ productId: 53, scentName: "ดอกแก้ว", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "strong", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 58, scentName: "ดอกโมก", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "medium", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 93, scentName: "ดอกลิลลี่", scentFamily: "floral", moodBenefit: "romantic_cozy", scentIntensity: "medium", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 103, scentName: "อัญชัน", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "light", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 108, scentName: "กุหลาบแดง", scentFamily: "floral", moodBenefit: "romantic_cozy", scentIntensity: "strong", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 133, scentName: "ดอกปีบ", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "medium", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 163, scentName: "ดอกชมนาด", scentFamily: "floral", moodBenefit: "romantic_cozy", scentIntensity: "strong", roomPlacement: "living_room" },
|
||||||
|
{ productId: 173, scentName: "ดอกการะเวก", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "strong", roomPlacement: "living_room" },
|
||||||
|
{ productId: 178, scentName: "ดอกจำปา", scentFamily: "floral", moodBenefit: "romantic_cozy", scentIntensity: "strong", roomPlacement: "living_room" },
|
||||||
|
{ productId: 183, scentName: "ดอกพิกุล", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "medium", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 188, scentName: "ดอกพุด", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "strong", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 193, scentName: "ดอกราตรี", scentFamily: "floral", moodBenefit: "romantic_cozy", scentIntensity: "strong", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 198, scentName: "ดอกลำดวน", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "medium", roomPlacement: "bedroom" },
|
||||||
|
{ productId: 213, scentName: "ดอกราชาวดี", scentFamily: "floral", moodBenefit: "relax_sleep", scentIntensity: "medium", roomPlacement: "bedroom" },
|
||||||
|
|
||||||
|
// Fruity
|
||||||
|
{ productId: 68, scentName: "ส้ม", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 78, scentName: "แคนตาลูป", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 83, scentName: "แอปเปิ้ลแดง", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 88, scentName: "แอปเปิ้ลเขียว", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "workspace" },
|
||||||
|
{ productId: 118, scentName: "ผลไม้รวม", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 153, scentName: "กล้วย", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 203, scentName: "ทับทิม", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 208, scentName: "มะละกอสุก", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 218, scentName: "กลิ่นลูกพีช", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 223, scentName: "กลิ่นองุ่น", scentFamily: "fruity", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
|
||||||
|
// Sweet & Warm
|
||||||
|
{ productId: 13, scentName: "วานิลลา", scentFamily: "sweet_warm", moodBenefit: "romantic_cozy", scentIntensity: "strong", roomPlacement: "living_room" },
|
||||||
|
{ productId: 113, scentName: "หมากฝรั่ง", scentFamily: "sweet_warm", moodBenefit: "refresh_energize", scentIntensity: "strong", roomPlacement: "living_room" },
|
||||||
|
{ productId: 128, scentName: "แซนด้าวูส", scentFamily: "sweet_warm", moodBenefit: "focus_mind", scentIntensity: "medium", roomPlacement: "workspace" },
|
||||||
|
{ productId: 158, scentName: "กาแฟ", scentFamily: "sweet_warm", moodBenefit: "focus_mind", scentIntensity: "strong", roomPlacement: "workspace" },
|
||||||
|
{ productId: 168, scentName: "กลิ่นน้ำนม", scentFamily: "sweet_warm", moodBenefit: "relax_sleep", scentIntensity: "medium", roomPlacement: "bedroom" },
|
||||||
|
|
||||||
|
// Aquatic & Ozone
|
||||||
|
{ productId: 43, scentName: "โอเชี่ยน", scentFamily: "aquatic_ozone", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 48, scentName: "เซเว่นซี", scentFamily: "aquatic_ozone", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
{ productId: 143, scentName: "โอโซน", scentFamily: "aquatic_ozone", moodBenefit: "refresh_energize", scentIntensity: "medium", roomPlacement: "living_room" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Standard Reed Diffuser specifications common across all items
|
||||||
|
const REED_DIFFUSER_SPECS = {
|
||||||
|
compatible_device: "ก้านไม้ไฟเบอร์กระจายกลิ่น (Fiber Reeds) / ขวดแก้วตั้งโต๊ะ",
|
||||||
|
coverage_area: "15 - 25",
|
||||||
|
scent_duration: "3 - 4 สัปดาห์ (ขนาด 60ml), 6 - 8 สัปดาห์ (ขนาด 85-120ml)",
|
||||||
|
formula_base: "Alcohol-Free Reed Diffuser Base + Premium Fragrance Oil",
|
||||||
|
usage_instructions: "เปิดจุกขวดแก้ว ปักก้านไม้ 4-5 ก้านลงในขวด กลับด้านก้านไม้ทุก 3-5 วันเพื่อเพิ่มความฟุ้งกระจายของกลิ่น หลีกเลี่ยงการวางกลางแดดหรือลมพัดแรง",
|
||||||
|
};
|
||||||
|
|
||||||
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log("=== Seeding Product Attributes for Reed Diffuser Products ===");
|
||||||
|
console.log(`Targeting ${REED_DIFFUSER_MAPPINGS.length} products on API: ${PROD_API}`);
|
||||||
|
|
||||||
|
// 1. Authenticate to get JWT token
|
||||||
|
console.log(`1. Logging in as '${ADMIN_USER}' to obtain JWT token...`);
|
||||||
|
const loginRes = await fetch(`${PROD_API}/auth/login`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ username: ADMIN_USER, password: ADMIN_PASS }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!loginRes.ok) {
|
||||||
|
throw new Error(`Login failed (${loginRes.status}): ${await loginRes.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { token } = (await loginRes.json()) as { token: string };
|
||||||
|
console.log("✓ Authenticated successfully. JWT token acquired.\n");
|
||||||
|
|
||||||
|
// 2. Fetch active attribute definitions from API to map codes to IDs
|
||||||
|
console.log("2. Fetching active attribute definitions from API...");
|
||||||
|
const defsRes = await fetch(`${PROD_API}/product-attributes/definitions?lang=th`);
|
||||||
|
const definitions = (await defsRes.json()) as Array<{ id: number; code: string; type: string }>;
|
||||||
|
const codeToDef = new Map<string, { id: number; code: string; type: string }>();
|
||||||
|
for (const def of definitions) {
|
||||||
|
codeToDef.set(def.code, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
const requiredCodes = [
|
||||||
|
"scent_family",
|
||||||
|
"mood_benefit",
|
||||||
|
"scent_intensity",
|
||||||
|
"room_placement",
|
||||||
|
"compatible_device",
|
||||||
|
"coverage_area",
|
||||||
|
"scent_duration",
|
||||||
|
"formula_base",
|
||||||
|
"usage_instructions",
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const code of requiredCodes) {
|
||||||
|
if (!codeToDef.has(code)) {
|
||||||
|
throw new Error(`Attribute definition code '${code}' not found in API!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("✓ All 9 attribute definitions verified on API.\n");
|
||||||
|
|
||||||
|
// Save the full matrix as JSON artifact
|
||||||
|
const exportPath = path.resolve(__dirname, "../data/reed_diffuser_attributes_matrix.json");
|
||||||
|
fs.writeFileSync(
|
||||||
|
exportPath,
|
||||||
|
JSON.stringify(
|
||||||
|
REED_DIFFUSER_MAPPINGS.map((m) => ({
|
||||||
|
...m,
|
||||||
|
specs: REED_DIFFUSER_SPECS,
|
||||||
|
})),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
),
|
||||||
|
"utf-8"
|
||||||
|
);
|
||||||
|
console.log(`✓ Saved mapping matrix to ${exportPath}\n`);
|
||||||
|
|
||||||
|
// 3. Process each product via API
|
||||||
|
console.log(`3. Updating attributes for ${REED_DIFFUSER_MAPPINGS.length} Reed Diffuser products via API...`);
|
||||||
|
let successCount = 0;
|
||||||
|
let failCount = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < REED_DIFFUSER_MAPPINGS.length; i++) {
|
||||||
|
const item = REED_DIFFUSER_MAPPINGS[i];
|
||||||
|
const attributesPayload = [
|
||||||
|
{
|
||||||
|
attributeDefinitionId: codeToDef.get("scent_family")!.id,
|
||||||
|
valueText: item.scentFamily,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributeDefinitionId: codeToDef.get("mood_benefit")!.id,
|
||||||
|
valueText: item.moodBenefit,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributeDefinitionId: codeToDef.get("scent_intensity")!.id,
|
||||||
|
valueText: item.scentIntensity,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributeDefinitionId: codeToDef.get("room_placement")!.id,
|
||||||
|
valueText: item.roomPlacement,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributeDefinitionId: codeToDef.get("compatible_device")!.id,
|
||||||
|
valueText: REED_DIFFUSER_SPECS.compatible_device,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributeDefinitionId: codeToDef.get("coverage_area")!.id,
|
||||||
|
valueText: REED_DIFFUSER_SPECS.coverage_area,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributeDefinitionId: codeToDef.get("scent_duration")!.id,
|
||||||
|
valueText: REED_DIFFUSER_SPECS.scent_duration,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributeDefinitionId: codeToDef.get("formula_base")!.id,
|
||||||
|
valueText: REED_DIFFUSER_SPECS.formula_base,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributeDefinitionId: codeToDef.get("usage_instructions")!.id,
|
||||||
|
valueText: REED_DIFFUSER_SPECS.usage_instructions,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updateRes = await fetch(`${PROD_API}/products/${item.productId}/attributes`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ attributes: attributesPayload }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updateRes.ok) {
|
||||||
|
const errText = await updateRes.text();
|
||||||
|
console.error(`❌ [${i + 1}/${REED_DIFFUSER_MAPPINGS.length}] Product #${item.productId} (${item.scentName}) failed: ${updateRes.status} ${errText}`);
|
||||||
|
failCount++;
|
||||||
|
} else {
|
||||||
|
console.log(`✓ [${i + 1}/${REED_DIFFUSER_MAPPINGS.length}] Product #${item.productId} (${item.scentName}) linked with 9 attributes.`);
|
||||||
|
successCount++;
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(`❌ [${i + 1}/${REED_DIFFUSER_MAPPINGS.length}] Product #${item.productId} error: ${err.message}`);
|
||||||
|
failCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(80);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n=== API Seeding Completed: ${successCount} succeeded, ${failCount} failed ===\n`);
|
||||||
|
|
||||||
|
// 4. Also sync to Local Database if connected
|
||||||
|
try {
|
||||||
|
console.log("4. Syncing attributes to Local Database via Prisma...");
|
||||||
|
const localDefs = await prisma.attributeDefinition.findMany();
|
||||||
|
const localDefMap = new Map<string, number>();
|
||||||
|
for (const d of localDefs) {
|
||||||
|
localDefMap.set(d.code, d.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let localSuccess = 0;
|
||||||
|
for (const item of REED_DIFFUSER_MAPPINGS) {
|
||||||
|
// Find local product by name
|
||||||
|
const expectedName = `${item.scentName} Reed Diffuser`;
|
||||||
|
const localProd = await prisma.product.findFirst({
|
||||||
|
where: {
|
||||||
|
name: { contains: item.scentName },
|
||||||
|
category: { name: { contains: "ก้านไม้" } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!localProd) continue;
|
||||||
|
|
||||||
|
const attrs = [
|
||||||
|
{ code: "scent_family", val: item.scentFamily, isText: true },
|
||||||
|
{ code: "mood_benefit", val: item.moodBenefit, isText: true },
|
||||||
|
{ code: "scent_intensity", val: item.scentIntensity, isText: true },
|
||||||
|
{ code: "room_placement", val: item.roomPlacement, isText: true },
|
||||||
|
{ code: "compatible_device", val: REED_DIFFUSER_SPECS.compatible_device, isText: true },
|
||||||
|
{ code: "coverage_area", val: REED_DIFFUSER_SPECS.coverage_area, isText: true },
|
||||||
|
{ code: "scent_duration", val: REED_DIFFUSER_SPECS.scent_duration, isText: true },
|
||||||
|
{ code: "formula_base", val: REED_DIFFUSER_SPECS.formula_base, isText: true },
|
||||||
|
{ code: "usage_instructions", val: REED_DIFFUSER_SPECS.usage_instructions, isText: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const attr of attrs) {
|
||||||
|
const defId = localDefMap.get(attr.code);
|
||||||
|
if (!defId) continue;
|
||||||
|
|
||||||
|
await prisma.productAttribute.upsert({
|
||||||
|
where: {
|
||||||
|
productId_attributeDefinitionId: {
|
||||||
|
productId: localProd.id,
|
||||||
|
attributeDefinitionId: defId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
productId: localProd.id,
|
||||||
|
attributeDefinitionId: defId,
|
||||||
|
valueText: attr.val,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
valueText: attr.val,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
localSuccess++;
|
||||||
|
}
|
||||||
|
console.log(`✓ Synced ${localSuccess} products into Local Database.\n`);
|
||||||
|
} catch (err: any) {
|
||||||
|
console.warn(`Local DB sync skipped or encountered an error: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then(() => {
|
||||||
|
console.log("✅ All Reed Diffuser Product Attributes successfully linked!");
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("❌ Process failed:", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user