x
This commit is contained in:
1 parent
81ed9af406
commit
4536db6741
56 files changed
+1068
-139
No files matched your search
@@ -16,7 +16,7 @@ description: Main Agent / Multi-Agent Orchestrator for planning, delegation, coo
|
||||
|
||||
## Operating Principles
|
||||
- **Clarify before committing:** ถ้า requirement เสี่ยงหรือไม่ชัด ให้ค้นจาก repo/task/context ก่อน ถ้ายังไม่พอจึงถาม User
|
||||
- **Delegate by default:** งานเฉพาะทางควรส่งให้ specialist agent ที่เหมาะสม ยกเว้นงานเล็กมากหรือ User สั่งให้ทำเองชัดเจน
|
||||
- **Delegate by default:** งานเฉพาะทางต้องส่งให้ specialist agent ที่เหมาะสมทำเสมอ หาก User สั่งว่า "ทำได้เลย" หรือ "ลงมือได้เลย" หมายถึงการสั่งให้คุณส่งงานต่อให้ Sub Agent ที่เหมาะสมลงมือทำ
|
||||
- **Read before assigning:** ก่อนสั่งงานโดยอ้างชื่อ field, type, API, schema, route, token, หรือ component ต้องอ่าน source จริงก่อน
|
||||
- **Verify after delivery:** งานจาก agent ต้องถูกตรวจตาม risk ไม่ใช่รับผลแล้วส่งต่อทันที
|
||||
- **Escalate early:** ถ้าปัญหาข้ามขอบเขต agent หรือกระทบ contract/schema/infra ให้หยุดและแจ้งผู้รับผิดชอบ
|
||||
@@ -66,7 +66,7 @@ description: Main Agent / Multi-Agent Orchestrator for planning, delegation, coo
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **RULE #2: ใช้ specialist agent ให้ถูกงาน**
|
||||
> อย่าทำงานเฉพาะทางเองถ้ามี agent ที่รับผิดชอบชัดเจน ยกเว้นงานเล็กมาก, ไม่มีเครื่องมือ delegate พร้อมใช้, หรือ User สั่งให้ลงมือเอง
|
||||
> อย่าทำงานเฉพาะทางเองเด็ดขาด ต้องมอบหมายให้ agent ที่รับผิดชอบชัดเจนเสมอ หาก User สั่งว่า "ทำได้เลย" หรือ "ลงมือได้เลย" หมายถึงคำสั่งให้คุณเรียกใช้งาน Sub Agent ที่มี Skill เหมาะสมเพื่อลงมือทำ
|
||||
|
||||
> [!WARNING]
|
||||
> **RULE #3: ห้าม bypass process หรือ tool**
|
||||
|
||||
@@ -46,6 +46,7 @@ function App() {
|
||||
<Route element={<AdminLayout />}>
|
||||
<Route path="/" element={<DashboardPage />} />
|
||||
<Route path="/products" element={<ProductsPage />} />
|
||||
<Route path="/products/new" element={<ProductEditPage />} />
|
||||
<Route path="/products/:id/edit" element={<ProductEditPage />} />
|
||||
<Route path="/categories" element={<CategoriesPage />} />
|
||||
<Route path="/customers" element={<CustomersPage />} />
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function OrderDetailPage() {
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-bold">{item.productName}</p>
|
||||
<p className="text-xs text-slate-500 uppercase">{item.variantSize}</p>
|
||||
<p className="text-xs text-slate-500 uppercase">{item.variantName}</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-cyan-glow font-bold">{formatPrice(item.priceAtPurchase)}</p>
|
||||
|
||||
@@ -88,18 +88,16 @@ export default function ProductEditPage() {
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const [prodRes, catRes] = await Promise.all([
|
||||
fetch(`${API_BASE}/products/${id}?lang=all`),
|
||||
fetch(`${API_BASE}/categories`)
|
||||
])
|
||||
const catRes = await fetch(`${API_BASE}/categories`)
|
||||
const catData: Category[] = await catRes.json()
|
||||
setCategories(catData)
|
||||
|
||||
if (id) {
|
||||
const prodRes = await fetch(`${API_BASE}/products/${id}?lang=all`)
|
||||
if (!prodRes.ok) throw new Error('Product not found')
|
||||
|
||||
const prodData: Product = await prodRes.json()
|
||||
const catData: Category[] = await catRes.json()
|
||||
|
||||
setProduct(prodData)
|
||||
setCategories(catData)
|
||||
setCategoryId(prodData.categoryId)
|
||||
|
||||
const parseTranslatable = (val: any) => typeof val === 'object' && val !== null ? val : { th: val || '' }
|
||||
@@ -111,6 +109,16 @@ export default function ProductEditPage() {
|
||||
setOptions(parseOptions(prodData.options))
|
||||
setVariants(prodData.variants || [])
|
||||
setImages(prodData.images || [])
|
||||
} else {
|
||||
setProduct(null)
|
||||
if (catData.length > 0) setCategoryId(catData[0].id)
|
||||
setName({ th: '', en: '' })
|
||||
setDescription({ th: '', en: '' })
|
||||
setDetails({ th: '', en: '' })
|
||||
setOptions([])
|
||||
setVariants([])
|
||||
setImages([])
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message)
|
||||
} finally {
|
||||
@@ -172,8 +180,12 @@ export default function ProductEditPage() {
|
||||
setSuccess(false)
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/products/${id}`, {
|
||||
method: 'PATCH',
|
||||
const isNew = !id
|
||||
const method = isNew ? 'POST' : 'PUT'
|
||||
const url = isNew ? `${API_BASE}/products` : `${API_BASE}/products/${id}`
|
||||
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
categoryId,
|
||||
@@ -182,6 +194,7 @@ export default function ProductEditPage() {
|
||||
details,
|
||||
options,
|
||||
variants: variants.map(v => ({
|
||||
...(v.id ? { id: v.id } : {}),
|
||||
option1: v.option1 || '',
|
||||
option2: v.option2 || '',
|
||||
option3: v.option3 || '',
|
||||
@@ -191,16 +204,21 @@ export default function ProductEditPage() {
|
||||
barcode: v.barcode || ''
|
||||
})),
|
||||
images: images.filter(img => img.url).map((img, idx) => ({
|
||||
...(img.id ? { id: img.id } : {}),
|
||||
url: img.url,
|
||||
order: img.order ?? idx
|
||||
order: img.order ?? (typeof img.order === 'number' ? img.order : idx)
|
||||
}))
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update product')
|
||||
if (!response.ok) throw new Error(isNew ? 'Failed to create product' : 'Failed to update product')
|
||||
|
||||
setSuccess(true)
|
||||
if (isNew) {
|
||||
setTimeout(() => navigate('/products'), 1500)
|
||||
} else {
|
||||
setTimeout(() => setSuccess(false), 3000)
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message)
|
||||
} finally {
|
||||
|
||||
@@ -81,6 +81,7 @@ export default function ProductsPage() {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
categoryId: selectedProduct.categoryId,
|
||||
options: selectedProduct.options,
|
||||
variants: selectedProduct.variants.map(v => ({ option1: v.option1, option2: v.option2, option3: v.option3, sku: v.sku, barcode: v.barcode, stock: v.stock, price: v.price })),
|
||||
images: updatedImages
|
||||
})
|
||||
@@ -145,6 +146,7 @@ export default function ProductsPage() {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
categoryId: selectedProduct.categoryId,
|
||||
options: selectedProduct.options,
|
||||
variants: selectedProduct.variants.map(v => ({ option1: v.option1, option2: v.option2, option3: v.option3, sku: v.sku, barcode: v.barcode, stock: v.stock, price: v.price })),
|
||||
images: updatedImages
|
||||
})
|
||||
@@ -234,7 +236,10 @@ export default function ProductsPage() {
|
||||
{meta ? `Displaying ${products.length} of ${meta.total} products` : 'Loading products...'}
|
||||
</p>
|
||||
</div>
|
||||
<button className="btn-primary flex items-center gap-2">
|
||||
<button
|
||||
className="btn-primary flex items-center gap-2"
|
||||
onClick={() => navigate('/products/new')}
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
Add Product
|
||||
</button>
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `variantSize` on the `OrderItem` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "OrderItem" DROP COLUMN "variantSize",
|
||||
ADD COLUMN "variantName" TEXT;
|
||||
@@ -273,7 +273,7 @@ model OrderItem {
|
||||
quantity Int
|
||||
priceAtPurchase Float
|
||||
productName String
|
||||
variantSize String?
|
||||
variantName String?
|
||||
productImage String?
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=fix-menus.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"fix-menus.d.ts","sourceRoot":"","sources":["fix-menus.ts"],"names":[],"mappings":""}
|
||||
@@ -0,0 +1,46 @@
|
||||
import prisma from '../src/lib/prisma';
|
||||
function fixNode(node) {
|
||||
if (node.translations) {
|
||||
const labelKey = node.title !== undefined ? 'title' : 'label';
|
||||
if (typeof node[labelKey] === 'string') {
|
||||
node[labelKey] = { en: node[labelKey] };
|
||||
}
|
||||
node[labelKey] = { ...(node[labelKey] || {}), ...node.translations };
|
||||
delete node.translations;
|
||||
}
|
||||
if (node.columns && Array.isArray(node.columns)) {
|
||||
node.columns.forEach((col) => fixNode(col));
|
||||
}
|
||||
if (node.links && Array.isArray(node.links)) {
|
||||
node.links.forEach((link) => fixNode(link));
|
||||
}
|
||||
if (node.items && Array.isArray(node.items)) {
|
||||
node.items.forEach((item) => fixNode(item));
|
||||
}
|
||||
}
|
||||
async function main() {
|
||||
const menu = await prisma.menu.findUnique({
|
||||
where: { name: 'main' }
|
||||
});
|
||||
if (!menu || !menu.payload) {
|
||||
console.log('No main menu payload found');
|
||||
return;
|
||||
}
|
||||
const payload = menu.payload;
|
||||
console.log('Original payload length:', payload.length);
|
||||
payload.forEach(fixNode);
|
||||
await prisma.menu.update({
|
||||
where: { name: 'main' },
|
||||
data: { payload }
|
||||
});
|
||||
console.log('Menu fixed successfully');
|
||||
}
|
||||
main()
|
||||
.catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
//# sourceMappingURL=fix-menus.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"fix-menus.js","sourceRoot":"","sources":["fix-menus.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AAEvC,SAAS,OAAO,CAAC,IAAS;IACxB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAC9D,IAAI,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1C,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACrE,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,IAAI,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAChD,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QACxC,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;KACxB,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC1C,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAgB,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,0BAA0B,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAExD,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEzB,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACvB,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACvB,IAAI,EAAE,EAAE,OAAO,EAAE;KAClB,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;AACzC,CAAC;AAED,IAAI,EAAE;KACH,KAAK,CAAC,CAAC,CAAC,EAAE;IACT,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC;KACD,OAAO,CAAC,KAAK,IAAI,EAAE;IAClB,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;AAC7B,CAAC,CAAC,CAAC"}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=fix-scent-guide.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"fix-scent-guide.d.ts","sourceRoot":"","sources":["fix-scent-guide.ts"],"names":[],"mappings":""}
|
||||
@@ -0,0 +1,53 @@
|
||||
import prisma from '../src/lib/prisma';
|
||||
import crypto from 'crypto';
|
||||
async function main() {
|
||||
const menu = await prisma.menu.findUnique({
|
||||
where: { name: 'main' }
|
||||
});
|
||||
if (!menu || !menu.payload) {
|
||||
console.error('Menu main not found or payload is empty');
|
||||
return;
|
||||
}
|
||||
const payload = menu.payload;
|
||||
let updated = false;
|
||||
for (let i = 0; i < payload.length; i++) {
|
||||
const node = payload[i];
|
||||
if (node.nodeId === 'b4b2faf8-8881-4ca9-a8d5-a88054a37385') {
|
||||
if (node.links && !node.columns) {
|
||||
console.log('Fixing Scent Guide node...');
|
||||
const newColumn = {
|
||||
nodeId: crypto.randomUUID(),
|
||||
title: {
|
||||
en: 'Essential Oils',
|
||||
th: 'น้ำมันหอมระเหย',
|
||||
ja: 'エッセンシャルオイル',
|
||||
ko: '에센셜 오일',
|
||||
vi: 'Tinh dầu',
|
||||
zh: '精油'
|
||||
},
|
||||
links: node.links
|
||||
};
|
||||
node.columns = [newColumn];
|
||||
delete node.links;
|
||||
updated = true;
|
||||
}
|
||||
else {
|
||||
console.log('Scent Guide node already has columns or no links to fix.');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updated) {
|
||||
await prisma.menu.update({
|
||||
where: { name: 'main' },
|
||||
data: { payload }
|
||||
});
|
||||
console.log('Successfully updated main menu payload.');
|
||||
}
|
||||
else {
|
||||
console.log('No updates were necessary.');
|
||||
}
|
||||
}
|
||||
main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
//# sourceMappingURL=fix-scent-guide.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"fix-scent-guide.js","sourceRoot":"","sources":["fix-scent-guide.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AACvC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QACxC,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;KACxB,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;QACzD,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAgB,CAAC;IACtC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,IAAI,CAAC,MAAM,KAAK,sCAAsC,EAAE,CAAC;YAC3D,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAChC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;gBAE1C,MAAM,SAAS,GAAG;oBAChB,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE;oBAC3B,KAAK,EAAE;wBACL,EAAE,EAAE,gBAAgB;wBACpB,EAAE,EAAE,gBAAgB;wBACpB,EAAE,EAAE,YAAY;wBAChB,EAAE,EAAE,QAAQ;wBACZ,EAAE,EAAE,UAAU;wBACd,EAAE,EAAE,IAAI;qBACT;oBACD,KAAK,EAAE,IAAI,CAAC,KAAK;iBAClB,CAAC;gBAEF,IAAI,CAAC,OAAO,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC3B,OAAO,IAAI,CAAC,KAAK,CAAC;gBAClB,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;YACvB,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;YACvB,IAAI,EAAE,EAAE,OAAO,EAAE;SAClB,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;IACzD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,IAAI,EAAE;KACH,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;KACpB,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC"}
|
||||
@@ -7,7 +7,6 @@ import { categoriesRoute } from "./routes/categories";
|
||||
import { productsRoute } from "./routes/products";
|
||||
import { authRoute } from "./routes/auth";
|
||||
import { uploadRoute } from "./routes/upload";
|
||||
import { heroRoute } from "./routes/hero";
|
||||
import { ordersRoute } from "./routes/orders";
|
||||
import { settingsRoute } from "./routes/settings";
|
||||
import { articlesRoute } from "./routes/articles";
|
||||
@@ -27,9 +26,10 @@ import { lineRoutes } from "./routes/line";
|
||||
import { menusRoute } from "./routes/menus";
|
||||
import { newsletterRoute } from "./routes/newsletter";
|
||||
import { mediaRoute } from "./routes/media";
|
||||
import { contactsRoute } from "./routes/contacts";
|
||||
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3051;
|
||||
const CORS_ORIGIN = process.env.CORS_ORIGIN || "http://localhost:5173";
|
||||
const JWT_SECRET = process.env.JWT_SECRET || "homaroma_secret_2026_scent";
|
||||
const JWT_SECRET = process.env.JWT_SECRET || "homaroma_secret_2026_brand";
|
||||
const isDevelopment = process.env.NODE_ENV !== "production";
|
||||
const isPrivateLanHostname = (hostname) => {
|
||||
return (hostname === "localhost" ||
|
||||
@@ -114,7 +114,7 @@ const app = new Elysia()
|
||||
}
|
||||
return file;
|
||||
})
|
||||
.group("/api", (app) => app.use(authRoute).use(categoriesRoute).use(productsRoute).use(uploadRoute).use(heroRoute).use(ordersRoute).use(settingsRoute).use(articlesRoute).use(tiktokRoute).use(rawMaterialsRoute).use(shopeeRoute).use(tiktokOrderRoute).use(lazadaOrderRoute).use(customersRoute).use(couponsRoute).use(reviewsRoute).use(promotionsRoute).use(pagesRoute).use(paymentsRoute).use(profileRoute).use(lineRoutes).use(menusRoute).use(newsletterRoute).use(mediaRoute))
|
||||
.group("/api", (app) => app.use(authRoute).use(categoriesRoute).use(productsRoute).use(uploadRoute).use(ordersRoute).use(settingsRoute).use(articlesRoute).use(tiktokRoute).use(rawMaterialsRoute).use(shopeeRoute).use(tiktokOrderRoute).use(lazadaOrderRoute).use(customersRoute).use(couponsRoute).use(reviewsRoute).use(promotionsRoute).use(pagesRoute).use(paymentsRoute).use(profileRoute).use(lineRoutes).use(menusRoute).use(newsletterRoute).use(mediaRoute).use(contactsRoute))
|
||||
.listen({ port: PORT, hostname: "0.0.0.0" });
|
||||
console.log(`🌿 HomAroma API is running at http://${app.server?.hostname}:${app.server?.port}`);
|
||||
console.log(`📖 Swagger docs: http://${app.server?.hostname}:${app.server?.port}/swagger`);
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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 truncated
|
||||
@@ -20,7 +20,7 @@ export async function sendOrderCreatedNotification(orderId) {
|
||||
contents: [
|
||||
{
|
||||
type: "text",
|
||||
text: `${item.productName}${item.variantSize ? ` (${item.variantSize})` : ""} x ${item.quantity}`,
|
||||
text: `${item.productName}${item.variantName ? ` (${item.variantName})` : ""} x ${item.quantity}`,
|
||||
size: "sm",
|
||||
color: "#333333",
|
||||
flex: 3,
|
||||
|
||||
@@ -24,7 +24,7 @@ export async function sendOrderCreatedNotification(orderId: number) {
|
||||
contents: [
|
||||
{
|
||||
type: "text",
|
||||
text: `${item.productName}${item.variantSize ? ` (${item.variantSize})` : ""} x ${item.quantity}`,
|
||||
text: `${item.productName}${item.variantName ? ` (${item.variantName})` : ""} x ${item.quantity}`,
|
||||
size: "sm",
|
||||
color: "#333333",
|
||||
flex: 3,
|
||||
|
||||
@@ -46,7 +46,7 @@ export const categoriesRoute = new Elysia({ prefix: "/categories" })
|
||||
translations: true,
|
||||
products: {
|
||||
include: {
|
||||
scent: true,
|
||||
brand: true,
|
||||
category: { include: { translations: true } },
|
||||
translations: true,
|
||||
variants: { orderBy: { price: "asc" } },
|
||||
@@ -94,7 +94,7 @@ export const categoriesRoute = new Elysia({ prefix: "/categories" })
|
||||
const trans = p.translations?.find((t) => t.langCode === lang) || p.translations?.find((t) => t.langCode === 'th') || p.translations?.[0];
|
||||
return {
|
||||
...p,
|
||||
name: trans?.name || p.scent?.name || '',
|
||||
name: trans?.name || p.brand?.name || '',
|
||||
description: trans?.description,
|
||||
details: trans?.details,
|
||||
category: { ...p.category, name: resolveCatName(p.category, lang) },
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
import { Elysia } from "elysia";
|
||||
export declare const contactsRoute: Elysia<"/contacts", {
|
||||
decorator: {};
|
||||
store: {};
|
||||
derive: {};
|
||||
resolve: {};
|
||||
}, {
|
||||
typebox: {};
|
||||
error: {};
|
||||
}, {
|
||||
schema: {};
|
||||
standaloneSchema: {};
|
||||
macro: {};
|
||||
macroFn: {};
|
||||
parser: {};
|
||||
response: {};
|
||||
}, {
|
||||
contacts: {
|
||||
post: {
|
||||
body: {
|
||||
name: string;
|
||||
email: string;
|
||||
message: string;
|
||||
};
|
||||
params: {};
|
||||
query: unknown;
|
||||
headers: unknown;
|
||||
response: {
|
||||
200: {
|
||||
success: boolean;
|
||||
data: {
|
||||
id: number;
|
||||
name: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
email: string;
|
||||
message: string;
|
||||
isRead: boolean;
|
||||
};
|
||||
error?: never;
|
||||
} | {
|
||||
success: boolean;
|
||||
error: string;
|
||||
data?: never;
|
||||
};
|
||||
422: {
|
||||
type: "validation";
|
||||
on: string;
|
||||
summary?: string;
|
||||
message?: string;
|
||||
found?: unknown;
|
||||
property?: string;
|
||||
expected?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
} & {
|
||||
contacts: {
|
||||
get: {
|
||||
body: unknown;
|
||||
params: {};
|
||||
query: unknown;
|
||||
headers: unknown;
|
||||
response: {
|
||||
200: {
|
||||
id: number;
|
||||
name: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
email: string;
|
||||
message: string;
|
||||
isRead: boolean;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
};
|
||||
} & {
|
||||
contacts: {
|
||||
":id": {
|
||||
patch: {
|
||||
body: {
|
||||
isRead: boolean;
|
||||
};
|
||||
params: {
|
||||
id: string;
|
||||
};
|
||||
query: unknown;
|
||||
headers: unknown;
|
||||
response: {
|
||||
200: {
|
||||
success: boolean;
|
||||
data: {
|
||||
id: number;
|
||||
name: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
email: string;
|
||||
message: string;
|
||||
isRead: boolean;
|
||||
};
|
||||
error?: never;
|
||||
} | {
|
||||
success: boolean;
|
||||
error: string;
|
||||
data?: never;
|
||||
};
|
||||
422: {
|
||||
type: "validation";
|
||||
on: string;
|
||||
summary?: string;
|
||||
message?: string;
|
||||
found?: unknown;
|
||||
property?: string;
|
||||
expected?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}, {
|
||||
derive: {};
|
||||
resolve: {};
|
||||
schema: {};
|
||||
standaloneSchema: {};
|
||||
response: {};
|
||||
}, {
|
||||
derive: {};
|
||||
resolve: {};
|
||||
schema: {};
|
||||
standaloneSchema: {};
|
||||
response: {};
|
||||
}>;
|
||||
//# sourceMappingURL=contacts.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"contacts.d.ts","sourceRoot":"","sources":["contacts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAK,MAAM,QAAQ,CAAC;AAGnC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsDtB,CAAC"}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import prisma from "../lib/prisma.js";
|
||||
export const contactsRoute = new Elysia({ prefix: "/contacts" })
|
||||
.post("/", async ({ body, set }) => {
|
||||
try {
|
||||
const { name, email, message } = body;
|
||||
const contactMessage = await prisma.contactMessage.create({
|
||||
data: {
|
||||
name,
|
||||
email,
|
||||
message
|
||||
}
|
||||
});
|
||||
return { success: true, data: contactMessage };
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
set.status = 500;
|
||||
return { success: false, error: "Failed to submit contact message" };
|
||||
}
|
||||
}, {
|
||||
body: t.Object({
|
||||
name: t.String({ minLength: 1 }),
|
||||
email: t.String({ format: "email" }),
|
||||
message: t.String({ minLength: 1 })
|
||||
})
|
||||
})
|
||||
.get("/", async () => {
|
||||
return await prisma.contactMessage.findMany({
|
||||
orderBy: { createdAt: "desc" }
|
||||
});
|
||||
})
|
||||
.patch("/:id", async ({ params, body, set }) => {
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
const { isRead } = body;
|
||||
const updated = await prisma.contactMessage.update({
|
||||
where: { id },
|
||||
data: { isRead }
|
||||
});
|
||||
return { success: true, data: updated };
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
set.status = 500;
|
||||
return { success: false, error: "Failed to update contact message" };
|
||||
}
|
||||
}, {
|
||||
params: t.Object({
|
||||
id: t.String()
|
||||
}),
|
||||
body: t.Object({
|
||||
isRead: t.Boolean()
|
||||
})
|
||||
});
|
||||
//# sourceMappingURL=contacts.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"contacts.js","sourceRoot":"","sources":["contacts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,MAAM,MAAM,kBAAkB,CAAC;AAEtC,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;KAC7D,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;IACjC,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAEtC,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;YACxD,IAAI,EAAE;gBACJ,IAAI;gBACJ,KAAK;gBACL,OAAO;aACR;SACF,CAAC,CAAC;QAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;QACjB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC;IACvE,CAAC;AACH,CAAC,EAAE;IACD,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;QAChC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QACpC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;KACpC,CAAC;CACH,CAAC;KACD,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE;IACnB,OAAO,MAAM,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC;QAC1C,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;KAC/B,CAAC,CAAC;AACL,CAAC,CAAC;KACD,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;IAC7C,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAExB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;YACjD,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE,EAAE,MAAM,EAAE;SACjB,CAAC,CAAC;QAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;QACjB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC;IACvE,CAAC;AACH,CAAC,EAAE;IACD,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;KACf,CAAC;IACF,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;KACpB,CAAC;CACH,CAAC,CAAC"}
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"menus.d.ts","sourceRoot":"","sources":["menus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAK,MAAM,QAAQ,CAAC;AAInC,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgLnB,CAAC"}
|
||||
{"version":3,"file":"menus.d.ts","sourceRoot":"","sources":["menus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAK,MAAM,QAAQ,CAAC;AAInC,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4LnB,CAAC"}
|
||||
@@ -23,7 +23,7 @@ export const menusRoute = new Elysia({ prefix: "/menus" })
|
||||
}
|
||||
});
|
||||
if (!menu) {
|
||||
return { name, items: [] };
|
||||
return { name, items: [], payload: null, isActive: false };
|
||||
}
|
||||
return menu;
|
||||
})
|
||||
@@ -59,10 +59,18 @@ export const menusRoute = new Elysia({ prefix: "/menus" })
|
||||
})
|
||||
.put("/:name", async ({ params: { name }, body, set }) => {
|
||||
try {
|
||||
const { items } = body;
|
||||
const { items, payload, isActive } = body;
|
||||
let menu = await prisma.menu.findUnique({ where: { name } });
|
||||
const updateData = {};
|
||||
if (payload !== undefined)
|
||||
updateData.payload = payload;
|
||||
if (isActive !== undefined)
|
||||
updateData.isActive = isActive;
|
||||
if (!menu) {
|
||||
menu = await prisma.menu.create({ data: { name } });
|
||||
menu = await prisma.menu.create({ data: { name, ...updateData } });
|
||||
}
|
||||
else if (Object.keys(updateData).length > 0) {
|
||||
menu = await prisma.menu.update({ where: { name }, data: updateData });
|
||||
}
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const existingItems = await tx.menuItem.findMany({ where: { menuId: menu.id } });
|
||||
@@ -118,6 +126,8 @@ export const menusRoute = new Elysia({ prefix: "/menus" })
|
||||
}
|
||||
}, {
|
||||
body: t.Object({
|
||||
payload: t.Optional(t.Any()),
|
||||
isActive: t.Optional(t.Boolean()),
|
||||
items: t.Array(t.Object({
|
||||
id: t.Optional(t.Number()),
|
||||
label: t.String(),
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"menus.js","sourceRoot":"","sources":["menus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,MAAM,MAAM,eAAe,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;KACvD,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE;IACnB,OAAO,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;QAChC,OAAO,EAAE;YACP,KAAK,EAAE;gBACL,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;gBACzB,OAAO,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;aAChC;SACF;KACF,CAAC,CAAC;AACL,CAAC,CAAC;KACD,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;IACjD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QACxC,KAAK,EAAE,EAAE,IAAI,EAAE;QACf,OAAO,EAAE;YACP,KAAK,EAAE;gBACL,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;gBACzB,OAAO,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;aAChC;SACF;KACF,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;KACD,GAAG,CAAC,SAAS,CAAC;KACd,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;IACjC,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAiF,CAAC;QAC1G,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;YACpC,IAAI,EAAE;gBACJ,IAAI;gBACJ,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;oBACb,MAAM,EAAE,KAAK;iBACd,CAAC,CAAC,CAAC,SAAS;aACd;YACD,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;SACzB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;QACjB,OAAO,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC;IAC5C,CAAC;AACH,CAAC,EAAE;IACD,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;YACjC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;YACjB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;YACf,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;SAClB,CAAC,CAAC,CAAC;KACL,CAAC;CACH,CAAC;KACD,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;IACvD,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,IAAqI,CAAC;QACxJ,IAAI,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAE7D,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,EAAO,EAAE,EAAE;YAC1C,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAClF,MAAM,eAAe,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5D,MAAM,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC;YACrE,MAAM,aAAa,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YAE3F,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC;YACzE,CAAC;YAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,IAAI,CAAC,EAAE,IAAI,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;oBACjD,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;wBACvB,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;wBACtB,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;qBAC9D,CAAC,CAAC;oBACH,IAAI,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;wBAC9B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;4BAClC,MAAM,EAAE,CAAC,mBAAmB,CAAC,MAAM,CAAC;gCAClC,KAAK,EAAE,EAAE,mBAAmB,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE;gCAC7E,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE;gCAC1B,MAAM,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE;6BACtE,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;wBACvB,IAAI,EAAE;4BACJ,MAAM,EAAE,IAAK,CAAC,EAAE;4BAChB,KAAK,EAAE,IAAI,CAAC,KAAK;4BACjB,GAAG,EAAE,IAAI,CAAC,GAAG;4BACb,KAAK,EAAE,IAAI,CAAC,KAAK;4BACjB,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;gCACxC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oCAClC,QAAQ,EAAE,CAAC,CAAC,QAAQ;oCACpB,KAAK,EAAE,CAAC,CAAC,KAAK;iCACf,CAAC,CAAC;6BACJ,CAAC,CAAC,CAAC,SAAS;yBACd;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;YAClC,KAAK,EAAE,EAAE,IAAI,EAAE;YACf,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,EAAE;SACnF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;QACjB,OAAO,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC;IAC5C,CAAC;AACH,CAAC,EAAE;IACD,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;YACtB,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YAC1B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;YACjB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;YACf,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;YACjB,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;gBACxC,QAAQ,EAAE,CAAC,CAAC,Line truncated
|
||||
{"version":3,"file":"menus.js","sourceRoot":"","sources":["menus.ts"],"names":[],"mappings":"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 truncated
|
||||
Vendored
+1
-1
@@ -62,7 +62,7 @@ export declare const ordersRoute: Elysia<"/orders", {
|
||||
pointsDiscount?: number;
|
||||
items: {
|
||||
variantId?: number;
|
||||
variantSize?: string;
|
||||
variantName?: string;
|
||||
productImage?: string;
|
||||
productId: number;
|
||||
quantity: number;
|
||||
|
||||
@@ -65,7 +65,7 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
quantity: item.quantity,
|
||||
priceAtPurchase: item.priceAtPurchase,
|
||||
productName: item.productName,
|
||||
variantSize: item.variantSize,
|
||||
variantName: item.variantName,
|
||||
productImage: item.productImage
|
||||
}))
|
||||
}
|
||||
@@ -119,7 +119,7 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
quantity: t.Number(),
|
||||
priceAtPurchase: t.Number(),
|
||||
productName: t.String(),
|
||||
variantSize: t.Optional(t.String()),
|
||||
variantName: t.Optional(t.String()),
|
||||
productImage: t.Optional(t.String())
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -74,7 +74,7 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
quantity: item.quantity,
|
||||
priceAtPurchase: item.priceAtPurchase,
|
||||
productName: item.productName,
|
||||
variantSize: item.variantSize,
|
||||
variantName: item.variantName,
|
||||
productImage: item.productImage
|
||||
}))
|
||||
}
|
||||
@@ -129,7 +129,7 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
quantity: t.Number(),
|
||||
priceAtPurchase: t.Number(),
|
||||
productName: t.String(),
|
||||
variantSize: t.Optional(t.String()),
|
||||
variantName: t.Optional(t.String()),
|
||||
productImage: t.Optional(t.String())
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["pages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAK,MAAM,QAAQ,CAAC;AA8BnC,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkXnB,CAAC"}
|
||||
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["pages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAK,MAAM,QAAQ,CAAC;AAgKnC,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6dnB,CAAC"}
|
||||
@@ -1,13 +1,164 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import prisma from "../lib/prisma";
|
||||
import { HeroSectionPayloadSchema, CategoriesSectionPayloadSchema, ProductsSectionPayloadSchema, BannerSectionPayloadSchema, TextSectionPayloadSchema, AnnouncementBarSectionPayloadSchema, NewsletterSectionPayloadSchema, EditorialTileSectionPayloadSchema, EditorialCarouselSectionPayloadSchema, HeroArticlePayloadSchema, RichTextPayloadSchema, ScentNotesBreakdownPayloadSchema, QuoteCalloutPayloadSchema, ProductCarouselPayloadSchema, AccordionSectionPayloadSchema, ContactInfoSectionPayloadSchema, ContactFormSectionPayloadSchema } from "@homaroma/types";
|
||||
import { LocalizedStringSchema, HeroSectionPayloadSchema, CategoriesSectionPayloadSchema, ProductsSectionPayloadSchema, BannerSectionPayloadSchema, TextSectionPayloadSchema, AnnouncementBarSectionPayloadSchema, NewsletterSectionPayloadSchema, EditorialTileSectionPayloadSchema, EditorialCarouselSectionPayloadSchema, HeroArticlePayloadSchema, RichTextPayloadSchema, ScentNotesBreakdownPayloadSchema, QuoteCalloutPayloadSchema, ProductCarouselPayloadSchema, AccordionSectionPayloadSchema, ContactInfoSectionPayloadSchema, ContactFormSectionPayloadSchema } from "@homaroma/types";
|
||||
import { adminOnly } from "../middleware/auth";
|
||||
function isLocalizedStringValue(value) {
|
||||
if (typeof value === "string")
|
||||
return true;
|
||||
if (!value || typeof value !== "object" || Array.isArray(value))
|
||||
return false;
|
||||
return Object.values(value).every((entry) => typeof entry === "string");
|
||||
}
|
||||
function isLocalizedStringList(value) {
|
||||
return Array.isArray(value) && value.every((entry) => isLocalizedStringValue(entry));
|
||||
}
|
||||
function hasValidString(value) {
|
||||
return typeof value === "string" && value.trim().length > 0;
|
||||
}
|
||||
function hasValidNumber(value) {
|
||||
return typeof value === "number" && Number.isFinite(value);
|
||||
}
|
||||
function hasValidBoolean(value) {
|
||||
return typeof value === "boolean";
|
||||
}
|
||||
function validateHeroArticlePayload(payload) {
|
||||
if (!payload || typeof payload !== "object")
|
||||
return "Payload must be a JSON object";
|
||||
if (!isLocalizedStringValue(payload.title))
|
||||
return "title: Must be a localized string";
|
||||
const localizedFields = ["subtitle", "author", "category"];
|
||||
for (const field of localizedFields) {
|
||||
if (payload[field] !== undefined && !isLocalizedStringValue(payload[field])) {
|
||||
return `${field}: Must be a localized string`;
|
||||
}
|
||||
}
|
||||
const stringFields = ["publishDate", "date", "imageUrl", "backgroundImageUrl", "overlayColor"];
|
||||
for (const field of stringFields) {
|
||||
if (payload[field] !== undefined && !hasValidString(payload[field])) {
|
||||
return `${field}: Must be a string`;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function validateRichTextPayload(payload) {
|
||||
if (!payload || typeof payload !== "object")
|
||||
return "Payload must be a JSON object";
|
||||
if (payload.title !== undefined && !isLocalizedStringValue(payload.title))
|
||||
return "title: Must be a localized string";
|
||||
if (!isLocalizedStringValue(payload.content))
|
||||
return "content: Must be a localized string";
|
||||
return null;
|
||||
}
|
||||
function validateScentNotesBreakdownPayload(payload) {
|
||||
if (!payload || typeof payload !== "object")
|
||||
return "Payload must be a JSON object";
|
||||
if (payload.title !== undefined && !isLocalizedStringValue(payload.title))
|
||||
return "title: Must be a localized string";
|
||||
if (payload.description !== undefined && !isLocalizedStringValue(payload.description))
|
||||
return "description: Must be a localized string";
|
||||
const listFields = ["topNotes", "heartNotes", "middleNotes", "baseNotes"];
|
||||
for (const field of listFields) {
|
||||
const value = payload[field];
|
||||
if (value !== undefined && !(isLocalizedStringValue(value) || isLocalizedStringList(value))) {
|
||||
return `${field}: Must be a localized string or localized string list`;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function validateQuoteCalloutPayload(payload) {
|
||||
if (!payload || typeof payload !== "object")
|
||||
return "Payload must be a JSON object";
|
||||
if (!isLocalizedStringValue(payload.quote))
|
||||
return "quote: Must be a localized string";
|
||||
if (payload.author !== undefined && !isLocalizedStringValue(payload.author))
|
||||
return "author: Must be a localized string";
|
||||
if (payload.role !== undefined && !isLocalizedStringValue(payload.role))
|
||||
return "role: Must be a localized string";
|
||||
return null;
|
||||
}
|
||||
function validateProductCarouselPayload(payload) {
|
||||
if (!payload || typeof payload !== "object")
|
||||
return "Payload must be a JSON object";
|
||||
if (payload.title !== undefined && !isLocalizedStringValue(payload.title))
|
||||
return "title: Must be a localized string";
|
||||
if (payload.description !== undefined && !isLocalizedStringValue(payload.description))
|
||||
return "description: Must be a localized string";
|
||||
if (payload.collectionId !== undefined && !hasValidString(payload.collectionId))
|
||||
return "collectionId: Must be a string";
|
||||
if (payload.limit !== undefined && (!hasValidNumber(payload.limit) || payload.limit <= 0))
|
||||
return "limit: Must be a positive number";
|
||||
if (payload.showPrice !== undefined && !hasValidBoolean(payload.showPrice))
|
||||
return "showPrice: Must be a boolean";
|
||||
if (payload.showAddToCart !== undefined && !hasValidBoolean(payload.showAddToCart))
|
||||
return "showAddToCart: Must be a boolean";
|
||||
return null;
|
||||
}
|
||||
function validateAccordionPayload(payload) {
|
||||
if (!payload || typeof payload !== "object")
|
||||
return "Payload must be a JSON object";
|
||||
if (payload.title !== undefined && !isLocalizedStringValue(payload.title))
|
||||
return "title: Must be a localized string";
|
||||
if (!Array.isArray(payload.categories) || payload.categories.length === 0)
|
||||
return "categories: Must be a non-empty array";
|
||||
for (const [catIndex, category] of payload.categories.entries()) {
|
||||
if (!category || typeof category !== "object")
|
||||
return `categories.${catIndex}: Must be an object`;
|
||||
if (!isLocalizedStringValue(category.name))
|
||||
return `categories.${catIndex}.name: Must be a localized string`;
|
||||
if (category.icon !== undefined && !hasValidString(category.icon))
|
||||
return `categories.${catIndex}.icon: Must be a string`;
|
||||
if (!Array.isArray(category.questions) || category.questions.length === 0) {
|
||||
return `categories.${catIndex}.questions: Must be a non-empty array`;
|
||||
}
|
||||
for (const [qIndex, question] of category.questions.entries()) {
|
||||
if (!question || typeof question !== "object")
|
||||
return `categories.${catIndex}.questions.${qIndex}: Must be an object`;
|
||||
if (!isLocalizedStringValue(question.q))
|
||||
return `categories.${catIndex}.questions.${qIndex}.q: Must be a localized string`;
|
||||
if (!isLocalizedStringValue(question.a))
|
||||
return `categories.${catIndex}.questions.${qIndex}.a: Must be a localized string`;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function validateContactInfoPayload(payload) {
|
||||
if (!payload || typeof payload !== "object")
|
||||
return "Payload must be a JSON object";
|
||||
if (payload.title !== undefined && !isLocalizedStringValue(payload.title))
|
||||
return "title: Must be a localized string";
|
||||
for (const field of ["address", "email", "line", "hours"]) {
|
||||
if (!isLocalizedStringValue(payload[field]))
|
||||
return `${field}: Must be a localized string`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function validateContactFormPayload(payload) {
|
||||
if (!payload || typeof payload !== "object")
|
||||
return "Payload must be a JSON object";
|
||||
if (payload.title !== undefined && !isLocalizedStringValue(payload.title))
|
||||
return "title: Must be a localized string";
|
||||
if (payload.subtitle !== undefined && !isLocalizedStringValue(payload.subtitle))
|
||||
return "subtitle: Must be a localized string";
|
||||
return null;
|
||||
}
|
||||
const RESERVED_SLUGS = [
|
||||
"shop", "category", "products", "cart", "checkout", "account", "search",
|
||||
"login", "admin", "wishlist", "track", "coupons", "scent-guide",
|
||||
"profile", "article", "pages", "orders", "customers",
|
||||
"settings", "menus", "auth", "api"
|
||||
];
|
||||
/** Resolve category name — mirrors the same helper in categories.ts */
|
||||
function resolveCatName(cat, lang) {
|
||||
if (lang === 'all') {
|
||||
const nameObj = {};
|
||||
cat.translations?.forEach((tr) => { nameObj[tr.langCode] = tr.name; });
|
||||
return nameObj;
|
||||
}
|
||||
const trans = cat.translations?.find((t) => t.langCode === lang)
|
||||
|| cat.translations?.find((t) => t.langCode === 'th')
|
||||
|| cat.translations?.[0];
|
||||
return trans?.name ?? cat.name;
|
||||
}
|
||||
export const pagesRoute = new Elysia({ prefix: "/pages" })
|
||||
.get("/", async () => {
|
||||
return await prisma.page.findMany({
|
||||
@@ -43,7 +194,9 @@ export const pagesRoute = new Elysia({ prefix: "/pages" })
|
||||
let products = [];
|
||||
const limit = payload.limit || 3;
|
||||
const productInclude = {
|
||||
category: true,
|
||||
category: {
|
||||
include: { translations: true },
|
||||
},
|
||||
scent: true,
|
||||
translations: true,
|
||||
variants: {
|
||||
@@ -96,6 +249,69 @@ export const pagesRoute = new Elysia({ prefix: "/pages" })
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (section.type === 'PRODUCT_CAROUSEL' && !Array.isArray(populatedPayload.products)) {
|
||||
let products = [];
|
||||
const limit = payload.limit || 3;
|
||||
const productInclude = {
|
||||
category: {
|
||||
include: { translations: true },
|
||||
},
|
||||
scent: true,
|
||||
translations: true,
|
||||
variants: {
|
||||
include: {
|
||||
bomItems: { include: { rawMaterial: true } }
|
||||
},
|
||||
orderBy: { price: "asc" }
|
||||
},
|
||||
images: {
|
||||
orderBy: { order: "asc" }
|
||||
}
|
||||
};
|
||||
if (payload.type === 'featured' || payload.collectionId === 'featured') {
|
||||
products = await prisma.product.findMany({
|
||||
where: { categoryId: 1 },
|
||||
include: productInclude,
|
||||
take: limit
|
||||
});
|
||||
}
|
||||
else if (payload.type === 'scent' && payload.scentId) {
|
||||
products = await prisma.product.findMany({
|
||||
where: { scentId: Number(payload.scentId) },
|
||||
include: productInclude,
|
||||
take: limit
|
||||
});
|
||||
}
|
||||
else if (Array.isArray(payload.productIds)) {
|
||||
const ids = payload.productIds.map(Number);
|
||||
products = await prisma.product.findMany({
|
||||
where: { id: { in: ids } },
|
||||
include: productInclude
|
||||
});
|
||||
products.sort((a, b) => ids.indexOf(a.id) - ids.indexOf(b.id));
|
||||
}
|
||||
else {
|
||||
products = await prisma.product.findMany({
|
||||
include: productInclude,
|
||||
take: limit
|
||||
});
|
||||
}
|
||||
populatedPayload.products = products.map((p) => {
|
||||
if (lang === 'all') {
|
||||
const nameObj = {};
|
||||
const descObj = {};
|
||||
const detailsObj = {};
|
||||
p.translations?.forEach((t) => {
|
||||
nameObj[t.langCode] = t.name;
|
||||
descObj[t.langCode] = t.description;
|
||||
detailsObj[t.langCode] = t.details;
|
||||
});
|
||||
return { ...p, name: nameObj, description: descObj, details: detailsObj };
|
||||
}
|
||||
const trans = p.translations?.find((t) => t.langCode === lang) || p.translations?.find((t) => t.langCode === 'th') || p.translations?.[0];
|
||||
return { ...p, name: trans?.name || p.scent?.name || '', description: trans?.description, details: trans?.details };
|
||||
});
|
||||
}
|
||||
else if (section.type === 'CATEGORIES') {
|
||||
let categories = [];
|
||||
if (Array.isArray(payload.categoryIds) && payload.categoryIds.length > 0) {
|
||||
@@ -103,6 +319,7 @@ export const pagesRoute = new Elysia({ prefix: "/pages" })
|
||||
categories = await prisma.category.findMany({
|
||||
where: { id: { in: ids } },
|
||||
include: {
|
||||
translations: true,
|
||||
_count: {
|
||||
select: { products: true }
|
||||
}
|
||||
@@ -113,13 +330,18 @@ export const pagesRoute = new Elysia({ prefix: "/pages" })
|
||||
else {
|
||||
categories = await prisma.category.findMany({
|
||||
include: {
|
||||
translations: true,
|
||||
_count: {
|
||||
select: { products: true }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
populatedPayload.categories = categories;
|
||||
// Resolve name for each category using the lang param (supports 'all')
|
||||
populatedPayload.categories = categories.map((cat) => ({
|
||||
...cat,
|
||||
name: resolveCatName(cat, lang),
|
||||
}));
|
||||
}
|
||||
return {
|
||||
...section,
|
||||
@@ -328,8 +550,17 @@ export const pagesRoute = new Elysia({ prefix: "/pages" })
|
||||
try {
|
||||
const id = Number(sectionId);
|
||||
const { type, order, payload, isActive } = body;
|
||||
if (payload && type) {
|
||||
const validationError = validateSectionPayload(type, payload);
|
||||
if (payload) {
|
||||
let validationType = type;
|
||||
if (!validationType) {
|
||||
const existingSection = await prisma.pageSection.findUnique({ where: { id } });
|
||||
if (!existingSection) {
|
||||
set.status = 404;
|
||||
return { error: "Section not found" };
|
||||
}
|
||||
validationType = existingSection.type;
|
||||
}
|
||||
const validationError = validateSectionPayload(validationType, payload);
|
||||
if (validationError) {
|
||||
set.status = 400;
|
||||
return { error: validationError };
|
||||
@@ -453,51 +684,51 @@ function validateSectionPayload(type, payload) {
|
||||
break;
|
||||
}
|
||||
case 'HERO_ARTICLE': {
|
||||
const res = HeroArticlePayloadSchema.safeParse(payload);
|
||||
if (!res.success)
|
||||
return res.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
||||
const error = validateHeroArticlePayload(payload);
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
}
|
||||
case 'RICH_TEXT': {
|
||||
const res = RichTextPayloadSchema.safeParse(payload);
|
||||
if (!res.success)
|
||||
return res.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
||||
const error = validateRichTextPayload(payload);
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
}
|
||||
case 'SCENT_NOTES_BREAKDOWN': {
|
||||
const res = ScentNotesBreakdownPayloadSchema.safeParse(payload);
|
||||
if (!res.success)
|
||||
return res.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
||||
const error = validateScentNotesBreakdownPayload(payload);
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
}
|
||||
case 'QUOTE_CALLOUT': {
|
||||
const res = QuoteCalloutPayloadSchema.safeParse(payload);
|
||||
if (!res.success)
|
||||
return res.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
||||
const error = validateQuoteCalloutPayload(payload);
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
}
|
||||
case 'PRODUCT_CAROUSEL': {
|
||||
const res = ProductCarouselPayloadSchema.safeParse(payload);
|
||||
if (!res.success)
|
||||
return res.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
||||
const error = validateProductCarouselPayload(payload);
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
}
|
||||
case 'ACCORDION': {
|
||||
const res = AccordionSectionPayloadSchema.safeParse(payload);
|
||||
if (!res.success)
|
||||
return res.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
||||
const error = validateAccordionPayload(payload);
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
}
|
||||
case 'CONTACT_INFO': {
|
||||
const res = ContactInfoSectionPayloadSchema.safeParse(payload);
|
||||
if (!res.success)
|
||||
return res.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
||||
const error = validateContactInfoPayload(payload);
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
}
|
||||
case 'CONTACT_FORM': {
|
||||
const res = ContactFormSectionPayloadSchema.safeParse(payload);
|
||||
if (!res.success)
|
||||
return res.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
||||
const error = validateContactFormPayload(payload);
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"pages.js","sourceRoot":"","sources":["pages.ts"],"names":[],"mappings":"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 truncated
|
||||
{"version":3,"file":"pages.js","sourceRoot":"","sources":["pages.ts"],"names":[],"mappings":"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 truncated
|
||||
+67
-4
@@ -16,7 +16,7 @@ export declare const productsRoute: Elysia<"/products", {
|
||||
response: {};
|
||||
}, {
|
||||
products: {
|
||||
scents: {
|
||||
brands: {
|
||||
get: {
|
||||
body: unknown;
|
||||
params: {};
|
||||
@@ -32,7 +32,7 @@ export declare const productsRoute: Elysia<"/products", {
|
||||
};
|
||||
} & {
|
||||
products: {
|
||||
scents: {
|
||||
brands: {
|
||||
":id": {
|
||||
translations: {
|
||||
patch: {
|
||||
@@ -46,10 +46,10 @@ export declare const productsRoute: Elysia<"/products", {
|
||||
200: {
|
||||
success: boolean;
|
||||
translations: any[];
|
||||
scent: any;
|
||||
brand: any;
|
||||
};
|
||||
400: {
|
||||
readonly message: "Invalid scent ID";
|
||||
readonly message: "Invalid brand ID";
|
||||
};
|
||||
422: {
|
||||
type: "validation";
|
||||
@@ -145,6 +145,69 @@ export declare const productsRoute: Elysia<"/products", {
|
||||
};
|
||||
};
|
||||
};
|
||||
} & {
|
||||
products: {
|
||||
":id": {
|
||||
images: {
|
||||
post: {
|
||||
body: {
|
||||
url: string;
|
||||
};
|
||||
params: {
|
||||
id: string;
|
||||
} & {};
|
||||
query: unknown;
|
||||
headers: unknown;
|
||||
response: {
|
||||
200: {
|
||||
success: boolean;
|
||||
image: any;
|
||||
};
|
||||
400: {
|
||||
readonly message: "Invalid product ID";
|
||||
} | {
|
||||
readonly message: "URL is required";
|
||||
};
|
||||
404: {
|
||||
readonly message: "Product not found";
|
||||
};
|
||||
422: {
|
||||
type: "validation";
|
||||
on: string;
|
||||
summary?: string;
|
||||
message?: string;
|
||||
found?: unknown;
|
||||
property?: string;
|
||||
expected?: string;
|
||||
};
|
||||
500: {
|
||||
readonly success: false;
|
||||
readonly message: "Failed to add product image";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
} & {
|
||||
products: {
|
||||
post: {
|
||||
body: any;
|
||||
params: {};
|
||||
query: unknown;
|
||||
headers: unknown;
|
||||
response: {
|
||||
200: {
|
||||
success: boolean;
|
||||
product: any;
|
||||
};
|
||||
500: {
|
||||
readonly success: false;
|
||||
readonly message: "Failed to create product";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
} & {
|
||||
products: {
|
||||
":id": {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"products.d.ts","sourceRoot":"","sources":["products.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAK,MAAM,QAAQ,CAAC;AAGnC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoUvB,CAAC"}
|
||||
{"version":3,"file":"products.d.ts","sourceRoot":"","sources":["products.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAK,MAAM,QAAQ,CAAC;AAGnC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2bvB,CAAC"}
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import prisma from "../lib/prisma";
|
||||
export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
.get("/scents", async () => {
|
||||
return await prisma.scent.findMany({
|
||||
.get("/brands", async () => {
|
||||
return await prisma.productBrand.findMany({
|
||||
orderBy: { name: "asc" },
|
||||
include: { translations: true }
|
||||
});
|
||||
})
|
||||
.patch("/scents/:id/translations", async ({ params, body, status }) => {
|
||||
.patch("/brands/:id/translations", async ({ params, body, status }) => {
|
||||
const id = parseInt(params.id);
|
||||
if (isNaN(id))
|
||||
return status(400, { message: "Invalid scent ID" });
|
||||
return status(400, { message: "Invalid brand ID" });
|
||||
const payload = body;
|
||||
const results = [];
|
||||
const LANGS = ['th', 'en', 'ja', 'zh', 'vi', 'ko'];
|
||||
@@ -18,32 +18,32 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
const name = typeof payload.name === 'string' ? (lang === 'th' ? payload.name : null) : payload.name?.[lang];
|
||||
if (!name)
|
||||
continue;
|
||||
const existing = await prisma.scentTranslation.findUnique({
|
||||
where: { scentId_langCode: { scentId: id, langCode: lang } }
|
||||
const existing = await prisma.productBrandTranslation.findUnique({
|
||||
where: { brandId_langCode: { brandId: id, langCode: lang } }
|
||||
});
|
||||
if (existing) {
|
||||
const updated = await prisma.scentTranslation.update({
|
||||
const updated = await prisma.productBrandTranslation.update({
|
||||
where: { id: existing.id },
|
||||
data: { name },
|
||||
});
|
||||
results.push(updated);
|
||||
}
|
||||
else {
|
||||
const created = await prisma.scentTranslation.create({
|
||||
data: { scentId: id, langCode: lang, name },
|
||||
const created = await prisma.productBrandTranslation.create({
|
||||
data: { brandId: id, langCode: lang, name },
|
||||
});
|
||||
results.push(created);
|
||||
}
|
||||
}
|
||||
const updated = await prisma.scent.findUnique({
|
||||
const updated = await prisma.productBrand.findUnique({
|
||||
where: { id },
|
||||
include: { translations: true },
|
||||
});
|
||||
return { success: true, translations: results, scent: updated };
|
||||
return { success: true, translations: results, brand: updated };
|
||||
})
|
||||
.get("/", async ({ query }) => {
|
||||
const categoryId = query.categoryId ? parseInt(query.categoryId) : undefined;
|
||||
const scentId = query.scentId ? parseInt(query.scentId) : undefined;
|
||||
const brandId = query.brandId ? parseInt(query.brandId) : undefined;
|
||||
const search = query.search?.trim() || undefined;
|
||||
const lang = query.lang || 'th';
|
||||
// Pagination params
|
||||
@@ -52,11 +52,11 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
const skip = (page - 1) * limit;
|
||||
const where = {
|
||||
...(categoryId && { categoryId }),
|
||||
...(scentId && { scentId }),
|
||||
...(brandId && { brandId }),
|
||||
...(search && {
|
||||
OR: [
|
||||
{ translations: { some: { name: { contains: search, mode: "insensitive" } } } },
|
||||
{ scent: { name: { contains: search, mode: "insensitive" } } },
|
||||
{ brand: { name: { contains: search, mode: "insensitive" } } },
|
||||
{ category: { name: { contains: search, mode: "insensitive" } } },
|
||||
],
|
||||
}),
|
||||
@@ -66,7 +66,7 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
where,
|
||||
include: {
|
||||
category: { include: { translations: true } },
|
||||
scent: true,
|
||||
brand: true,
|
||||
translations: true,
|
||||
variants: {
|
||||
include: {
|
||||
@@ -99,9 +99,9 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
const catTrans = p.category.translations?.find((t) => t.langCode === lang) || p.category.translations?.find((t) => t.langCode === 'th') || p.category.translations?.[0];
|
||||
return {
|
||||
...p,
|
||||
name: trans?.name || '',
|
||||
description: trans?.description,
|
||||
details: trans?.details,
|
||||
name: trans?.name || p.name || '',
|
||||
description: trans?.description || p.description,
|
||||
details: trans?.details || p.details,
|
||||
category: { ...p.category, name: catTrans?.name || p.category.name }
|
||||
};
|
||||
}
|
||||
@@ -130,7 +130,7 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
where: { id },
|
||||
include: {
|
||||
category: { include: { translations: true } },
|
||||
scent: true,
|
||||
brand: true,
|
||||
translations: true,
|
||||
variants: {
|
||||
include: {
|
||||
@@ -159,9 +159,9 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
const catTrans = product.category.translations?.find((t) => t.langCode === lang) || product.category.translations?.find((t) => t.langCode === 'th') || product.category.translations?.[0];
|
||||
return {
|
||||
...product,
|
||||
name: trans?.name || '',
|
||||
description: trans?.description,
|
||||
details: trans?.details,
|
||||
name: trans?.name || product.name || '',
|
||||
description: trans?.description || product.description,
|
||||
details: trans?.details || product.details,
|
||||
category: { ...product.category, name: catTrans?.name || product.category.name }
|
||||
};
|
||||
}
|
||||
@@ -174,9 +174,14 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
try {
|
||||
const updatedProduct = await prisma.$transaction(async (tx) => {
|
||||
// 1. Update basic product info
|
||||
const updateData = {};
|
||||
if (categoryId !== undefined)
|
||||
updateData.categoryId = categoryId;
|
||||
if (body.options !== undefined)
|
||||
updateData.options = body.options;
|
||||
const prod = await tx.product.update({
|
||||
where: { id },
|
||||
data: { categoryId },
|
||||
data: updateData,
|
||||
});
|
||||
// 2. Sync Variants
|
||||
await tx.productVariant.deleteMany({
|
||||
@@ -187,8 +192,13 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
await tx.productVariant.create({
|
||||
data: {
|
||||
productId: id,
|
||||
size: v.size,
|
||||
price: v.price,
|
||||
sku: v.sku || null,
|
||||
barcode: v.barcode || null,
|
||||
stock: v.stock || 0,
|
||||
option1: v.option1 || "",
|
||||
option2: v.option2 || "",
|
||||
option3: v.option3 || "",
|
||||
price: v.price || 0,
|
||||
bomItems: v.bomItems && v.bomItems.length > 0 ? {
|
||||
create: v.bomItems.map((b) => ({
|
||||
rawMaterialId: b.rawMaterialId,
|
||||
@@ -228,6 +238,104 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
summary: "Update Product",
|
||||
tags: ["products"],
|
||||
},
|
||||
})
|
||||
.post("/:id/images", async ({ params, body, status }) => {
|
||||
const id = parseInt(params.id);
|
||||
if (isNaN(id))
|
||||
return status(400, { message: "Invalid product ID" });
|
||||
const { url } = body;
|
||||
if (!url)
|
||||
return status(400, { message: "URL is required" });
|
||||
try {
|
||||
const product = await prisma.product.findUnique({
|
||||
where: { id },
|
||||
include: { images: true }
|
||||
});
|
||||
if (!product)
|
||||
return status(404, { message: "Product not found" });
|
||||
const newImage = await prisma.productImage.create({
|
||||
data: {
|
||||
productId: id,
|
||||
url,
|
||||
order: product.images.length
|
||||
}
|
||||
});
|
||||
return { success: true, image: newImage };
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Add product image error:", err);
|
||||
return status(500, { success: false, message: "Failed to add product image" });
|
||||
}
|
||||
}, {
|
||||
body: t.Object({
|
||||
url: t.String()
|
||||
})
|
||||
})
|
||||
.post("/", async ({ body, status }) => {
|
||||
const payload = body;
|
||||
try {
|
||||
const product = await prisma.$transaction(async (tx) => {
|
||||
const prod = await tx.product.create({
|
||||
data: {
|
||||
categoryId: payload.categoryId,
|
||||
brandId: payload.brandId,
|
||||
name: payload.name || "New Product",
|
||||
price: payload.price || 0,
|
||||
description: payload.description || "",
|
||||
details: payload.details || "",
|
||||
options: payload.options || [],
|
||||
}
|
||||
});
|
||||
await tx.productTranslation.create({
|
||||
data: {
|
||||
productId: prod.id,
|
||||
langCode: 'th',
|
||||
name: payload.name || "New Product",
|
||||
description: payload.description || "",
|
||||
details: payload.details || "",
|
||||
}
|
||||
});
|
||||
if (payload.variants && payload.variants.length > 0) {
|
||||
for (const v of payload.variants) {
|
||||
await tx.productVariant.create({
|
||||
data: {
|
||||
productId: prod.id,
|
||||
sku: v.sku || null,
|
||||
barcode: v.barcode || null,
|
||||
stock: v.stock || 0,
|
||||
option1: v.option1 || "",
|
||||
option2: v.option2 || "",
|
||||
option3: v.option3 || "",
|
||||
price: v.price || 0,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (payload.images && payload.images.length > 0) {
|
||||
for (const [idx, img] of payload.images.entries()) {
|
||||
await tx.productImage.create({
|
||||
data: {
|
||||
productId: prod.id,
|
||||
url: img.url,
|
||||
order: img.order ?? idx,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return prod;
|
||||
});
|
||||
return { success: true, product };
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Create product error:", err);
|
||||
return status(500, { success: false, message: "Failed to create product" });
|
||||
}
|
||||
}, {
|
||||
body: t.Any(),
|
||||
detail: {
|
||||
summary: "Create Product",
|
||||
tags: ["products"],
|
||||
},
|
||||
})
|
||||
.patch("/:id", async ({ params, body, status }) => {
|
||||
const id = parseInt(params.id);
|
||||
@@ -238,10 +346,12 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
const prodData = {};
|
||||
if (payload.categoryId !== undefined)
|
||||
prodData.categoryId = payload.categoryId;
|
||||
if (payload.scentId !== undefined)
|
||||
prodData.scentId = payload.scentId;
|
||||
if (payload.brandId !== undefined)
|
||||
prodData.brandId = payload.brandId;
|
||||
if (payload.price !== undefined)
|
||||
prodData.price = payload.price;
|
||||
if (payload.options !== undefined)
|
||||
prodData.options = payload.options;
|
||||
const prod = await prisma.product.update({
|
||||
where: { id },
|
||||
data: prodData,
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"products.js","sourceRoot":"","sources":["products.ts"],"names":[],"mappings":"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 truncated
|
||||
{"version":3,"file":"products.js","sourceRoot":"","sources":["products.ts"],"names":[],"mappings":"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 truncated
|
||||
@@ -2,6 +2,20 @@ import { Elysia, t } from "elysia";
|
||||
import prisma from "../lib/prisma";
|
||||
|
||||
export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
.decorate("validateVariants", (options: any[] = [], variants: any[] = []) => {
|
||||
if (options.length > 3) throw new Error("Options array length cannot exceed 3.");
|
||||
for (const v of variants) {
|
||||
if (v.option1 && options[0] && !options[0].values.includes(v.option1)) {
|
||||
throw new Error(`Variant option1 '${v.option1}' is not found in options[0].values [${options[0].values.join(', ')}]`);
|
||||
}
|
||||
if (v.option2 && options[1] && !options[1].values.includes(v.option2)) {
|
||||
throw new Error(`Variant option2 '${v.option2}' is not found in options[1].values [${options[1].values.join(', ')}]`);
|
||||
}
|
||||
if (v.option3 && options[2] && !options[2].values.includes(v.option3)) {
|
||||
throw new Error(`Variant option3 '${v.option3}' is not found in options[2].values [${options[2].values.join(', ')}]`);
|
||||
}
|
||||
}
|
||||
})
|
||||
.get("/brands", async () => {
|
||||
return await prisma.productBrand.findMany({
|
||||
orderBy: { name: "asc" },
|
||||
@@ -177,13 +191,22 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
})
|
||||
.put(
|
||||
"/:id",
|
||||
async ({ params, body, status }) => {
|
||||
async ({ params, body, status, validateVariants }) => {
|
||||
const id = parseInt(params.id);
|
||||
if (isNaN(id)) return status(400, { message: "Invalid product ID" });
|
||||
|
||||
const { categoryId, description, details, variants, images } = body as any;
|
||||
let options = (body as any).options;
|
||||
|
||||
try {
|
||||
if (variants && Array.isArray(variants)) {
|
||||
// If options is not passed but variants are, we might need to fetch existing options
|
||||
if (options === undefined) {
|
||||
const existingProduct = await prisma.product.findUnique({ where: { id } });
|
||||
options = existingProduct?.options || [];
|
||||
}
|
||||
validateVariants(options, variants);
|
||||
}
|
||||
const updatedProduct = await prisma.$transaction(async (tx) => {
|
||||
// 1. Update basic product info
|
||||
const updateData: any = {};
|
||||
@@ -196,12 +219,37 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
});
|
||||
|
||||
// 2. Sync Variants
|
||||
await tx.productVariant.deleteMany({
|
||||
where: { productId: id },
|
||||
});
|
||||
if (variants && Array.isArray(variants)) {
|
||||
const existingVariants = await tx.productVariant.findMany({ where: { productId: id } });
|
||||
const incomingIds = variants.map((v: any) => v.id).filter(Boolean);
|
||||
|
||||
// Delete variants not in incoming payload
|
||||
const variantsToDelete = existingVariants.filter(ev => !incomingIds.includes(ev.id));
|
||||
if (variantsToDelete.length > 0) {
|
||||
await tx.productVariant.deleteMany({
|
||||
where: { id: { in: variantsToDelete.map(ev => ev.id) } }
|
||||
});
|
||||
}
|
||||
|
||||
if (variants && variants.length > 0) {
|
||||
for (const v of variants) {
|
||||
if (v.id) {
|
||||
// Check if it really belongs to this product
|
||||
const exists = existingVariants.find(ev => ev.id === v.id);
|
||||
if (exists) {
|
||||
await tx.productVariant.update({
|
||||
where: { id: v.id },
|
||||
data: {
|
||||
sku: v.sku || null,
|
||||
barcode: v.barcode || null,
|
||||
stock: v.stock || 0,
|
||||
option1: v.option1 || "",
|
||||
option2: v.option2 || "",
|
||||
option3: v.option3 || "",
|
||||
price: v.price || 0,
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await tx.productVariant.create({
|
||||
data: {
|
||||
productId: id,
|
||||
@@ -222,14 +270,33 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Sync Images
|
||||
await tx.productImage.deleteMany({
|
||||
where: { productId: id },
|
||||
});
|
||||
if (images && Array.isArray(images)) {
|
||||
const existingImages = await tx.productImage.findMany({ where: { productId: id } });
|
||||
const incomingImgIds = images.map((img: any) => img.id).filter(Boolean);
|
||||
|
||||
const imagesToDelete = existingImages.filter(ei => !incomingImgIds.includes(ei.id));
|
||||
if (imagesToDelete.length > 0) {
|
||||
await tx.productImage.deleteMany({
|
||||
where: { id: { in: imagesToDelete.map(ei => ei.id) } }
|
||||
});
|
||||
}
|
||||
|
||||
if (images && images.length > 0) {
|
||||
for (const [idx, img] of images.entries()) {
|
||||
if (img.id) {
|
||||
const exists = existingImages.find(ei => ei.id === img.id);
|
||||
if (exists) {
|
||||
await tx.productImage.update({
|
||||
where: { id: img.id },
|
||||
data: {
|
||||
url: img.url,
|
||||
order: img.order ?? (typeof img.order === 'number' ? img.order : idx),
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await tx.productImage.create({
|
||||
data: {
|
||||
productId: id,
|
||||
@@ -239,6 +306,7 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return prod;
|
||||
});
|
||||
@@ -246,7 +314,7 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
return { success: true, product: updatedProduct };
|
||||
} catch (err: any) {
|
||||
console.error("Update product error:", err);
|
||||
return status(500, { success: false, message: "Failed to update product" });
|
||||
return status(500, { success: false, message: err?.message || "Failed to update product" });
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -283,7 +351,7 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
return { success: true, image: newImage };
|
||||
} catch (err: any) {
|
||||
console.error("Add product image error:", err);
|
||||
return status(500, { success: false, message: "Failed to add product image" });
|
||||
return status(500, { success: false, message: err?.message || "Failed to add product image" });
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -294,10 +362,14 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
)
|
||||
.post(
|
||||
"/",
|
||||
async ({ body, status }) => {
|
||||
async ({ body, status, validateVariants }) => {
|
||||
const payload = body as any;
|
||||
try {
|
||||
const product = await prisma.product.create({
|
||||
if (payload.variants && Array.isArray(payload.variants)) {
|
||||
validateVariants(payload.options || [], payload.variants);
|
||||
}
|
||||
const product = await prisma.$transaction(async (tx) => {
|
||||
const prod = await tx.product.create({
|
||||
data: {
|
||||
categoryId: payload.categoryId,
|
||||
brandId: payload.brandId,
|
||||
@@ -309,10 +381,9 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
}
|
||||
});
|
||||
|
||||
// Also create the TH translation by default
|
||||
await prisma.productTranslation.create({
|
||||
await tx.productTranslation.create({
|
||||
data: {
|
||||
productId: product.id,
|
||||
productId: prod.id,
|
||||
langCode: 'th',
|
||||
name: payload.name || "New Product",
|
||||
description: payload.description || "",
|
||||
@@ -320,10 +391,42 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
}
|
||||
});
|
||||
|
||||
if (payload.variants && payload.variants.length > 0) {
|
||||
for (const v of payload.variants) {
|
||||
await tx.productVariant.create({
|
||||
data: {
|
||||
productId: prod.id,
|
||||
sku: v.sku || null,
|
||||
barcode: v.barcode || null,
|
||||
stock: v.stock || 0,
|
||||
option1: v.option1 || "",
|
||||
option2: v.option2 || "",
|
||||
option3: v.option3 || "",
|
||||
price: v.price || 0,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.images && payload.images.length > 0) {
|
||||
for (const [idx, img] of payload.images.entries()) {
|
||||
await tx.productImage.create({
|
||||
data: {
|
||||
productId: prod.id,
|
||||
url: img.url,
|
||||
order: img.order ?? idx,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return prod;
|
||||
});
|
||||
|
||||
return { success: true, product };
|
||||
} catch (err: any) {
|
||||
console.error("Create product error:", err);
|
||||
return status(500, { success: false, message: "Failed to create product" });
|
||||
return status(500, { success: false, message: err?.message || "Failed to create product" });
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -402,7 +505,7 @@ export const productsRoute = new Elysia({ prefix: "/products" })
|
||||
return { success: true, product: prod };
|
||||
} catch (err: any) {
|
||||
console.error("Patch product error:", err);
|
||||
return status(500, { success: false, message: "Failed to patch product" });
|
||||
return status(500, { success: false, message: err?.message || "Failed to patch product" });
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAK,MAAM,QAAQ,CAAC;AAInC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6GtB,CAAC"}
|
||||
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAK,MAAM,QAAQ,CAAC;AAInC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiHtB,CAAC"}
|
||||
@@ -41,11 +41,15 @@ export const settingsRoute = new Elysia({ prefix: "/settings" })
|
||||
}
|
||||
}, {
|
||||
body: t.Object({
|
||||
brandName: t.String(),
|
||||
shippingFee: t.Number(),
|
||||
bankName: t.String(),
|
||||
bankAccountName: t.String(),
|
||||
bankAccountNumber: t.String(),
|
||||
pointsEarnRate: t.Number(),
|
||||
logoUrl: t.Optional(t.String()),
|
||||
themePreset: t.Optional(t.String()),
|
||||
themeTokens: t.Optional(t.Any()),
|
||||
facebookUrl: t.Optional(t.String()),
|
||||
instagramUrl: t.Optional(t.String()),
|
||||
tiktokUrl: t.Optional(t.String()),
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.js","sourceRoot":"","sources":["settings.ts"],"names":[],"mappings":"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 truncated
|
||||
{"version":3,"file":"settings.js","sourceRoot":"","sources":["settings.ts"],"names":[],"mappings":"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 truncated
|
||||
@@ -1 +1 @@
|
||||
{"root":["./check_cat.ts","./prisma.config.ts","./prisma/seed-pages.ts","./scripts/migrate-i18n.ts","./scripts/migrate-pages.ts","./scripts/migrate-production.ts","./src/index.ts","./src/lib/lineClient.ts","./src/lib/notifications.ts","./src/lib/prisma.ts","./src/middleware/auth.ts","./src/routes/articles.ts","./src/routes/auth.ts","./src/routes/categories.ts","./src/routes/coupons.ts","./src/routes/customers.ts","./src/routes/hero.ts","./src/routes/lazadaOrder.ts","./src/routes/line.ts","./src/routes/media.ts","./src/routes/menus.ts","./src/routes/newsletter.ts","./src/routes/orders.ts","./src/routes/pages.ts","./src/routes/payments.ts","./src/routes/products.ts","./src/routes/profile.ts","./src/routes/promotions.ts","./src/routes/raw-materials.ts","./src/routes/reviews.ts","./src/routes/settings.ts","./src/routes/shopee.ts","./src/routes/tiktok.ts","./src/routes/tiktokOrder.ts","./src/routes/upload.ts"],"errors":true,"version":"6.0.3"}
|
||||
{"root":["./check_cat.ts","./prisma.config.ts","./prisma/seed-pages.ts","./scripts/fix-menus.ts","./scripts/fix-scent-guide.ts","./scripts/migrate-i18n.ts","./scripts/migrate-pages.ts","./scripts/migrate-production.ts","./src/index.ts","./src/lib/lineClient.ts","./src/lib/notifications.ts","./src/lib/prisma.ts","./src/middleware/auth.ts","./src/routes/articles.ts","./src/routes/auth.ts","./src/routes/categories.ts","./src/routes/contacts.ts","./src/routes/coupons.ts","./src/routes/customers.ts","./src/routes/hero.d.ts","./src/routes/lazadaOrder.ts","./src/routes/line.ts","./src/routes/media.ts","./src/routes/menus.ts","./src/routes/newsletter.ts","./src/routes/orders.ts","./src/routes/pages.ts","./src/routes/payments.ts","./src/routes/products.ts","./src/routes/profile.ts","./src/routes/promotions.ts","./src/routes/raw-materials.ts","./src/routes/reviews.ts","./src/routes/settings.ts","./src/routes/shopee.ts","./src/routes/tiktok.ts","./src/routes/tiktokOrder.ts","./src/routes/upload.ts"],"errors":true,"version":"6.0.3"}
|
||||
@@ -253,20 +253,88 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
},
|
||||
{
|
||||
name: "create_product",
|
||||
description: "Create a new product. Minimum required fields are categoryId and brandId.",
|
||||
description: "Create a new product, optionally including its options and variant matrix. Minimum required fields are categoryId and brandId.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
categoryId: { type: "number" },
|
||||
brandId: { type: "number" },
|
||||
name: { type: "string" },
|
||||
price: { type: "number" },
|
||||
description: { type: "string" },
|
||||
details: { type: "string" }
|
||||
details: { type: "string" },
|
||||
price: { type: "number", description: "Base price if no variants are provided" },
|
||||
options: {
|
||||
type: "array",
|
||||
description: "Array of option definitions (max 3). E.g., [{'name': 'Size', 'values': ['S', 'M']}]",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
values: { type: "array", items: { type: "string" } }
|
||||
},
|
||||
required: ["name", "values"]
|
||||
}
|
||||
},
|
||||
variants: {
|
||||
type: "array",
|
||||
description: "List of variants. 'option1' maps to options[0].values, 'option2' to options[1].values, etc.",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
sku: { type: "string" },
|
||||
barcode: { type: "string" },
|
||||
price: { type: "number" },
|
||||
stock: { type: "number", default: 0 },
|
||||
option1: { type: "string" },
|
||||
option2: { type: "string" },
|
||||
option3: { type: "string" }
|
||||
},
|
||||
required: ["option1"]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["categoryId", "brandId"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "sync_product_variants",
|
||||
description: "Overwrite and synchronize the options and variant matrix for an existing product. The provided variants will replace or update existing ones based on option combinations.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
productId: { type: "number" },
|
||||
options: {
|
||||
type: "array",
|
||||
description: "The new or updated options array (max 3).",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
values: { type: "array", items: { type: "string" } }
|
||||
},
|
||||
required: ["name", "values"]
|
||||
}
|
||||
},
|
||||
variants: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "number", description: "Include if updating an existing variant" },
|
||||
sku: { type: "string" },
|
||||
price: { type: "number" },
|
||||
stock: { type: "number" },
|
||||
option1: { type: "string" },
|
||||
option2: { type: "string" },
|
||||
option3: { type: "string" }
|
||||
},
|
||||
required: ["option1"]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["productId", "options", "variants"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "update_product_content",
|
||||
description: "Update product basic content for the default/Thai translation. Note: If the Thai translation record does not exist yet, the 'name' field is strictly required, otherwise the backend will silently drop the update.",
|
||||
@@ -517,6 +585,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const res = await api.post(`/products`, params);
|
||||
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
|
||||
}
|
||||
case "sync_product_variants": {
|
||||
const payload = {
|
||||
options: params.options,
|
||||
variants: params.variants
|
||||
};
|
||||
const res = await api.put(`/products/${params.productId}`, payload);
|
||||
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
|
||||
}
|
||||
case "update_category_translation": {
|
||||
const { id, lang, name } = params as any;
|
||||
const res = await api.patch(`/categories/${id}/translations`, {
|
||||
|
||||
@@ -76,8 +76,8 @@ export default function CartDrawer() {
|
||||
|
||||
<div className="flex-1 flex flex-col pt-1 space-y-1 relative min-w-0">
|
||||
<h3 className="font-bold text-sm text-pop-black leading-tight pr-6 truncate">{item.productName}</h3>
|
||||
{item.variantSize && (
|
||||
<p className="text-xs text-pop-black/60">{item.variantSize}</p>
|
||||
{item.variantName && (
|
||||
<p className="text-xs text-pop-black/60">{item.variantName}</p>
|
||||
)}
|
||||
|
||||
{/* Price row */}
|
||||
|
||||
@@ -131,7 +131,7 @@ export default function ProductCard({ product, enableQuickShop = false, category
|
||||
quantity: 1,
|
||||
price: selectedVariant.price || 0,
|
||||
productName: displayName,
|
||||
variantSize: [selectedVariant.option1, selectedVariant.option2, selectedVariant.option3].filter(Boolean).join(' / '),
|
||||
variantName: [selectedVariant.option1, selectedVariant.option2, selectedVariant.option3].filter(Boolean).join(' / '),
|
||||
productImage: product.images?.[0]?.url || getCategoryImage(displayCategoryName)
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -293,7 +293,7 @@ export default function CheckoutPage() {
|
||||
quantity: item.quantity,
|
||||
priceAtPurchase: item.price,
|
||||
productName: item.productName,
|
||||
variantSize: item.variantSize,
|
||||
variantName: item.variantName,
|
||||
productImage: item.productImage
|
||||
}))
|
||||
}
|
||||
@@ -542,7 +542,7 @@ export default function CheckoutPage() {
|
||||
<div key={item.cartItemId} className="flex justify-between items-start gap-4 text-sm text-pop-black">
|
||||
<div className="flex-1">
|
||||
<p className="font-bold">{item.productName}</p>
|
||||
<p className="text-spa-stone/80 text-xs mt-0.5">{item.variantSize} x {item.quantity}</p>
|
||||
<p className="text-spa-stone/80 text-xs mt-0.5">{item.variantName} x {item.quantity}</p>
|
||||
</div>
|
||||
<p className="font-bold text-rose-500">{formatPrice(item.price * item.quantity)}</p>
|
||||
</div>
|
||||
|
||||
@@ -268,7 +268,7 @@ export default function OrderSuccessPage() {
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-bold text-pop-black">{item.productName}</p>
|
||||
<p className="text-spa-stone/80 text-xs mt-0.5">{item.variantSize} x {item.quantity}</p>
|
||||
<p className="text-spa-stone/80 text-xs mt-0.5">{item.variantName} x {item.quantity}</p>
|
||||
</div>
|
||||
<p className="font-bold text-rose-500">{formatPrice(item.priceAtPurchase * item.quantity)}</p>
|
||||
</div>
|
||||
|
||||
@@ -134,7 +134,7 @@ export default function ProductDetailPage() {
|
||||
quantity: 1,
|
||||
price: currentVariant.price || product.price || 0,
|
||||
productName: displayName,
|
||||
variantSize: [currentVariant.option1, currentVariant.option2, currentVariant.option3].filter(Boolean).join(' / '),
|
||||
variantName: [currentVariant.option1, currentVariant.option2, currentVariant.option3].filter(Boolean).join(' / '),
|
||||
productImage: product.images?.[0]?.url || getCategoryImage(typeof product.category.name === 'string' ? product.category.name : (product.category.name as any)?.en || '')
|
||||
})
|
||||
}
|
||||
|
||||
@@ -508,7 +508,7 @@ export default function ProfilePage() {
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-bold text-pop-black truncate">{item.productName}</p>
|
||||
<p className="text-[10px] text-spa-stone/80 mt-0.5">{item.variantSize} x {item.quantity}</p>
|
||||
<p className="text-[10px] text-spa-stone/80 mt-0.5">{item.variantName} x {item.quantity}</p>
|
||||
</div>
|
||||
<p className="font-bold text-rose-500 shrink-0">{formatPrice(item.priceAtPurchase * item.quantity)}</p>
|
||||
</div>
|
||||
|
||||
@@ -139,7 +139,7 @@ export default async function OrderStatusPage({ params }: { params: Promise<{ id
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="font-semibold text-text-main">{item.productName}</p>
|
||||
<p className="text-sm text-muted-text">
|
||||
{item.variantSize ? `${item.variantSize} · ` : ''}x {item.quantity}
|
||||
{item.variantName ? `${item.variantName} · ` : ''}x {item.quantity}
|
||||
</p>
|
||||
</div>
|
||||
<p className="font-bold text-brand-primary">{formatPrice(item.priceAtPurchase * item.quantity)}</p>
|
||||
|
||||
@@ -74,7 +74,7 @@ export default function AddToCartSection({ product }: Props) {
|
||||
quantity: 1,
|
||||
price: currentVariant.price || product.price || 0,
|
||||
productName,
|
||||
variantSize: variantName || undefined,
|
||||
variantName: variantName || undefined,
|
||||
productImage: product.images?.[0]?.url || product.category.imageUrl || getCategoryImage(categoryName),
|
||||
})
|
||||
toggleCart(true)
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function CartDrawer() {
|
||||
|
||||
<div className="relative flex flex-1 flex-col space-y-1 pt-1">
|
||||
<h3 className="pr-6 text-sm font-bold leading-tight text-text-main">{item.productName}</h3>
|
||||
{item.variantSize && <p className="text-xs text-muted-text">{item.variantSize}</p>}
|
||||
{item.variantName && <p className="text-xs text-muted-text">{item.variantName}</p>}
|
||||
<p className="mt-auto text-sm font-bold text-brand-primary">{formatPrice(item.price)}</p>
|
||||
|
||||
<div className="mt-2 flex w-max items-center gap-3 rounded-lg border border-line-primary/20 bg-surface-card">
|
||||
|
||||
@@ -98,7 +98,7 @@ function toOrderItem(item: CartItem) {
|
||||
quantity: item.quantity,
|
||||
priceAtPurchase: item.price,
|
||||
productName: item.productName,
|
||||
variantSize: item.variantSize ?? undefined,
|
||||
variantName: item.variantName ?? undefined,
|
||||
productImage: item.productImage ?? undefined,
|
||||
}
|
||||
}
|
||||
@@ -620,7 +620,7 @@ export default function CheckoutClient() {
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-bold text-text-main">{item.productName}</p>
|
||||
<p className="mt-1 text-xs text-muted-text">{item.variantSize || 'Standard'} x {item.quantity}</p>
|
||||
<p className="mt-1 text-xs text-muted-text">{item.variantName || 'Standard'} x {item.quantity}</p>
|
||||
</div>
|
||||
<p className="text-sm font-bold text-brand-primary">{formatPrice(item.price * item.quantity)}</p>
|
||||
</div>
|
||||
|
||||
@@ -114,7 +114,7 @@ export default function ProductCard({ product }: ProductCardProps) {
|
||||
quantity: 1,
|
||||
price: selectedVariant.price || 0,
|
||||
productName: productName,
|
||||
variantSize: [selectedVariant.option1, selectedVariant.option2, selectedVariant.option3].filter(Boolean).join(' / '),
|
||||
variantName: [selectedVariant.option1, selectedVariant.option2, selectedVariant.option3].filter(Boolean).join(' / '),
|
||||
productImage: imageUrl
|
||||
})
|
||||
}}
|
||||
|
||||
@@ -408,7 +408,7 @@ export default function ProfileClient() {
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-semibold text-text-main">{item.productName}</p>
|
||||
<p className="text-xs text-muted-text">{item.variantSize ? `${item.variantSize} · ` : ''}x {item.quantity}</p>
|
||||
<p className="text-xs text-muted-text">{item.variantName ? `${item.variantName} · ` : ''}x {item.quantity}</p>
|
||||
</div>
|
||||
<p className="font-bold text-brand-primary">{formatPrice(item.priceAtPurchase * item.quantity)}</p>
|
||||
</div>
|
||||
|
||||
@@ -169,7 +169,7 @@ export interface OrderItem {
|
||||
quantity: number
|
||||
priceAtPurchase: number
|
||||
productName: string
|
||||
variantSize?: string | null
|
||||
variantName?: string | null
|
||||
productImage?: string | null
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ export interface CartItem {
|
||||
quantity: number
|
||||
price: number
|
||||
productName: string
|
||||
variantSize?: string | null
|
||||
variantName?: string | null
|
||||
productImage?: string | null
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user