This commit is contained in:
Your Name committed 2026-06-24 19:15:04 -04:00
1 parent 81ed9af406
commit 4536db6741
56 files changed
+1127 -198

No files matched your search

+1
View File
@@ -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 />} />
+1 -1
View File
@@ -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>
+44 -26
View File
@@ -88,29 +88,37 @@ 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`)
])
if (!prodRes.ok) throw new Error('Product not found')
const prodData: Product = await prodRes.json()
const catRes = await fetch(`${API_BASE}/categories`)
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 || '' }
setName(parseTranslatable(prodData.name))
setDescription(parseTranslatable(prodData.description))
setDetails(parseTranslatable(prodData.details))
const parseOptions = (val: any) => Array.isArray(val) ? val : []
setOptions(parseOptions(prodData.options))
setVariants(prodData.variants || [])
setImages(prodData.images || [])
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()
setProduct(prodData)
setCategoryId(prodData.categoryId)
const parseTranslatable = (val: any) => typeof val === 'object' && val !== null ? val : { th: val || '' }
setName(parseTranslatable(prodData.name))
setDescription(parseTranslatable(prodData.description))
setDetails(parseTranslatable(prodData.details))
const parseOptions = (val: any) => Array.isArray(val) ? val : []
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)
setTimeout(() => setSuccess(false), 3000)
if (isNew) {
setTimeout(() => navigate('/products'), 1500)
} else {
setTimeout(() => setSuccess(false), 3000)
}
} catch (err: any) {
setError(err.message)
} finally {
+6 -1
View File
@@ -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>
@@ -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;
+1 -1
View File
@@ -273,7 +273,7 @@ model OrderItem {
quantity Int
priceAtPurchase Float
productName String
variantSize String?
variantName String?
productImage String?
}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=fix-menus.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"fix-menus.d.ts","sourceRoot":"","sources":["fix-menus.ts"],"names":[],"mappings":""}
+46
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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":""}
+53
View File
@@ -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"}
+5 -5
View File
@@ -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" ||
@@ -60,7 +60,7 @@ const app = new Elysia()
return text;
}
})
.use(cors({
.use(cors({
origin: [
...CORS_ORIGIN.split(",").map((o) => o.trim()),
...(isDevelopment ? [allowLanDevOrigin] : []),
@@ -114,8 +114,8 @@ 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`);
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAClE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,uBAAuB,CAAC;AACvE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,4BAA4B,CAAC;AAE1E,MAAM,GAAG,GAAG,IAAI,MAAM,EAAE;KACrB,OAAO,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IAC7B,OAAO,CAAC,GAAG,CAAC,iCAAiC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,qDAAqD,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAChF,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;KACD,GAAG,CACF,IAAI,CAAC;IACH,MAAM,EAAE;QACN,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,6BAA6B;QAC7B,2BAA2B,EAAE,aAAa;QAC1C,uBAAuB,EAAO,eAAe;QAC7C,uBAAuB,EAAE,QAAQ;QACjC,uBAAuB;QACvB,uBAAuB,EAAE,cAAc;QACvC,gCAAgC;QAChC,iCAAiC;KAClC;IACD,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC;IAC7D,WAAW,EAAE,IAAI;CAClB,CAAC,CACH;KACA,GAAG,CACF,GAAG,CAAC;IACF,IAAI,EAAE,KAAK;IACX,MAAM,EAAE,UAAU;IAClB,GAAG,EAAE,KAAK;CACX,CAAC,CACH;KACA,GAAG,CACF,OAAO,CAAC;IACN,aAAa,EAAE;QACb,IAAI,EAAE;YACJ,KAAK,EAAE,cAAc;YACrB,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,2CAA2C;SACzD;QACD,IAAI,EAAE;YACJ,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE;YACzD,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,uBAAuB,EAAE;YAC1D,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B,EAAE;SAC1D;KACF;CACF,CAAC,CACH;KACA,GAAG,CAAC,YAAY,CAAC;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,EAAE,EAAE,4BAA4B;CACzC,CAAC,CAAC;KACF,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACf,IAAI,EAAE,cAAc;IACpB,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,UAAU;CACjB,CAAC,CAAC;IACH,8EAA8E;KAC7E,GAAG,CAAC,oBAAoB,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;IAC3D,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,kBAAkB,QAAQ,EAAE,CAAC,CAAC;IACpD,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACpB,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;QACjB,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;KACD,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CACrB,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAC9b;KACA,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;AAE/C,OAAO,CAAC,GAAG,CACT,wCAAwC,GAAG,CAAC,MAAM,EAAE,QAAQ,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,CACnF,CAAC;AACF,OAAO,CAAC,GAAG,CACT,2BAA2B,GAAG,CAAC,MAAM,EAAE,QAAQ,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,UAAU,CAC9E,CAAC"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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 truncated
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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,
+2 -2
View File
@@ -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
View File
@@ -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"}
+56
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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"}
+13 -3
View File
@@ -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
View File
@@ -1 +1 @@
{"version":3,"file":"menus.js","sourceRoot":"","sources":["menus.ts"],"names":[],"mappings":"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 truncated
{"version":3,"file":"menus.js","sourceRoot":"","sources":["menus.ts"],"names":[],"mappings":"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 truncated
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -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())
}))
})
+2 -2
View File
@@ -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
View File
@@ -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"}
+261 -30
View File
@@ -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:
@@ -505,4 +736,4 @@ function validateSectionPayload(type, payload) {
}
return null;
}
//# sourceMappingURL=pages.js.map
//# sourceMappingURL=pages.js.map
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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"}
+137 -27
View File
@@ -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
View File
@@ -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
+162 -59
View File
@@ -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,48 +219,93 @@ export const productsRoute = new Elysia({ prefix: "/products" })
});
// 2. Sync Variants
await tx.productVariant.deleteMany({
where: { productId: id },
});
if (variants && variants.length > 0) {
for (const v of variants) {
await tx.productVariant.create({
data: {
productId: 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,
bomItems: v.bomItems && v.bomItems.length > 0 ? {
create: v.bomItems.map((b: any) => ({
rawMaterialId: b.rawMaterialId,
quantity: b.quantity,
}))
} : undefined
}
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) } }
});
}
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,
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: any) => ({
rawMaterialId: b.rawMaterialId,
quantity: b.quantity,
}))
} : undefined
}
});
}
}
}
// 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);
if (images && images.length > 0) {
for (const [idx, img] of images.entries()) {
await tx.productImage.create({
data: {
productId: id,
url: img.url,
order: img.order ?? (typeof img.order === 'number' ? img.order : idx),
}
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) } }
});
}
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,
url: img.url,
order: img.order ?? (typeof img.order === 'number' ? img.order : idx),
}
});
}
}
}
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,36 +362,71 @@ 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({
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 || [],
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,
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,
}
});
}
}
});
// Also create the TH translation by default
await prisma.productTranslation.create({
data: {
productId: product.id,
langCode: 'th',
name: payload.name || "New Product",
description: payload.description || "",
details: payload.details || "",
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
View File
@@ -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"}
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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"}
+79 -3
View File
@@ -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`, {
+2 -2
View File
@@ -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 */}
+1 -1
View File
@@ -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)
});
}}
+2 -2
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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 || '')
})
}
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>