@cimplify/sdk 0.38.1 → 0.38.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dispatcher.mjs +1 -1
- package/dist/mock/cli.mjs +995 -308
- package/dist/mock/library.d.mts +2 -2
- package/dist/mock/library.d.ts +2 -2
- package/dist/mock/library.js +995 -308
- package/dist/mock/library.mjs +995 -308
- package/dist/mock/msw.d.mts +1 -1
- package/dist/mock/msw.d.ts +1 -1
- package/dist/mock/msw.js +995 -308
- package/dist/mock/msw.mjs +995 -308
- package/dist/react.js +12 -2
- package/dist/react.mjs +12 -2
- package/dist/{server-B8TeE2OD.d.mts → server-BNy6Ri-4.d.mts} +18 -0
- package/dist/{server-B8TeE2OD.d.ts → server-BNy6Ri-4.d.ts} +18 -0
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/templates/storefront/.env.example +11 -2
- package/templates/storefront/README.md +35 -18
- package/templates/storefront/app/cart/page.tsx +9 -0
- package/templates/storefront/app/categories/[slug]/page.tsx +80 -0
- package/templates/storefront/app/checkout/page.tsx +17 -0
- package/templates/storefront/app/collections/[slug]/page.tsx +80 -0
- package/templates/storefront/app/globals.css +47 -0
- package/templates/storefront/app/layout.tsx +45 -0
- package/templates/storefront/app/orders/[id]/page.tsx +21 -0
- package/templates/storefront/app/page.tsx +65 -0
- package/templates/storefront/app/shop/page.tsx +46 -0
- package/templates/storefront/components/category-grid.tsx +28 -0
- package/templates/storefront/components/collection-strip.tsx +45 -0
- package/templates/storefront/components/footer.tsx +19 -0
- package/templates/storefront/components/header.tsx +41 -0
- package/templates/storefront/components/hero.tsx +25 -0
- package/templates/storefront/components/product-modal.tsx +105 -0
- package/templates/storefront/components/providers.tsx +31 -0
- package/templates/storefront/components/store-product-card.tsx +48 -0
- package/templates/storefront/lib/cart.ts +27 -0
- package/templates/storefront/next.config.ts +36 -0
- package/templates/storefront/package.json +13 -14
- package/templates/storefront/postcss.config.mjs +7 -0
- package/templates/storefront/tsconfig.json +15 -11
- package/templates/storefront/index.html +0 -12
- package/templates/storefront/src/App.tsx +0 -118
- package/templates/storefront/src/main.tsx +0 -28
- package/templates/storefront/src/styles.css +0 -65
- package/templates/storefront/vite.config.ts +0 -8
package/dist/mock/cli.mjs
CHANGED
|
@@ -3618,6 +3618,178 @@ function buildElements(deps) {
|
|
|
3618
3618
|
return app;
|
|
3619
3619
|
}
|
|
3620
3620
|
|
|
3621
|
+
// src/mock/routes/images.ts
|
|
3622
|
+
var SLUG_KEYWORDS = {
|
|
3623
|
+
// ─── Akua's Bakery (default seed) ───
|
|
3624
|
+
sourdough: "sourdough,bread",
|
|
3625
|
+
wheat: "whole-wheat,bread",
|
|
3626
|
+
baguette: "baguette",
|
|
3627
|
+
focaccia: "focaccia,bread",
|
|
3628
|
+
croissant: "croissant",
|
|
3629
|
+
pac: "pain-au-chocolat,croissant",
|
|
3630
|
+
danish: "danish,pastry",
|
|
3631
|
+
cinnamon: "cinnamon-roll",
|
|
3632
|
+
"choc-cake": "chocolate-cake,slice",
|
|
3633
|
+
"red-velvet": "red-velvet-cake",
|
|
3634
|
+
carrot: "carrot-cake",
|
|
3635
|
+
cheesecake: "cheesecake",
|
|
3636
|
+
coffee: "coffee,cup",
|
|
3637
|
+
tea: "tea,cup",
|
|
3638
|
+
sobolo: "hibiscus-drink",
|
|
3639
|
+
oj: "orange-juice,glass",
|
|
3640
|
+
banana: "banana-bread",
|
|
3641
|
+
brownie: "brownie",
|
|
3642
|
+
macaron: "macaron,box",
|
|
3643
|
+
birthday: "birthday-cake",
|
|
3644
|
+
"breakfast-box": "breakfast",
|
|
3645
|
+
"pastry-box": "pastry,assortment",
|
|
3646
|
+
// ─── Mama's Kitchen (restaurant seed) ───
|
|
3647
|
+
kelewele: "fried-plantain,spicy",
|
|
3648
|
+
"spring-rolls": "spring-rolls",
|
|
3649
|
+
"soup-of-the-day": "soup,bowl",
|
|
3650
|
+
"jollof-rice": "jollof-rice",
|
|
3651
|
+
"banku-tilapia": "tilapia,grilled-fish",
|
|
3652
|
+
"fufu-light-soup": "fufu,soup",
|
|
3653
|
+
waakye: "rice,beans,african-food",
|
|
3654
|
+
"grilled-chicken": "grilled-chicken",
|
|
3655
|
+
burger: "burger",
|
|
3656
|
+
"yam-fries": "yam-fries,chips",
|
|
3657
|
+
"fried-plantain": "fried-plantain",
|
|
3658
|
+
salad: "garden-salad",
|
|
3659
|
+
"ice-cream": "ice-cream,scoop",
|
|
3660
|
+
"cake-slice": "chocolate-cake,slice",
|
|
3661
|
+
"fruit-bowl": "fruit-bowl,tropical",
|
|
3662
|
+
"soft-drink": "cola,glass",
|
|
3663
|
+
"fresh-juice": "fresh-juice",
|
|
3664
|
+
water: "water-bottle",
|
|
3665
|
+
feast: "family-meal,buffet",
|
|
3666
|
+
byo: "buffet,plate",
|
|
3667
|
+
// ─── Currents Electronics ───
|
|
3668
|
+
"android-flagship": "smartphone,android",
|
|
3669
|
+
"android-mid": "smartphone",
|
|
3670
|
+
"phone-budget": "phone",
|
|
3671
|
+
"laptop-pro": "laptop,macbook",
|
|
3672
|
+
"laptop-air": "laptop,thin",
|
|
3673
|
+
"laptop-budget": "laptop",
|
|
3674
|
+
"headphones-anc": "headphones",
|
|
3675
|
+
"earbuds-pro": "earbuds,wireless",
|
|
3676
|
+
"speaker-bt": "speaker,bluetooth",
|
|
3677
|
+
"phone-case": "phone-case",
|
|
3678
|
+
"screen-protector": "screen,protector",
|
|
3679
|
+
"fast-charger": "charger,usb",
|
|
3680
|
+
"laptop-sleeve": "laptop-sleeve,case",
|
|
3681
|
+
"wireless-mouse": "wireless-mouse",
|
|
3682
|
+
"mech-keyboard": "mechanical-keyboard",
|
|
3683
|
+
"gaming-controller": "game-controller",
|
|
3684
|
+
"gaming-headset": "gaming-headset",
|
|
3685
|
+
"console-handheld": "handheld-console,gaming",
|
|
3686
|
+
wfh: "desk-setup,laptop",
|
|
3687
|
+
"game-pack": "gaming,console",
|
|
3688
|
+
// ─── Reesa apparel ───
|
|
3689
|
+
"ankara-shirt": "ankara,african-fashion",
|
|
3690
|
+
"agbada-set": "agbada,nigerian-fashion",
|
|
3691
|
+
kaftan: "kaftan,african-dress",
|
|
3692
|
+
"leather-tote": "leather-tote,bag",
|
|
3693
|
+
sandals: "sandals,leather",
|
|
3694
|
+
"beaded-bracelet": "beaded-bracelet",
|
|
3695
|
+
owambe: "african-fashion",
|
|
3696
|
+
// ─── FreshMart ───
|
|
3697
|
+
"tomatoes-1kg": "tomatoes",
|
|
3698
|
+
"onions-1kg": "onions",
|
|
3699
|
+
"ginger-200g": "ginger,root",
|
|
3700
|
+
"bananas-bunch": "bananas",
|
|
3701
|
+
pineapple: "pineapple",
|
|
3702
|
+
"spinach-bunch": "spinach,leaves",
|
|
3703
|
+
"milk-1l": "milk,carton",
|
|
3704
|
+
"yogurt-500g": "yogurt",
|
|
3705
|
+
"eggs-12": "eggs,carton",
|
|
3706
|
+
"cheese-200g": "cheese",
|
|
3707
|
+
"chicken-1kg": "raw-chicken",
|
|
3708
|
+
"beef-mince-500g": "beef,mince",
|
|
3709
|
+
"tilapia-each": "fish,tilapia",
|
|
3710
|
+
"rice-5kg": "rice,bag",
|
|
3711
|
+
"garri-2kg": "rice,grain",
|
|
3712
|
+
"palm-oil-1l": "oil,bottle",
|
|
3713
|
+
"vegetable-oil-1l": "oil,bottle",
|
|
3714
|
+
"salt-1kg": "salt,seasoning",
|
|
3715
|
+
"sugar-1kg": "sugar,white",
|
|
3716
|
+
"tomato-paste": "tomato-paste,can",
|
|
3717
|
+
"soap-bar": "soap,bar",
|
|
3718
|
+
"detergent-1kg": "detergent,laundry",
|
|
3719
|
+
"tissue-pack": "tissue,paper",
|
|
3720
|
+
"trash-bags": "trash-bag",
|
|
3721
|
+
essentials: "groceries,box",
|
|
3722
|
+
// ─── Serene Spa ───
|
|
3723
|
+
"swedish-massage-60": "massage,relaxation",
|
|
3724
|
+
"swedish-massage-90": "massage,relaxation",
|
|
3725
|
+
"deep-tissue-60": "massage,deep-tissue",
|
|
3726
|
+
"couples-massage": "couples-massage",
|
|
3727
|
+
"hot-stone-90": "hot-stone,massage",
|
|
3728
|
+
"facial-classic": "facial,spa",
|
|
3729
|
+
"facial-anti-aging": "facial,skincare",
|
|
3730
|
+
"facial-acne": "facial,skincare",
|
|
3731
|
+
manicure: "manicure,nails",
|
|
3732
|
+
pedicure: "pedicure,feet",
|
|
3733
|
+
"gel-manicure": "manicure,gel-nails",
|
|
3734
|
+
haircut: "haircut,salon",
|
|
3735
|
+
"hair-treatment": "hair-treatment,salon",
|
|
3736
|
+
color: "hair-color,salon",
|
|
3737
|
+
"yoga-session": "yoga,studio",
|
|
3738
|
+
meditation: "meditation,zen",
|
|
3739
|
+
"spa-day": "spa,wellness",
|
|
3740
|
+
"couples-retreat": "spa,couple",
|
|
3741
|
+
"custom-experience": "spa,wellness"
|
|
3742
|
+
};
|
|
3743
|
+
var NO_CACHE = {
|
|
3744
|
+
"Cache-Control": "no-cache, no-store, must-revalidate"
|
|
3745
|
+
};
|
|
3746
|
+
function lockFromSlug(slug) {
|
|
3747
|
+
let hash = 0;
|
|
3748
|
+
for (let i = 0; i < slug.length; i++) hash = hash * 31 + slug.charCodeAt(i) >>> 0;
|
|
3749
|
+
return hash % 9999 + 1;
|
|
3750
|
+
}
|
|
3751
|
+
function svgFallback(slug) {
|
|
3752
|
+
const label = slug.replace(/[-_]+/g, " ").trim() || "product";
|
|
3753
|
+
const display = label.length > 20 ? label.slice(0, 20) + "\u2026" : label;
|
|
3754
|
+
let hash = 0;
|
|
3755
|
+
for (let i = 0; i < slug.length; i++) hash = hash * 31 + slug.charCodeAt(i) >>> 0;
|
|
3756
|
+
const hue = hash % 360;
|
|
3757
|
+
const safe = display.replace(/[<>&]/g, "");
|
|
3758
|
+
const svg = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3759
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" width="400" height="400">
|
|
3760
|
+
<defs>
|
|
3761
|
+
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
|
3762
|
+
<stop offset="0%" stop-color="hsl(${hue}, 65%, 65%)"/>
|
|
3763
|
+
<stop offset="100%" stop-color="hsl(${(hue + 40) % 360}, 70%, 45%)"/>
|
|
3764
|
+
</linearGradient>
|
|
3765
|
+
</defs>
|
|
3766
|
+
<rect width="400" height="400" fill="url(#g)"/>
|
|
3767
|
+
<text x="200" y="210" font-family="-apple-system, BlinkMacSystemFont, Inter, sans-serif" font-size="22" font-weight="600" fill="white" text-anchor="middle" dominant-baseline="middle" style="text-transform: capitalize">${safe}</text>
|
|
3768
|
+
</svg>`;
|
|
3769
|
+
return new Response(svg, {
|
|
3770
|
+
headers: {
|
|
3771
|
+
"Content-Type": "image/svg+xml; charset=utf-8",
|
|
3772
|
+
...NO_CACHE,
|
|
3773
|
+
"Access-Control-Allow-Origin": "*"
|
|
3774
|
+
}
|
|
3775
|
+
});
|
|
3776
|
+
}
|
|
3777
|
+
function handleImageRequest(c) {
|
|
3778
|
+
const path = c.req.path.replace(/^\/img\//, "").replace(/\.[a-z0-9]+$/i, "");
|
|
3779
|
+
const slug = path.split("/").pop() ?? "";
|
|
3780
|
+
for (const [k, v] of Object.entries(NO_CACHE)) c.header(k, v);
|
|
3781
|
+
if (!slug) return svgFallback("");
|
|
3782
|
+
const keyword = SLUG_KEYWORDS[slug] ?? slug.replace(/[-_]+/g, ",");
|
|
3783
|
+
const lock = lockFromSlug(slug);
|
|
3784
|
+
return c.redirect(
|
|
3785
|
+
`https://loremflickr.com/600/600/${encodeURIComponent(keyword)}?lock=${lock}`,
|
|
3786
|
+
302
|
|
3787
|
+
);
|
|
3788
|
+
}
|
|
3789
|
+
function mountImageRoutes(app) {
|
|
3790
|
+
app.get("/img/*", handleImageRequest);
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3621
3793
|
// src/mock/core/clock.ts
|
|
3622
3794
|
function createClock(opts) {
|
|
3623
3795
|
let frozenAt = opts?.frozenAt ? opts.frozenAt.getTime() : null;
|
|
@@ -3955,36 +4127,249 @@ function percent(a, pct) {
|
|
|
3955
4127
|
return toMajor(Math.round(toMinor(a) * pct / 100));
|
|
3956
4128
|
}
|
|
3957
4129
|
|
|
3958
|
-
// src/mock/domain/catalogue.ts
|
|
3959
|
-
function
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
4130
|
+
// src/mock/domain/catalogue/price-info.ts
|
|
4131
|
+
function defaultPriceInfo(price, currency) {
|
|
4132
|
+
return {
|
|
4133
|
+
base_price: price,
|
|
4134
|
+
final_price: price,
|
|
4135
|
+
pre_tax_price: price,
|
|
4136
|
+
markup_percentage: "0.00",
|
|
4137
|
+
markup_amount: "0.00",
|
|
4138
|
+
markup_discount_percentage: "0.00",
|
|
4139
|
+
markup_discount_amount: "0.00",
|
|
4140
|
+
currency,
|
|
4141
|
+
tax_info: {
|
|
4142
|
+
tax_rate: "0",
|
|
4143
|
+
tax_amount: "0.00",
|
|
4144
|
+
is_inclusive: true,
|
|
4145
|
+
pre_tax_price: price,
|
|
4146
|
+
components: []
|
|
4147
|
+
}
|
|
4148
|
+
};
|
|
4149
|
+
}
|
|
4150
|
+
|
|
4151
|
+
// src/mock/domain/catalogue/product-shape.ts
|
|
4152
|
+
function toCanonicalProductSummary(p) {
|
|
4153
|
+
return {
|
|
4154
|
+
id: p.id,
|
|
4155
|
+
business_id: p.business_id,
|
|
4156
|
+
category_id: p.category_ids[0],
|
|
4157
|
+
name: p.name,
|
|
4158
|
+
slug: p.slug,
|
|
4159
|
+
description: p.description,
|
|
4160
|
+
image_url: p.image,
|
|
4161
|
+
images: p.images,
|
|
4162
|
+
default_price: p.base_price,
|
|
4163
|
+
type: p.product_type,
|
|
4164
|
+
inventory_type: "none",
|
|
4165
|
+
variant_strategy: "fetch_all",
|
|
4166
|
+
is_active: p.is_available,
|
|
4167
|
+
inventory_status: { in_stock: p.is_available, stock_level: 50, low_stock: false },
|
|
4168
|
+
tags: p.tags ?? [],
|
|
4169
|
+
created_at: p.created_at,
|
|
4170
|
+
updated_at: p.updated_at
|
|
4171
|
+
};
|
|
4172
|
+
}
|
|
4173
|
+
function deriveRenderHint(p) {
|
|
4174
|
+
if (p.product_type === "service") return "general";
|
|
4175
|
+
const foodish = (p.tags ?? []).some(
|
|
4176
|
+
(t) => /food|drink|cake|bread|pastry|dessert|coffee|tea|juice|spice/i.test(t)
|
|
4177
|
+
);
|
|
4178
|
+
return foodish ? "food" : "general";
|
|
4179
|
+
}
|
|
4180
|
+
function toApiProduct(p, ctx) {
|
|
4181
|
+
const { registry, priceInfo, groups, components, composite, bundle } = ctx;
|
|
4182
|
+
return {
|
|
4183
|
+
// ─── Canonical Product fields (SDK type: Product / ProductWithDetails) ───
|
|
3963
4184
|
id: p.id,
|
|
3964
4185
|
business_id: p.business_id,
|
|
4186
|
+
category_id: p.category_ids[0],
|
|
3965
4187
|
name: p.name,
|
|
3966
4188
|
slug: p.slug,
|
|
3967
4189
|
description: p.description ?? "",
|
|
3968
|
-
|
|
4190
|
+
image_url: p.image,
|
|
4191
|
+
images: p.images,
|
|
4192
|
+
default_price: p.base_price,
|
|
4193
|
+
type: p.product_type,
|
|
4194
|
+
render_hint: deriveRenderHint(p),
|
|
4195
|
+
inventory_type: "none",
|
|
4196
|
+
variant_strategy: p.variant_ids.length > 0 ? "use_axes" : "fetch_all",
|
|
4197
|
+
is_active: p.is_available,
|
|
4198
|
+
inventory_status: { in_stock: p.is_available, stock_level: 50, low_stock: false },
|
|
4199
|
+
tags: p.tags ?? [],
|
|
4200
|
+
calories: p.calories,
|
|
4201
|
+
allergies: p.allergies ?? [],
|
|
4202
|
+
created_at: p.created_at,
|
|
4203
|
+
updated_at: p.updated_at,
|
|
4204
|
+
metadata: {
|
|
4205
|
+
is_signature: p.is_signature ?? false,
|
|
4206
|
+
is_new: p.is_new ?? false,
|
|
4207
|
+
ingredients: p.ingredients ?? [],
|
|
4208
|
+
pairings: p.pairings ?? [],
|
|
4209
|
+
...p.metadata ?? {}
|
|
4210
|
+
},
|
|
4211
|
+
// ─── Pricing breakdown (Price / ProductCustomizer) ───
|
|
3969
4212
|
base_price: p.base_price,
|
|
4213
|
+
default_price_info: priceInfo,
|
|
4214
|
+
price_info: priceInfo,
|
|
3970
4215
|
currency: p.currency,
|
|
4216
|
+
// ─── Compatibility aliases (older callers) ───
|
|
3971
4217
|
image: p.image,
|
|
3972
|
-
|
|
4218
|
+
product_type: p.product_type,
|
|
3973
4219
|
is_available: p.is_available,
|
|
3974
4220
|
category_ids: p.category_ids,
|
|
3975
4221
|
collection_ids: p.collection_ids,
|
|
3976
4222
|
add_on_ids: p.add_on_ids,
|
|
3977
|
-
|
|
3978
|
-
created_at: p.created_at,
|
|
3979
|
-
updated_at: p.updated_at,
|
|
4223
|
+
variant_ids: p.variant_ids,
|
|
3980
4224
|
custom_attributes: [],
|
|
3981
4225
|
properties: [],
|
|
3982
4226
|
has_variants: p.variant_ids.length > 0,
|
|
3983
|
-
|
|
3984
|
-
|
|
4227
|
+
// ─── Embedded relations (ProductWithDetails) ───
|
|
4228
|
+
category: registry.categories.get(p.category_ids[0]),
|
|
4229
|
+
add_ons: p.add_on_ids.map((aid) => registry.addOns.get(aid)).filter(Boolean),
|
|
4230
|
+
variants: registry.variants.list((v) => v.product_id === p.id),
|
|
4231
|
+
variant_axes: registry.variantAxes.list((a) => a.product_id === p.id),
|
|
4232
|
+
// ─── Composite-specific (CompositeProductLayout reads product.groups) ───
|
|
4233
|
+
groups,
|
|
4234
|
+
composite_id: composite?.id,
|
|
4235
|
+
pricing_mode: composite?.pricing_mode,
|
|
4236
|
+
min_order_quantity: composite?.min_order_quantity,
|
|
4237
|
+
max_order_quantity: composite?.max_order_quantity,
|
|
4238
|
+
// ─── Bundle-specific (BundleProductLayout reads product.components) ───
|
|
4239
|
+
bundle_id: bundle?.id,
|
|
4240
|
+
bundle_pricing_type: bundle?.pricing_type,
|
|
4241
|
+
bundle_price: bundle?.bundle_price,
|
|
4242
|
+
bundle_discount_value: bundle?.discount_value,
|
|
4243
|
+
components
|
|
4244
|
+
};
|
|
4245
|
+
}
|
|
4246
|
+
|
|
4247
|
+
// src/mock/domain/catalogue/composite-shape.ts
|
|
4248
|
+
function expandCompositeGroups(registry, productId, isoNow) {
|
|
4249
|
+
const composite = registry.composites.list((c) => c.product_id === productId)[0];
|
|
4250
|
+
if (!composite) return void 0;
|
|
4251
|
+
return composite.groups.map((g, gIdx) => buildGroup(registry, composite, g, gIdx, isoNow));
|
|
4252
|
+
}
|
|
4253
|
+
function buildGroup(registry, composite, group, groupIdx, isoNow) {
|
|
4254
|
+
return {
|
|
4255
|
+
// ─── Canonical ComponentGroupWithComponents ───
|
|
4256
|
+
id: group.id,
|
|
4257
|
+
composite_id: composite.id,
|
|
4258
|
+
name: group.name,
|
|
4259
|
+
description: void 0,
|
|
4260
|
+
display_order: groupIdx,
|
|
4261
|
+
min_selections: group.min,
|
|
4262
|
+
max_selections: group.max,
|
|
4263
|
+
allow_quantity: true,
|
|
4264
|
+
max_quantity_per_component: group.max,
|
|
4265
|
+
pricing_behavior: "additive",
|
|
4266
|
+
created_at: isoNow,
|
|
4267
|
+
updated_at: isoNow,
|
|
4268
|
+
// ─── Aliases for older selectors ───
|
|
4269
|
+
min: group.min,
|
|
4270
|
+
max: group.max,
|
|
4271
|
+
components: group.component_ids.map((cid, idx) => ({ p: registry.products.get(cid), idx })).filter((x) => Boolean(x.p)).map(({ p, idx }) => buildComponent(composite, group, p, idx))
|
|
4272
|
+
};
|
|
4273
|
+
}
|
|
4274
|
+
function buildComponent(composite, group, p, idx) {
|
|
4275
|
+
return {
|
|
4276
|
+
// ─── Canonical CompositeComponent ───
|
|
4277
|
+
id: `comp_${composite.product_id}_${p.id}`,
|
|
4278
|
+
composite_id: composite.id,
|
|
4279
|
+
group_id: group.id,
|
|
4280
|
+
source_type: "product",
|
|
4281
|
+
source_product_id: p.id,
|
|
4282
|
+
product_id: p.id,
|
|
4283
|
+
display_name: p.name,
|
|
4284
|
+
display_description: p.description,
|
|
4285
|
+
display_image_url: p.image,
|
|
4286
|
+
price: p.base_price,
|
|
4287
|
+
calories: p.calories,
|
|
4288
|
+
allergens: p.allergies ?? [],
|
|
4289
|
+
display_order: idx,
|
|
4290
|
+
is_popular: idx === 0,
|
|
4291
|
+
is_premium: false,
|
|
4292
|
+
is_available: p.is_available,
|
|
4293
|
+
is_archived: false,
|
|
4294
|
+
created_at: p.created_at,
|
|
4295
|
+
updated_at: p.updated_at,
|
|
4296
|
+
// ─── Compatibility aliases ───
|
|
4297
|
+
name: p.name,
|
|
4298
|
+
description: p.description,
|
|
4299
|
+
image_url: p.image,
|
|
4300
|
+
unit_price: p.base_price,
|
|
4301
|
+
default_price: p.base_price,
|
|
4302
|
+
base_price: p.base_price,
|
|
4303
|
+
currency: p.currency,
|
|
4304
|
+
product: toCanonicalProductSummary(p)
|
|
4305
|
+
};
|
|
4306
|
+
}
|
|
4307
|
+
|
|
4308
|
+
// src/mock/domain/catalogue/bundle-shape.ts
|
|
4309
|
+
function expandBundleComponents(registry, productId) {
|
|
4310
|
+
const bundle = registry.bundles.list((b) => b.product_id === productId)[0];
|
|
4311
|
+
if (!bundle) return void 0;
|
|
4312
|
+
return bundle.product_ids.map((cid) => registry.products.get(cid)).filter((p) => Boolean(p)).map((p) => buildBundleComponent(bundle.id, productId, p));
|
|
4313
|
+
}
|
|
4314
|
+
function buildBundleComponent(bundleId, productId, p) {
|
|
4315
|
+
return {
|
|
4316
|
+
id: `bp_${productId}_${p.id}`,
|
|
4317
|
+
bundle_id: bundleId,
|
|
4318
|
+
product_id: p.id,
|
|
4319
|
+
// ─── Fields BundleProductLayout reads ───
|
|
4320
|
+
product_name: p.name,
|
|
4321
|
+
product_image_url: p.image,
|
|
4322
|
+
product_description: p.description,
|
|
4323
|
+
product_slug: p.slug,
|
|
4324
|
+
effective_price: p.base_price,
|
|
4325
|
+
unit_price: p.base_price,
|
|
4326
|
+
default_price: p.base_price,
|
|
4327
|
+
quantity: 1,
|
|
4328
|
+
allow_variant_choice: false,
|
|
4329
|
+
available_variants: [],
|
|
4330
|
+
selected_variant_id: null,
|
|
4331
|
+
// ─── Compatibility aliases ───
|
|
4332
|
+
display_name: p.name,
|
|
4333
|
+
display_description: p.description,
|
|
4334
|
+
name: p.name,
|
|
4335
|
+
description: p.description,
|
|
4336
|
+
image_url: p.image,
|
|
4337
|
+
currency: p.currency,
|
|
4338
|
+
is_available: p.is_available,
|
|
4339
|
+
product: toCanonicalProductSummary(p)
|
|
4340
|
+
};
|
|
4341
|
+
}
|
|
4342
|
+
|
|
4343
|
+
// src/mock/domain/catalogue/index.ts
|
|
4344
|
+
function createCatalogueService(opts) {
|
|
4345
|
+
const { registry, clock, ids } = opts;
|
|
4346
|
+
const productsForBusiness = (businessId) => registry.products.list((p) => p.business_id === businessId);
|
|
3985
4347
|
const variantsForProduct = (productId) => registry.variants.list((v) => v.product_id === productId);
|
|
3986
4348
|
const axesForProduct = (productId) => registry.variantAxes.list((a) => a.product_id === productId);
|
|
3987
4349
|
const addOnLinksForProduct = (productId) => registry.productAddOns.list((l) => l.product_id === productId);
|
|
4350
|
+
const apiProduct = (p) => {
|
|
4351
|
+
const composite = p.product_type === "composite" ? registry.composites.list((c) => c.product_id === p.id)[0] : void 0;
|
|
4352
|
+
const bundle = p.product_type === "bundle" ? registry.bundles.list((b) => b.product_id === p.id)[0] : void 0;
|
|
4353
|
+
return toApiProduct(p, {
|
|
4354
|
+
registry,
|
|
4355
|
+
priceInfo: defaultPriceInfo(p.base_price, p.currency),
|
|
4356
|
+
groups: expandCompositeGroups(registry, p.id, clock.iso()),
|
|
4357
|
+
components: expandBundleComponents(registry, p.id),
|
|
4358
|
+
composite: composite ? {
|
|
4359
|
+
id: composite.id,
|
|
4360
|
+
pricing_mode: composite.pricing_mode,
|
|
4361
|
+
min_order_quantity: composite.min_order_quantity,
|
|
4362
|
+
max_order_quantity: composite.max_order_quantity
|
|
4363
|
+
} : void 0,
|
|
4364
|
+
bundle: bundle ? {
|
|
4365
|
+
id: bundle.id,
|
|
4366
|
+
pricing_type: bundle.pricing_type,
|
|
4367
|
+
bundle_price: bundle.bundle_price,
|
|
4368
|
+
discount_value: bundle.discount_value
|
|
4369
|
+
} : void 0
|
|
4370
|
+
});
|
|
4371
|
+
};
|
|
4372
|
+
const resolveProduct = (idOrSlug) => registry.products.get(idOrSlug) ?? registry.products.list((p) => p.slug === idOrSlug)[0];
|
|
3988
4373
|
return {
|
|
3989
4374
|
snapshot(businessId) {
|
|
3990
4375
|
const products = productsForBusiness(businessId);
|
|
@@ -3992,7 +4377,7 @@ function createCatalogueService(opts) {
|
|
|
3992
4377
|
const addOns = registry.addOns.list((a) => a.business_id === businessId);
|
|
3993
4378
|
return {
|
|
3994
4379
|
categories,
|
|
3995
|
-
products: products.map(
|
|
4380
|
+
products: products.map(apiProduct),
|
|
3996
4381
|
add_ons: addOns,
|
|
3997
4382
|
is_complete: true,
|
|
3998
4383
|
total_available: products.length
|
|
@@ -4007,7 +4392,7 @@ function createCatalogueService(opts) {
|
|
|
4007
4392
|
const start = (page - 1) * limit;
|
|
4008
4393
|
const slice = items.slice(start, start + limit);
|
|
4009
4394
|
return {
|
|
4010
|
-
items: slice.map(
|
|
4395
|
+
items: slice.map(apiProduct),
|
|
4011
4396
|
is_complete: start + limit >= items.length,
|
|
4012
4397
|
total_available: items.length,
|
|
4013
4398
|
pagination: {
|
|
@@ -4020,9 +4405,9 @@ function createCatalogueService(opts) {
|
|
|
4020
4405
|
};
|
|
4021
4406
|
},
|
|
4022
4407
|
getProduct(productId) {
|
|
4023
|
-
const p =
|
|
4408
|
+
const p = resolveProduct(productId);
|
|
4024
4409
|
if (!p) throw notFound(`Product ${productId} not found`);
|
|
4025
|
-
return
|
|
4410
|
+
return apiProduct(p);
|
|
4026
4411
|
},
|
|
4027
4412
|
getProductVariants(productId) {
|
|
4028
4413
|
return variantsForProduct(productId);
|
|
@@ -4032,7 +4417,9 @@ function createCatalogueService(opts) {
|
|
|
4032
4417
|
},
|
|
4033
4418
|
getProductVariantById(productId, variantId) {
|
|
4034
4419
|
const v = registry.variants.get(variantId);
|
|
4035
|
-
if (!v || v.product_id !== productId)
|
|
4420
|
+
if (!v || v.product_id !== productId) {
|
|
4421
|
+
throw notFound(`Variant ${variantId} not found on product ${productId}`);
|
|
4422
|
+
}
|
|
4036
4423
|
return v;
|
|
4037
4424
|
},
|
|
4038
4425
|
findProductVariant(productId, properties) {
|
|
@@ -4062,13 +4449,13 @@ function createCatalogueService(opts) {
|
|
|
4062
4449
|
return registry.categories.list((c) => c.business_id === businessId);
|
|
4063
4450
|
},
|
|
4064
4451
|
getCategory(id) {
|
|
4065
|
-
const c = registry.categories.get(id);
|
|
4452
|
+
const c = registry.categories.get(id) ?? registry.categories.list((x) => x.slug === id)[0];
|
|
4066
4453
|
if (!c) throw notFound(`Category ${id} not found`);
|
|
4067
4454
|
return c;
|
|
4068
4455
|
},
|
|
4069
4456
|
getCategoryProducts(id) {
|
|
4070
4457
|
const c = this.getCategory(id);
|
|
4071
|
-
return c.product_ids.map((pid) => registry.products.get(pid)).filter((p) => Boolean(p)).map(
|
|
4458
|
+
return c.product_ids.map((pid) => registry.products.get(pid)).filter((p) => Boolean(p)).map(apiProduct);
|
|
4072
4459
|
},
|
|
4073
4460
|
getCategoryAttributes(id) {
|
|
4074
4461
|
const c = registry.categories.get(id);
|
|
@@ -4081,13 +4468,13 @@ function createCatalogueService(opts) {
|
|
|
4081
4468
|
return registry.collections.list((c) => c.business_id === businessId);
|
|
4082
4469
|
},
|
|
4083
4470
|
getCollection(id) {
|
|
4084
|
-
const c = registry.collections.get(id);
|
|
4471
|
+
const c = registry.collections.get(id) ?? registry.collections.list((x) => x.slug === id)[0];
|
|
4085
4472
|
if (!c) throw notFound(`Collection ${id} not found`);
|
|
4086
4473
|
return c;
|
|
4087
4474
|
},
|
|
4088
4475
|
getCollectionProducts(id) {
|
|
4089
4476
|
const c = this.getCollection(id);
|
|
4090
|
-
return c.product_ids.map((pid) => registry.products.get(pid)).filter((p) => Boolean(p)).map(
|
|
4477
|
+
return c.product_ids.map((pid) => registry.products.get(pid)).filter((p) => Boolean(p)).map(apiProduct);
|
|
4091
4478
|
},
|
|
4092
4479
|
getCollectionAttributes(id) {
|
|
4093
4480
|
const c = registry.collections.get(id);
|
|
@@ -4100,7 +4487,7 @@ function createCatalogueService(opts) {
|
|
|
4100
4487
|
return registry.bundles.list((b) => b.business_id === businessId);
|
|
4101
4488
|
},
|
|
4102
4489
|
getBundle(id) {
|
|
4103
|
-
const b = registry.bundles.get(id);
|
|
4490
|
+
const b = registry.bundles.get(id) ?? registry.bundles.list((x) => x.product_id === id || x.slug === id)[0];
|
|
4104
4491
|
if (!b) throw notFound(`Bundle ${id} not found`);
|
|
4105
4492
|
return b;
|
|
4106
4493
|
},
|
|
@@ -4108,12 +4495,16 @@ function createCatalogueService(opts) {
|
|
|
4108
4495
|
return registry.composites.list((c) => c.business_id === businessId);
|
|
4109
4496
|
},
|
|
4110
4497
|
getComposite(id) {
|
|
4111
|
-
const c = registry.composites.get(id);
|
|
4498
|
+
const c = registry.composites.get(id) ?? registry.composites.list((x) => x.product_id === id)[0];
|
|
4112
4499
|
if (!c) throw notFound(`Composite ${id} not found`);
|
|
4113
4500
|
return c;
|
|
4114
4501
|
},
|
|
4115
4502
|
getCompositeByProduct(productId) {
|
|
4116
|
-
|
|
4503
|
+
const direct = registry.composites.list((c) => c.product_id === productId)[0];
|
|
4504
|
+
if (direct) return direct;
|
|
4505
|
+
const product = registry.products.list((p) => p.slug === productId)[0];
|
|
4506
|
+
if (!product) return null;
|
|
4507
|
+
return registry.composites.list((c) => c.product_id === product.id)[0] ?? null;
|
|
4117
4508
|
},
|
|
4118
4509
|
calculateCompositePrice(id, body) {
|
|
4119
4510
|
const c = registry.composites.get(id);
|
|
@@ -4121,14 +4512,20 @@ function createCatalogueService(opts) {
|
|
|
4121
4512
|
const sel = body.selections ?? [];
|
|
4122
4513
|
let total = c.base_price;
|
|
4123
4514
|
for (const s of sel) total = add(total, money(parseFloat(s.unit_price ?? "0") * (s.quantity ?? 1)));
|
|
4124
|
-
return {
|
|
4515
|
+
return {
|
|
4516
|
+
final_price: total,
|
|
4517
|
+
base_price: c.base_price,
|
|
4518
|
+
components_total: money(parseFloat(total) - parseFloat(c.base_price))
|
|
4519
|
+
};
|
|
4125
4520
|
},
|
|
4126
4521
|
listTaxonomies(businessId) {
|
|
4127
4522
|
return registry.taxonomies.list((t) => t.business_id === businessId);
|
|
4128
4523
|
},
|
|
4129
4524
|
searchTaxonomies(businessId, q) {
|
|
4130
4525
|
const term = q.toLowerCase();
|
|
4131
|
-
return registry.taxonomies.list(
|
|
4526
|
+
return registry.taxonomies.list(
|
|
4527
|
+
(t) => t.business_id === businessId && t.name.toLowerCase().includes(term)
|
|
4528
|
+
);
|
|
4132
4529
|
},
|
|
4133
4530
|
getTaxonomy(id) {
|
|
4134
4531
|
const t = registry.taxonomies.get(id);
|
|
@@ -4162,7 +4559,11 @@ function createCatalogueService(opts) {
|
|
|
4162
4559
|
listPropertyFacets(businessId) {
|
|
4163
4560
|
const products = productsForBusiness(businessId);
|
|
4164
4561
|
const byCategory = /* @__PURE__ */ new Map();
|
|
4165
|
-
for (const p of products)
|
|
4562
|
+
for (const p of products) {
|
|
4563
|
+
for (const cid of p.category_ids) {
|
|
4564
|
+
byCategory.set(cid, (byCategory.get(cid) ?? 0) + 1);
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4166
4567
|
return [
|
|
4167
4568
|
{
|
|
4168
4569
|
name: "Category",
|
|
@@ -4181,12 +4582,6 @@ function createCatalogueService(opts) {
|
|
|
4181
4582
|
listOnSale() {
|
|
4182
4583
|
return [];
|
|
4183
4584
|
},
|
|
4184
|
-
validateDiscount(code) {
|
|
4185
|
-
if (code === "WELCOME10") {
|
|
4186
|
-
return { valid: true, discount_id: "disc_welcome10", discount_value: "10.00", discount_type: "percentage" };
|
|
4187
|
-
}
|
|
4188
|
-
return { valid: false, reason: "Code not recognized" };
|
|
4189
|
-
},
|
|
4190
4585
|
getProductDeals() {
|
|
4191
4586
|
return [];
|
|
4192
4587
|
},
|
|
@@ -4196,15 +4591,33 @@ function createCatalogueService(opts) {
|
|
|
4196
4591
|
getCollectionDeals() {
|
|
4197
4592
|
return [];
|
|
4198
4593
|
},
|
|
4594
|
+
validateDiscount(code) {
|
|
4595
|
+
if (code === "WELCOME10") {
|
|
4596
|
+
return { valid: true, discount_id: "disc_welcome10", discount_value: "10.00", discount_type: "percentage" };
|
|
4597
|
+
}
|
|
4598
|
+
return { valid: false, reason: "Code not recognized" };
|
|
4599
|
+
},
|
|
4199
4600
|
createQuote(businessId, body) {
|
|
4200
4601
|
const b = body;
|
|
4201
4602
|
const id = ids.quote();
|
|
4603
|
+
const productId = b.product_id ?? "";
|
|
4604
|
+
const product = resolveProduct(productId);
|
|
4605
|
+
const price = b.total_price ?? product?.base_price ?? "0.00";
|
|
4606
|
+
const currency = b.currency ?? product?.currency ?? "GHS";
|
|
4607
|
+
const priceInfo = defaultPriceInfo(price, currency);
|
|
4202
4608
|
const quote = {
|
|
4203
4609
|
id,
|
|
4610
|
+
quote_id: id,
|
|
4204
4611
|
business_id: businessId,
|
|
4205
|
-
product_id:
|
|
4612
|
+
product_id: product?.id ?? productId,
|
|
4206
4613
|
status: "fresh",
|
|
4207
|
-
total_price:
|
|
4614
|
+
total_price: price,
|
|
4615
|
+
currency,
|
|
4616
|
+
unit_price_info: priceInfo,
|
|
4617
|
+
final_price_info: priceInfo,
|
|
4618
|
+
quoted_total_price_info: priceInfo,
|
|
4619
|
+
components: [],
|
|
4620
|
+
breakdown: { base_price: price, components_total: "0.00", final_price: price },
|
|
4208
4621
|
expires_at: new Date(clock.nowMs() + 10 * 60 * 1e3).toISOString(),
|
|
4209
4622
|
created_at: clock.iso()
|
|
4210
4623
|
};
|
|
@@ -4220,7 +4633,11 @@ function createCatalogueService(opts) {
|
|
|
4220
4633
|
refreshQuote(id) {
|
|
4221
4634
|
const q = registry.quotes.get(id);
|
|
4222
4635
|
if (!q) throw notFound(`Quote ${id} not found`);
|
|
4223
|
-
const refreshed = {
|
|
4636
|
+
const refreshed = {
|
|
4637
|
+
...q,
|
|
4638
|
+
status: "fresh",
|
|
4639
|
+
expires_at: new Date(clock.nowMs() + 10 * 60 * 1e3).toISOString()
|
|
4640
|
+
};
|
|
4224
4641
|
registry.quotes.put(id, refreshed);
|
|
4225
4642
|
return refreshed;
|
|
4226
4643
|
}
|
|
@@ -5245,67 +5662,82 @@ function createLiteService(opts) {
|
|
|
5245
5662
|
|
|
5246
5663
|
// src/mock/seeds/default.ts
|
|
5247
5664
|
var ISO = "2026-01-01T00:00:00.000Z";
|
|
5248
|
-
var
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
{
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5665
|
+
var BUSINESS_ID = "bus_default_akua_bakery";
|
|
5666
|
+
var img = (id) => `https://images.unsplash.com/photo-${id}?w=800&h=800&fit=crop&auto=format&q=80`;
|
|
5667
|
+
function makeProduct(input, categoryIds) {
|
|
5668
|
+
const catId = categoryIds.get(input.category);
|
|
5669
|
+
return {
|
|
5670
|
+
id: input.id,
|
|
5671
|
+
business_id: BUSINESS_ID,
|
|
5672
|
+
name: input.name,
|
|
5673
|
+
slug: input.slug,
|
|
5674
|
+
description: input.description,
|
|
5675
|
+
product_type: input.productType ?? "product",
|
|
5676
|
+
base_price: input.price,
|
|
5677
|
+
currency: "GHS",
|
|
5678
|
+
image: input.imageUrl,
|
|
5679
|
+
images: input.images ?? [input.imageUrl],
|
|
5680
|
+
is_available: true,
|
|
5681
|
+
category_ids: [catId],
|
|
5682
|
+
collection_ids: [],
|
|
5683
|
+
add_on_ids: [],
|
|
5684
|
+
variant_ids: [],
|
|
5685
|
+
tags: input.tags ?? [],
|
|
5686
|
+
calories: input.calories,
|
|
5687
|
+
allergies: input.allergies ?? [],
|
|
5688
|
+
ingredients: input.ingredients ?? [],
|
|
5689
|
+
pairings: input.pairings ?? [],
|
|
5690
|
+
is_signature: input.isSignature ?? false,
|
|
5691
|
+
is_new: input.isNew ?? false,
|
|
5692
|
+
created_at: ISO,
|
|
5693
|
+
updated_at: ISO
|
|
5694
|
+
};
|
|
5695
|
+
}
|
|
5276
5696
|
function seedDefault(registry) {
|
|
5277
|
-
const businessId = "bus_default_akua_bakery";
|
|
5278
5697
|
const business = {
|
|
5279
|
-
id:
|
|
5698
|
+
id: BUSINESS_ID,
|
|
5280
5699
|
name: "Akua's Bakery",
|
|
5281
5700
|
handle: "akua",
|
|
5282
5701
|
business_type: "eatery",
|
|
5283
5702
|
email: "hello@akuasbakery.test",
|
|
5284
5703
|
default_currency: "GHS",
|
|
5285
|
-
default_phone: "+
|
|
5704
|
+
default_phone: "+233 244 000 000",
|
|
5286
5705
|
default_address: "12 Independence Ave, Accra",
|
|
5287
5706
|
default_offers_table_service: true,
|
|
5288
5707
|
default_accepts_online_orders: true,
|
|
5289
|
-
image: "
|
|
5708
|
+
image: img("1568254183919-78a4f43a2877"),
|
|
5290
5709
|
status: "active",
|
|
5291
5710
|
created_at: ISO,
|
|
5292
5711
|
updated_at: ISO,
|
|
5293
5712
|
created_by: "seed",
|
|
5294
|
-
preferences: {
|
|
5713
|
+
preferences: {
|
|
5714
|
+
tagline: "Fresh from the oven, every day",
|
|
5715
|
+
description: "Family-run bakery in East Legon. Sourdough, croissants, layered cakes \u2014 handcrafted since 2018, delivered across Accra.",
|
|
5716
|
+
instagram: "https://instagram.com/akuasbakery",
|
|
5717
|
+
theme: "warm",
|
|
5718
|
+
checkout: { allow_guest: true }
|
|
5719
|
+
},
|
|
5295
5720
|
is_online_only: false,
|
|
5296
5721
|
enabled_payment_types: ["mobile_money", "card", "cash"],
|
|
5297
5722
|
default_location_settings: {},
|
|
5298
5723
|
country_code: "GH",
|
|
5299
5724
|
timezone: "Africa/Accra"
|
|
5300
5725
|
};
|
|
5301
|
-
registry.businesses.put(
|
|
5302
|
-
const
|
|
5303
|
-
|
|
5726
|
+
registry.businesses.put(BUSINESS_ID, business);
|
|
5727
|
+
const categoryDefs = [
|
|
5728
|
+
{ slug: "breads", name: "Breads", description: "Naturally leavened sourdough, baguettes, focaccia." },
|
|
5729
|
+
{ slug: "pastries", name: "Pastries", description: "Hand-laminated croissants, danishes, brownies." },
|
|
5730
|
+
{ slug: "cakes", name: "Cakes", description: "Slices and made-to-order showstoppers." },
|
|
5731
|
+
{ slug: "drinks", name: "Drinks", description: "Locally roasted coffee, brewed teas, fresh juice." },
|
|
5732
|
+
{ slug: "boxes", name: "Boxes & Bundles", description: "Curated boxes for sharing." }
|
|
5733
|
+
];
|
|
5734
|
+
const catIds = /* @__PURE__ */ new Map();
|
|
5735
|
+
for (const c of categoryDefs) {
|
|
5304
5736
|
const id = `cat_${c.slug}`;
|
|
5305
|
-
|
|
5737
|
+
catIds.set(c.slug, id);
|
|
5306
5738
|
const cat = {
|
|
5307
5739
|
id,
|
|
5308
|
-
business_id:
|
|
5740
|
+
business_id: BUSINESS_ID,
|
|
5309
5741
|
name: c.name,
|
|
5310
5742
|
slug: c.slug,
|
|
5311
5743
|
description: c.description,
|
|
@@ -5317,99 +5749,354 @@ function seedDefault(registry) {
|
|
|
5317
5749
|
};
|
|
5318
5750
|
registry.categories.put(id, cat);
|
|
5319
5751
|
}
|
|
5320
|
-
const
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5752
|
+
const productInputs = [
|
|
5753
|
+
// ── Breads ──
|
|
5754
|
+
{
|
|
5755
|
+
id: "prod_sourdough-loaf",
|
|
5756
|
+
name: "Country Sourdough",
|
|
5757
|
+
slug: "sourdough-loaf",
|
|
5758
|
+
price: "25.00",
|
|
5759
|
+
description: "24-hour fermented country loaf with our heritage starter \u2014 open crumb, blistered crust, naturally tangy. Baked fresh every morning.",
|
|
5760
|
+
category: "breads",
|
|
5761
|
+
imageUrl: img("1509440159596-0249088772ff"),
|
|
5762
|
+
images: [img("1509440159596-0249088772ff"), img("1486887396153-fa416526c108")],
|
|
5763
|
+
tags: ["bread", "vegan", "signature"],
|
|
5764
|
+
isSignature: true,
|
|
5765
|
+
ingredients: ["Wheat flour", "Sourdough starter", "Sea salt", "Filtered water"],
|
|
5766
|
+
pairings: ["Olive oil", "Aged cheese", "Tomato jam"],
|
|
5767
|
+
allergies: ["wheat", "gluten"],
|
|
5768
|
+
calories: 240
|
|
5769
|
+
},
|
|
5770
|
+
{
|
|
5771
|
+
id: "prod_whole-wheat",
|
|
5772
|
+
name: "Whole Wheat Seed Loaf",
|
|
5773
|
+
slug: "whole-wheat",
|
|
5774
|
+
price: "20.00",
|
|
5775
|
+
description: "Hearty 100% whole wheat with sunflower, sesame, and pumpkin seeds. Slow-fermented for a soft crumb and nutty depth.",
|
|
5776
|
+
category: "breads",
|
|
5777
|
+
imageUrl: img("1486887396153-fa416526c108"),
|
|
5778
|
+
tags: ["bread", "vegan", "high-fibre"],
|
|
5779
|
+
ingredients: ["Whole wheat flour", "Sunflower seeds", "Sesame", "Pumpkin seeds"],
|
|
5780
|
+
allergies: ["wheat", "gluten", "sesame"],
|
|
5781
|
+
calories: 220
|
|
5782
|
+
},
|
|
5783
|
+
{
|
|
5784
|
+
id: "prod_baguette",
|
|
5785
|
+
name: "Classic Baguette",
|
|
5786
|
+
slug: "baguette",
|
|
5787
|
+
price: "15.00",
|
|
5788
|
+
description: "Crackling crust, airy crumb. Best eaten warm, within an hour of baking.",
|
|
5789
|
+
category: "breads",
|
|
5790
|
+
imageUrl: img("1568471173242-461f0a730452"),
|
|
5791
|
+
tags: ["bread", "vegan"],
|
|
5792
|
+
ingredients: ["Wheat flour", "Yeast", "Sea salt", "Water"],
|
|
5793
|
+
allergies: ["wheat", "gluten"],
|
|
5794
|
+
calories: 180
|
|
5795
|
+
},
|
|
5796
|
+
{
|
|
5797
|
+
id: "prod_focaccia",
|
|
5798
|
+
name: "Rosemary Focaccia",
|
|
5799
|
+
slug: "focaccia",
|
|
5800
|
+
price: "30.00",
|
|
5801
|
+
description: "High-hydration focaccia drenched in olive oil, scattered with sea salt and fresh rosemary. Sold whole or by the slab.",
|
|
5802
|
+
category: "breads",
|
|
5803
|
+
imageUrl: img("1571167530149-c1105da4c2c7"),
|
|
5804
|
+
tags: ["bread", "vegetarian"],
|
|
5805
|
+
ingredients: ["00 flour", "Olive oil", "Rosemary", "Maldon salt"],
|
|
5806
|
+
pairings: ["Burrata", "Prosciutto", "Caponata"],
|
|
5807
|
+
allergies: ["wheat", "gluten"],
|
|
5808
|
+
calories: 310
|
|
5809
|
+
},
|
|
5810
|
+
{
|
|
5811
|
+
id: "prod_banana-bread",
|
|
5812
|
+
name: "Brown Butter Banana Bread",
|
|
5813
|
+
slug: "banana-bread",
|
|
5814
|
+
price: "18.00",
|
|
5815
|
+
description: "Caramelised brown butter, three over-ripe bananas, walnuts, finished with a salted maple glaze.",
|
|
5816
|
+
category: "breads",
|
|
5817
|
+
imageUrl: img("1632931057819-4eefffa8e007"),
|
|
5818
|
+
tags: ["sweet", "comfort"],
|
|
5819
|
+
ingredients: ["Bananas", "Brown butter", "Walnuts", "Maple syrup"],
|
|
5820
|
+
allergies: ["wheat", "gluten", "dairy", "nuts", "eggs"],
|
|
5821
|
+
calories: 360,
|
|
5822
|
+
isNew: true
|
|
5823
|
+
},
|
|
5824
|
+
// ── Pastries ──
|
|
5825
|
+
{
|
|
5826
|
+
id: "prod_croissant",
|
|
5827
|
+
name: "Butter Croissant",
|
|
5828
|
+
slug: "croissant",
|
|
5829
|
+
price: "12.00",
|
|
5830
|
+
description: "27 layers of laminated dough wrapped around French butter. Crisp, shattering crust and a tender honeycomb interior.",
|
|
5831
|
+
category: "pastries",
|
|
5832
|
+
imageUrl: img("1555507036-ab1f4038808a"),
|
|
5833
|
+
images: [img("1555507036-ab1f4038808a"), img("1530610476181-d83430b64dcd")],
|
|
5834
|
+
tags: ["pastry", "vegetarian", "signature"],
|
|
5835
|
+
isSignature: true,
|
|
5836
|
+
ingredients: ["French butter", "Wheat flour", "Milk", "Eggs"],
|
|
5837
|
+
allergies: ["wheat", "gluten", "dairy", "eggs"],
|
|
5838
|
+
calories: 280
|
|
5839
|
+
},
|
|
5840
|
+
{
|
|
5841
|
+
id: "prod_pain-au-chocolat",
|
|
5842
|
+
name: "Pain au Chocolat",
|
|
5843
|
+
slug: "pain-au-chocolat",
|
|
5844
|
+
price: "14.00",
|
|
5845
|
+
description: "Our croissant dough wrapped around two batons of 70% Valrhona dark chocolate.",
|
|
5846
|
+
category: "pastries",
|
|
5847
|
+
imageUrl: img("1530610476181-d83430b64dcd"),
|
|
5848
|
+
tags: ["pastry", "chocolate"],
|
|
5849
|
+
ingredients: ["Croissant dough", "Valrhona chocolate"],
|
|
5850
|
+
allergies: ["wheat", "gluten", "dairy", "eggs"],
|
|
5851
|
+
calories: 320
|
|
5852
|
+
},
|
|
5853
|
+
{
|
|
5854
|
+
id: "prod_almond-danish",
|
|
5855
|
+
name: "Almond Danish",
|
|
5856
|
+
slug: "almond-danish",
|
|
5857
|
+
price: "16.00",
|
|
5858
|
+
description: "Flaky pastry filled with almond cream, dusted with toasted slivers and powdered sugar.",
|
|
5859
|
+
category: "pastries",
|
|
5860
|
+
imageUrl: img("1517433367423-c7e5b0f35086"),
|
|
5861
|
+
tags: ["pastry", "nuts"],
|
|
5862
|
+
ingredients: ["Pastry dough", "Almond cream", "Toasted almonds"],
|
|
5863
|
+
allergies: ["wheat", "gluten", "dairy", "eggs", "nuts"],
|
|
5864
|
+
calories: 340
|
|
5865
|
+
},
|
|
5866
|
+
{
|
|
5867
|
+
id: "prod_cinnamon-roll",
|
|
5868
|
+
name: "Cinnamon Roll",
|
|
5869
|
+
slug: "cinnamon-roll",
|
|
5870
|
+
price: "13.00",
|
|
5871
|
+
description: "Soft enriched dough swirled with butter and Ceylon cinnamon, finished with cream-cheese glaze.",
|
|
5872
|
+
category: "pastries",
|
|
5873
|
+
imageUrl: img("1607920591413-4ec007e70023"),
|
|
5874
|
+
tags: ["pastry", "comfort"],
|
|
5875
|
+
ingredients: ["Enriched dough", "Cinnamon", "Brown sugar", "Cream cheese"],
|
|
5876
|
+
allergies: ["wheat", "gluten", "dairy", "eggs"],
|
|
5877
|
+
calories: 380
|
|
5878
|
+
},
|
|
5879
|
+
{
|
|
5880
|
+
id: "prod_brownie",
|
|
5881
|
+
name: "Fudge Brownie",
|
|
5882
|
+
slug: "brownie",
|
|
5883
|
+
price: "12.00",
|
|
5884
|
+
description: "Dark, dense, fudgy brownie with a glossy shell. Chocolate four ways. Best slightly warm.",
|
|
5885
|
+
category: "pastries",
|
|
5886
|
+
imageUrl: img("1606313564200-e75d5e30476c"),
|
|
5887
|
+
tags: ["chocolate", "vegetarian"],
|
|
5888
|
+
ingredients: ["Dark chocolate", "Cocoa", "Brown butter", "Eggs"],
|
|
5889
|
+
allergies: ["wheat", "gluten", "dairy", "eggs"],
|
|
5890
|
+
calories: 420
|
|
5891
|
+
},
|
|
5892
|
+
{
|
|
5893
|
+
id: "prod_macaron-box",
|
|
5894
|
+
name: "Macaron Box (6)",
|
|
5895
|
+
slug: "macaron-box",
|
|
5896
|
+
price: "60.00",
|
|
5897
|
+
description: "Six French macarons \u2014 pistachio, raspberry, salted caramel, espresso, vanilla bean, and dark chocolate ganache.",
|
|
5898
|
+
category: "pastries",
|
|
5899
|
+
imageUrl: img("1558326567-98ae2405596b"),
|
|
5900
|
+
tags: ["pastry", "gift", "premium"],
|
|
5901
|
+
ingredients: ["Almond flour", "Egg whites", "Sugar", "Various fillings"],
|
|
5902
|
+
allergies: ["nuts", "eggs", "dairy"],
|
|
5903
|
+
calories: 540
|
|
5904
|
+
},
|
|
5905
|
+
// ── Cakes ──
|
|
5906
|
+
{
|
|
5907
|
+
id: "prod_chocolate-cake",
|
|
5908
|
+
name: "Dark Chocolate Layer Cake",
|
|
5909
|
+
slug: "chocolate-cake",
|
|
5910
|
+
price: "22.00",
|
|
5911
|
+
description: "Three layers of dark chocolate sponge with chocolate ganache and cr\xE8me p\xE2tissi\xE8re, finished with a mirror glaze. (Slice)",
|
|
5912
|
+
category: "cakes",
|
|
5913
|
+
imageUrl: img("1565958011703-44f9829ba187"),
|
|
5914
|
+
tags: ["cake", "chocolate", "signature"],
|
|
5915
|
+
isSignature: true,
|
|
5916
|
+
ingredients: ["Dark chocolate", "Cr\xE8me p\xE2tissi\xE8re", "Cocoa", "Eggs"],
|
|
5917
|
+
pairings: ["Espresso", "Cabernet"],
|
|
5918
|
+
allergies: ["wheat", "gluten", "dairy", "eggs"],
|
|
5919
|
+
calories: 480
|
|
5920
|
+
},
|
|
5921
|
+
{
|
|
5922
|
+
id: "prod_red-velvet",
|
|
5923
|
+
name: "Red Velvet (Slice)",
|
|
5924
|
+
slug: "red-velvet",
|
|
5925
|
+
price: "22.00",
|
|
5926
|
+
description: "Classic red velvet with cream cheese frosting and white chocolate shavings.",
|
|
5927
|
+
category: "cakes",
|
|
5928
|
+
imageUrl: img("1586788680434-30d324b2d46f"),
|
|
5929
|
+
tags: ["cake"],
|
|
5930
|
+
ingredients: ["Buttermilk", "Cocoa", "Cream cheese", "Beetroot"],
|
|
5931
|
+
allergies: ["wheat", "gluten", "dairy", "eggs"],
|
|
5932
|
+
calories: 460
|
|
5933
|
+
},
|
|
5934
|
+
{
|
|
5935
|
+
id: "prod_carrot-cake",
|
|
5936
|
+
name: "Carrot Cake (Slice)",
|
|
5937
|
+
slug: "carrot-cake",
|
|
5938
|
+
price: "20.00",
|
|
5939
|
+
description: "Spiced carrot cake with toasted walnuts, golden raisins, and cream-cheese frosting.",
|
|
5940
|
+
category: "cakes",
|
|
5941
|
+
imageUrl: img("1568827999250-3f6afff96e66"),
|
|
5942
|
+
tags: ["cake", "spiced"],
|
|
5943
|
+
ingredients: ["Carrot", "Walnuts", "Cinnamon", "Cream cheese"],
|
|
5944
|
+
allergies: ["wheat", "gluten", "dairy", "eggs", "nuts"],
|
|
5945
|
+
calories: 420
|
|
5946
|
+
},
|
|
5947
|
+
{
|
|
5948
|
+
id: "prod_cheesecake",
|
|
5949
|
+
name: "New York Cheesecake (Slice)",
|
|
5950
|
+
slug: "cheesecake",
|
|
5951
|
+
price: "24.00",
|
|
5952
|
+
description: "Dense, creamy New York cheesecake on a butter graham crust. Topped with seasonal berry compote.",
|
|
5953
|
+
category: "cakes",
|
|
5954
|
+
imageUrl: img("1567306226416-28f0efdc88ce"),
|
|
5955
|
+
tags: ["cake", "premium"],
|
|
5956
|
+
ingredients: ["Cream cheese", "Sour cream", "Graham crust", "Vanilla"],
|
|
5957
|
+
allergies: ["wheat", "gluten", "dairy", "eggs"],
|
|
5958
|
+
calories: 520
|
|
5959
|
+
},
|
|
5960
|
+
{
|
|
5961
|
+
id: "prod_birthday-cake",
|
|
5962
|
+
name: "Custom Celebration Cake",
|
|
5963
|
+
slug: "birthday-cake",
|
|
5964
|
+
price: "250.00",
|
|
5965
|
+
description: "Made-to-order three-tier cake. Choose size, flavour, and frosting. 48-hour notice required for personalisation.",
|
|
5966
|
+
category: "cakes",
|
|
5967
|
+
imageUrl: img("1535254973040-607b474cb50d"),
|
|
5968
|
+
images: [img("1535254973040-607b474cb50d"), img("1565958011703-44f9829ba187")],
|
|
5969
|
+
tags: ["cake", "made-to-order", "premium"],
|
|
5970
|
+
ingredients: ["Sponge cake", "Buttercream", "Custom decoration"],
|
|
5971
|
+
allergies: ["wheat", "gluten", "dairy", "eggs"],
|
|
5972
|
+
productType: "product"
|
|
5973
|
+
},
|
|
5974
|
+
// ── Drinks ──
|
|
5975
|
+
{
|
|
5976
|
+
id: "prod_coffee",
|
|
5977
|
+
name: "Single-Origin Coffee",
|
|
5978
|
+
slug: "coffee",
|
|
5979
|
+
price: "10.00",
|
|
5980
|
+
description: "Locally roasted Arabica from Volta Region. Notes of cocoa, dark fruit, and brown sugar.",
|
|
5981
|
+
category: "drinks",
|
|
5982
|
+
imageUrl: img("1495474472287-4d71bcdd2085"),
|
|
5983
|
+
tags: ["drink", "coffee", "vegan"],
|
|
5984
|
+
ingredients: ["Single-origin Arabica beans"],
|
|
5985
|
+
pairings: ["Croissant", "Brownie"],
|
|
5986
|
+
calories: 5
|
|
5987
|
+
},
|
|
5988
|
+
{
|
|
5989
|
+
id: "prod_tea",
|
|
5990
|
+
name: "Loose-Leaf Tea",
|
|
5991
|
+
slug: "tea",
|
|
5992
|
+
price: "8.00",
|
|
5993
|
+
description: "Choice of English breakfast, Earl Grey, green sencha, or chamomile.",
|
|
5994
|
+
category: "drinks",
|
|
5995
|
+
imageUrl: img("1576092768241-dec231879fc3"),
|
|
5996
|
+
tags: ["drink", "tea", "vegan"],
|
|
5997
|
+
calories: 0
|
|
5998
|
+
},
|
|
5999
|
+
{
|
|
6000
|
+
id: "prod_sobolo",
|
|
6001
|
+
name: "Hibiscus (Sobolo)",
|
|
6002
|
+
slug: "sobolo",
|
|
6003
|
+
price: "12.00",
|
|
6004
|
+
description: "Iced hibiscus tea with ginger, pineapple peel, and a hint of clove. Tart, refreshing, lightly sweet.",
|
|
6005
|
+
category: "drinks",
|
|
6006
|
+
imageUrl: img("1556679343-c7306c1976bc"),
|
|
6007
|
+
tags: ["drink", "vegan", "ghana"],
|
|
6008
|
+
isSignature: true,
|
|
6009
|
+
ingredients: ["Hibiscus", "Ginger", "Pineapple", "Clove"],
|
|
6010
|
+
calories: 60
|
|
6011
|
+
},
|
|
6012
|
+
{
|
|
6013
|
+
id: "prod_orange-juice",
|
|
6014
|
+
name: "Cold-Pressed Orange Juice",
|
|
6015
|
+
slug: "orange-juice",
|
|
6016
|
+
price: "15.00",
|
|
6017
|
+
description: "Hand-pressed at sunrise from Ghanaian Valencia oranges. No sugar, no concentrate.",
|
|
6018
|
+
category: "drinks",
|
|
6019
|
+
imageUrl: img("1622597467836-f3285f2131b8"),
|
|
6020
|
+
tags: ["drink", "vegan"],
|
|
6021
|
+
calories: 110
|
|
6022
|
+
},
|
|
6023
|
+
// ── Boxes (anchor products for bundle/composite) ──
|
|
6024
|
+
{
|
|
6025
|
+
id: "prod_breakfast-box",
|
|
6026
|
+
name: "Breakfast Box",
|
|
6027
|
+
slug: "breakfast-box",
|
|
6028
|
+
price: "30.00",
|
|
6029
|
+
description: "A buttery croissant, a single-origin coffee, and a fresh-pressed orange juice. Everything you need to start the day. Save GH\u20B57 vs buying separately.",
|
|
6030
|
+
category: "boxes",
|
|
6031
|
+
imageUrl: img("1533089860892-a7c6f0a88666"),
|
|
6032
|
+
images: [img("1533089860892-a7c6f0a88666"), img("1495474472287-4d71bcdd2085")],
|
|
6033
|
+
tags: ["bundle", "breakfast"],
|
|
6034
|
+
productType: "bundle"
|
|
6035
|
+
},
|
|
6036
|
+
{
|
|
6037
|
+
id: "prod_pastry-box",
|
|
6038
|
+
name: "Build-Your-Own Pastry Box",
|
|
6039
|
+
slug: "pastry-box",
|
|
6040
|
+
price: "60.00",
|
|
6041
|
+
description: "Pick 6 to 12 of our pastries \u2014 mix and match. Freshly boxed for sharing.",
|
|
6042
|
+
category: "boxes",
|
|
6043
|
+
imageUrl: img("1486427944299-d1955d23e34d"),
|
|
6044
|
+
tags: ["composite", "build-your-own", "gift"],
|
|
6045
|
+
productType: "composite"
|
|
6046
|
+
}
|
|
6047
|
+
];
|
|
6048
|
+
for (const input of productInputs) {
|
|
6049
|
+
const product = makeProduct(input, catIds);
|
|
6050
|
+
registry.products.put(product.id, product);
|
|
6051
|
+
const cat = registry.categories.get(catIds.get(input.category));
|
|
6052
|
+
if (cat) {
|
|
6053
|
+
cat.product_ids.push(product.id);
|
|
6054
|
+
registry.categories.put(cat.id, cat);
|
|
6055
|
+
}
|
|
6056
|
+
}
|
|
6057
|
+
const featuredIds = [
|
|
6058
|
+
"prod_sourdough-loaf",
|
|
6059
|
+
"prod_croissant",
|
|
6060
|
+
"prod_chocolate-cake",
|
|
6061
|
+
"prod_sobolo",
|
|
6062
|
+
"prod_pastry-box"
|
|
6063
|
+
];
|
|
6064
|
+
const featured = {
|
|
5350
6065
|
id: "col_featured",
|
|
5351
|
-
business_id:
|
|
5352
|
-
name: "Featured",
|
|
6066
|
+
business_id: BUSINESS_ID,
|
|
6067
|
+
name: "Signature & Featured",
|
|
5353
6068
|
slug: "featured",
|
|
5354
|
-
description: "Our most
|
|
5355
|
-
product_ids:
|
|
6069
|
+
description: "Our most-loved bakes and a few seasonal stars.",
|
|
6070
|
+
product_ids: featuredIds,
|
|
5356
6071
|
is_active: true,
|
|
5357
6072
|
created_at: ISO,
|
|
5358
6073
|
updated_at: ISO
|
|
5359
6074
|
};
|
|
5360
|
-
registry.collections.put(
|
|
5361
|
-
for (const pid of
|
|
6075
|
+
registry.collections.put(featured.id, featured);
|
|
6076
|
+
for (const pid of featuredIds) {
|
|
5362
6077
|
const p = registry.products.get(pid);
|
|
5363
6078
|
if (p) {
|
|
5364
|
-
p.collection_ids.push(
|
|
6079
|
+
p.collection_ids.push(featured.id);
|
|
5365
6080
|
registry.products.put(pid, p);
|
|
5366
6081
|
}
|
|
5367
6082
|
}
|
|
5368
|
-
const tasting = {
|
|
5369
|
-
id: "svc_tasting",
|
|
5370
|
-
business_id: businessId,
|
|
5371
|
-
name: "Bakery Tasting Session",
|
|
5372
|
-
description: "Walk through our pastry menu with our head baker.",
|
|
5373
|
-
duration_minutes: 60,
|
|
5374
|
-
price: "150.00",
|
|
5375
|
-
is_available: true
|
|
5376
|
-
};
|
|
5377
|
-
registry.services.put(tasting.id, tasting);
|
|
5378
|
-
const cake = {
|
|
5379
|
-
id: "svc_custom_cake",
|
|
5380
|
-
business_id: businessId,
|
|
5381
|
-
name: "Custom Cake Consultation",
|
|
5382
|
-
description: "Design a custom cake for your event.",
|
|
5383
|
-
duration_minutes: 30,
|
|
5384
|
-
price: "0.00",
|
|
5385
|
-
is_available: true
|
|
5386
|
-
};
|
|
5387
|
-
registry.services.put(cake.id, cake);
|
|
5388
|
-
const sizeAxisId = "axis_size";
|
|
5389
|
-
const flavourAxisId = "axis_flavour";
|
|
5390
|
-
const sizeValues = [
|
|
5391
|
-
{ id: "axv_size_small", name: 'Small (8")' },
|
|
5392
|
-
{ id: "axv_size_medium", name: 'Medium (10")' },
|
|
5393
|
-
{ id: "axv_size_large", name: 'Large (12")' }
|
|
5394
|
-
];
|
|
5395
|
-
const flavourValues = [
|
|
5396
|
-
{ id: "axv_flavour_chocolate", name: "Chocolate" },
|
|
5397
|
-
{ id: "axv_flavour_vanilla", name: "Vanilla" },
|
|
5398
|
-
{ id: "axv_flavour_red_velvet", name: "Red Velvet" }
|
|
5399
|
-
];
|
|
5400
6083
|
const sizeAxis = {
|
|
5401
|
-
id:
|
|
5402
|
-
business_id:
|
|
6084
|
+
id: "axis_cake_size",
|
|
6085
|
+
business_id: BUSINESS_ID,
|
|
5403
6086
|
product_id: "prod_birthday-cake",
|
|
5404
6087
|
name: "Size",
|
|
5405
6088
|
display_order: 0,
|
|
5406
6089
|
affects_recipe: false,
|
|
5407
|
-
values:
|
|
6090
|
+
values: [
|
|
6091
|
+
{ id: "axv_size_small", name: 'Small (8")', display: 0 },
|
|
6092
|
+
{ id: "axv_size_medium", name: 'Medium (10")', display: 1 },
|
|
6093
|
+
{ id: "axv_size_large", name: 'Large (12")', display: 2 }
|
|
6094
|
+
].map((v) => ({
|
|
5408
6095
|
id: v.id,
|
|
5409
|
-
business_id:
|
|
5410
|
-
axis_id:
|
|
6096
|
+
business_id: BUSINESS_ID,
|
|
6097
|
+
axis_id: "axis_cake_size",
|
|
5411
6098
|
name: v.name,
|
|
5412
|
-
display_order:
|
|
6099
|
+
display_order: v.display,
|
|
5413
6100
|
created_at: ISO,
|
|
5414
6101
|
updated_at: ISO
|
|
5415
6102
|
})),
|
|
@@ -5417,18 +6104,22 @@ function seedDefault(registry) {
|
|
|
5417
6104
|
updated_at: ISO
|
|
5418
6105
|
};
|
|
5419
6106
|
const flavourAxis = {
|
|
5420
|
-
id:
|
|
5421
|
-
business_id:
|
|
6107
|
+
id: "axis_cake_flavour",
|
|
6108
|
+
business_id: BUSINESS_ID,
|
|
5422
6109
|
product_id: "prod_birthday-cake",
|
|
5423
6110
|
name: "Flavour",
|
|
5424
6111
|
display_order: 1,
|
|
5425
6112
|
affects_recipe: true,
|
|
5426
|
-
values:
|
|
6113
|
+
values: [
|
|
6114
|
+
{ id: "axv_flavour_chocolate", name: "Dark Chocolate", display: 0 },
|
|
6115
|
+
{ id: "axv_flavour_vanilla", name: "Vanilla Bean", display: 1 },
|
|
6116
|
+
{ id: "axv_flavour_red_velvet", name: "Red Velvet", display: 2 }
|
|
6117
|
+
].map((v) => ({
|
|
5427
6118
|
id: v.id,
|
|
5428
|
-
business_id:
|
|
5429
|
-
axis_id:
|
|
6119
|
+
business_id: BUSINESS_ID,
|
|
6120
|
+
axis_id: "axis_cake_flavour",
|
|
5430
6121
|
name: v.name,
|
|
5431
|
-
display_order:
|
|
6122
|
+
display_order: v.display,
|
|
5432
6123
|
created_at: ISO,
|
|
5433
6124
|
updated_at: ISO
|
|
5434
6125
|
})),
|
|
@@ -5437,40 +6128,42 @@ function seedDefault(registry) {
|
|
|
5437
6128
|
};
|
|
5438
6129
|
registry.variantAxes.put(sizeAxis.id, sizeAxis);
|
|
5439
6130
|
registry.variantAxes.put(flavourAxis.id, flavourAxis);
|
|
5440
|
-
const
|
|
5441
|
-
const
|
|
5442
|
-
let
|
|
5443
|
-
for (
|
|
5444
|
-
for (
|
|
5445
|
-
const
|
|
6131
|
+
const sizeAdj = { small: "250.00", medium: "350.00", large: "450.00" };
|
|
6132
|
+
const cakeVariantIds = [];
|
|
6133
|
+
let firstVariant = true;
|
|
6134
|
+
for (const sz of sizeAxis.values) {
|
|
6135
|
+
for (const fl of flavourAxis.values) {
|
|
6136
|
+
const sizeKey = sz.id.replace("axv_size_", "");
|
|
6137
|
+
const flavourKey = fl.id.replace("axv_flavour_", "");
|
|
6138
|
+
const id = `var_size_${sizeKey}_flavour_${flavourKey}`;
|
|
5446
6139
|
const variant = {
|
|
5447
6140
|
id,
|
|
5448
6141
|
product_id: "prod_birthday-cake",
|
|
5449
|
-
business_id:
|
|
5450
|
-
name: `${
|
|
5451
|
-
sku: `BIRTHDAY-${
|
|
5452
|
-
price_adjustment:
|
|
6142
|
+
business_id: BUSINESS_ID,
|
|
6143
|
+
name: `${sz.name} \u2022 ${fl.name}`,
|
|
6144
|
+
sku: `BIRTHDAY-${sizeKey.toUpperCase()}-${flavourKey.toUpperCase()}`,
|
|
6145
|
+
price_adjustment: sizeAdj[sizeKey] ?? "0.00",
|
|
5453
6146
|
component_multiplier: "1.00",
|
|
5454
|
-
is_default:
|
|
6147
|
+
is_default: firstVariant,
|
|
5455
6148
|
is_active: true,
|
|
5456
|
-
axis_value_ids: [
|
|
6149
|
+
axis_value_ids: [sz.id, fl.id],
|
|
5457
6150
|
created_at: ISO,
|
|
5458
6151
|
updated_at: ISO
|
|
5459
6152
|
};
|
|
5460
6153
|
registry.variants.put(id, variant);
|
|
5461
|
-
|
|
5462
|
-
|
|
6154
|
+
cakeVariantIds.push(id);
|
|
6155
|
+
firstVariant = false;
|
|
5463
6156
|
}
|
|
5464
6157
|
}
|
|
5465
|
-
const
|
|
5466
|
-
if (
|
|
5467
|
-
|
|
5468
|
-
registry.products.put(
|
|
6158
|
+
const birthdayProduct = registry.products.get("prod_birthday-cake");
|
|
6159
|
+
if (birthdayProduct) {
|
|
6160
|
+
birthdayProduct.variant_ids = cakeVariantIds;
|
|
6161
|
+
registry.products.put(birthdayProduct.id, birthdayProduct);
|
|
5469
6162
|
}
|
|
5470
6163
|
const toppingsAddOn = {
|
|
5471
6164
|
id: "addon_toppings",
|
|
5472
|
-
business_id:
|
|
5473
|
-
name: "
|
|
6165
|
+
business_id: BUSINESS_ID,
|
|
6166
|
+
name: "Cake toppings",
|
|
5474
6167
|
is_multiple_allowed: true,
|
|
5475
6168
|
is_required: false,
|
|
5476
6169
|
is_mutually_exclusive: false,
|
|
@@ -5479,15 +6172,14 @@ function seedDefault(registry) {
|
|
|
5479
6172
|
created_at: ISO,
|
|
5480
6173
|
updated_at: ISO,
|
|
5481
6174
|
options: [
|
|
5482
|
-
{ id: "addopt_strawberry", add_on_id: "addon_toppings", business_id:
|
|
5483
|
-
{ id: "addopt_choc_chips", add_on_id: "addon_toppings", business_id:
|
|
5484
|
-
{ id: "addopt_walnuts", add_on_id: "addon_toppings", business_id:
|
|
6175
|
+
{ id: "addopt_strawberry", add_on_id: "addon_toppings", business_id: BUSINESS_ID, name: "Fresh strawberries", default_price: "8.00", is_required: false, is_mutually_exclusive: false, created_at: ISO, updated_at: ISO },
|
|
6176
|
+
{ id: "addopt_choc_chips", add_on_id: "addon_toppings", business_id: BUSINESS_ID, name: "Dark chocolate shards", default_price: "5.00", is_required: false, is_mutually_exclusive: false, created_at: ISO, updated_at: ISO },
|
|
6177
|
+
{ id: "addopt_walnuts", add_on_id: "addon_toppings", business_id: BUSINESS_ID, name: "Toasted walnuts", default_price: "6.00", is_required: false, is_mutually_exclusive: false, created_at: ISO, updated_at: ISO }
|
|
5485
6178
|
]
|
|
5486
6179
|
};
|
|
5487
|
-
registry.addOns.put(toppingsAddOn.id, toppingsAddOn);
|
|
5488
6180
|
const milkAddOn = {
|
|
5489
6181
|
id: "addon_milk",
|
|
5490
|
-
business_id:
|
|
6182
|
+
business_id: BUSINESS_ID,
|
|
5491
6183
|
name: "Milk",
|
|
5492
6184
|
is_multiple_allowed: false,
|
|
5493
6185
|
is_required: false,
|
|
@@ -5497,17 +6189,17 @@ function seedDefault(registry) {
|
|
|
5497
6189
|
created_at: ISO,
|
|
5498
6190
|
updated_at: ISO,
|
|
5499
6191
|
options: [
|
|
5500
|
-
{ id: "addopt_whole", add_on_id: "addon_milk", business_id:
|
|
5501
|
-
{ id: "addopt_oat", add_on_id: "addon_milk", business_id:
|
|
5502
|
-
{ id: "addopt_almond", add_on_id: "addon_milk", business_id:
|
|
6192
|
+
{ id: "addopt_whole", add_on_id: "addon_milk", business_id: BUSINESS_ID, name: "Whole milk", default_price: "0.00", is_required: false, is_mutually_exclusive: true, created_at: ISO, updated_at: ISO },
|
|
6193
|
+
{ id: "addopt_oat", add_on_id: "addon_milk", business_id: BUSINESS_ID, name: "Oat milk", default_price: "3.00", is_required: false, is_mutually_exclusive: true, created_at: ISO, updated_at: ISO },
|
|
6194
|
+
{ id: "addopt_almond", add_on_id: "addon_milk", business_id: BUSINESS_ID, name: "Almond milk", default_price: "3.00", is_required: false, is_mutually_exclusive: true, created_at: ISO, updated_at: ISO }
|
|
5503
6195
|
]
|
|
5504
6196
|
};
|
|
6197
|
+
registry.addOns.put(toppingsAddOn.id, toppingsAddOn);
|
|
5505
6198
|
registry.addOns.put(milkAddOn.id, milkAddOn);
|
|
5506
6199
|
const cakeProductIds = ["prod_chocolate-cake", "prod_red-velvet", "prod_carrot-cake", "prod_cheesecake", "prod_birthday-cake"];
|
|
5507
6200
|
const drinkProductIds = ["prod_coffee", "prod_tea"];
|
|
5508
6201
|
for (const pid of cakeProductIds) {
|
|
5509
|
-
|
|
5510
|
-
registry.productAddOns.put(`${pid}:${toppingsAddOn.id}`, link);
|
|
6202
|
+
registry.productAddOns.put(`${pid}:${toppingsAddOn.id}`, { product_id: pid, add_on_id: toppingsAddOn.id });
|
|
5511
6203
|
const p = registry.products.get(pid);
|
|
5512
6204
|
if (p) {
|
|
5513
6205
|
p.add_on_ids.push(toppingsAddOn.id);
|
|
@@ -5515,8 +6207,7 @@ function seedDefault(registry) {
|
|
|
5515
6207
|
}
|
|
5516
6208
|
}
|
|
5517
6209
|
for (const pid of drinkProductIds) {
|
|
5518
|
-
|
|
5519
|
-
registry.productAddOns.put(`${pid}:${milkAddOn.id}`, link);
|
|
6210
|
+
registry.productAddOns.put(`${pid}:${milkAddOn.id}`, { product_id: pid, add_on_id: milkAddOn.id });
|
|
5520
6211
|
const p = registry.products.get(pid);
|
|
5521
6212
|
if (p) {
|
|
5522
6213
|
p.add_on_ids.push(milkAddOn.id);
|
|
@@ -5525,7 +6216,7 @@ function seedDefault(registry) {
|
|
|
5525
6216
|
}
|
|
5526
6217
|
const breakfastBundle = {
|
|
5527
6218
|
id: "bnd_breakfast_box",
|
|
5528
|
-
business_id:
|
|
6219
|
+
business_id: BUSINESS_ID,
|
|
5529
6220
|
product_id: "prod_breakfast-box",
|
|
5530
6221
|
name: "Breakfast Box",
|
|
5531
6222
|
slug: "breakfast-box",
|
|
@@ -5537,29 +6228,9 @@ function seedDefault(registry) {
|
|
|
5537
6228
|
updated_at: ISO
|
|
5538
6229
|
};
|
|
5539
6230
|
registry.bundles.put(breakfastBundle.id, breakfastBundle);
|
|
5540
|
-
const breakfastProduct = {
|
|
5541
|
-
id: "prod_breakfast-box",
|
|
5542
|
-
business_id: businessId,
|
|
5543
|
-
name: "Breakfast Box",
|
|
5544
|
-
slug: "breakfast-box",
|
|
5545
|
-
description: "Bundled breakfast for one.",
|
|
5546
|
-
product_type: "bundle",
|
|
5547
|
-
base_price: "30.00",
|
|
5548
|
-
currency: "GHS",
|
|
5549
|
-
image: "https://mock.cimplify.io/img/breakfast-box.jpg",
|
|
5550
|
-
images: ["https://mock.cimplify.io/img/breakfast-box.jpg"],
|
|
5551
|
-
is_available: true,
|
|
5552
|
-
category_ids: [categoryIdsBySlug.get("pastries")],
|
|
5553
|
-
collection_ids: [],
|
|
5554
|
-
add_on_ids: [],
|
|
5555
|
-
variant_ids: [],
|
|
5556
|
-
created_at: ISO,
|
|
5557
|
-
updated_at: ISO
|
|
5558
|
-
};
|
|
5559
|
-
registry.products.put(breakfastProduct.id, breakfastProduct);
|
|
5560
6231
|
const pastryComposite = {
|
|
5561
6232
|
id: "comp_pastry_box",
|
|
5562
|
-
business_id:
|
|
6233
|
+
business_id: BUSINESS_ID,
|
|
5563
6234
|
product_id: "prod_pastry-box",
|
|
5564
6235
|
base_price: "60.00",
|
|
5565
6236
|
pricing_mode: "additive",
|
|
@@ -5571,43 +6242,51 @@ function seedDefault(registry) {
|
|
|
5571
6242
|
name: "Choose your pastries",
|
|
5572
6243
|
min: 6,
|
|
5573
6244
|
max: 12,
|
|
5574
|
-
component_ids: [
|
|
6245
|
+
component_ids: [
|
|
6246
|
+
"prod_croissant",
|
|
6247
|
+
"prod_pain-au-chocolat",
|
|
6248
|
+
"prod_almond-danish",
|
|
6249
|
+
"prod_cinnamon-roll",
|
|
6250
|
+
"prod_brownie",
|
|
6251
|
+
"prod_banana-bread",
|
|
6252
|
+
"prod_macaron-box"
|
|
6253
|
+
]
|
|
5575
6254
|
}
|
|
5576
6255
|
],
|
|
5577
6256
|
created_at: ISO,
|
|
5578
6257
|
updated_at: ISO
|
|
5579
6258
|
};
|
|
5580
6259
|
registry.composites.put(pastryComposite.id, pastryComposite);
|
|
5581
|
-
const
|
|
5582
|
-
id: "
|
|
5583
|
-
business_id:
|
|
5584
|
-
name: "
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
updated_at: ISO
|
|
6260
|
+
const tasting = {
|
|
6261
|
+
id: "svc_tasting",
|
|
6262
|
+
business_id: BUSINESS_ID,
|
|
6263
|
+
name: "Bakery Tasting Session",
|
|
6264
|
+
description: "Sample our pastry menu with our head baker.",
|
|
6265
|
+
duration_minutes: 60,
|
|
6266
|
+
price: "150.00",
|
|
6267
|
+
is_available: true
|
|
6268
|
+
};
|
|
6269
|
+
const consult = {
|
|
6270
|
+
id: "svc_custom_cake",
|
|
6271
|
+
business_id: BUSINESS_ID,
|
|
6272
|
+
name: "Custom Cake Consultation",
|
|
6273
|
+
description: "Design a custom cake for your event.",
|
|
6274
|
+
duration_minutes: 30,
|
|
6275
|
+
price: "0.00",
|
|
6276
|
+
is_available: true
|
|
5599
6277
|
};
|
|
5600
|
-
registry.
|
|
6278
|
+
registry.services.put(tasting.id, tasting);
|
|
6279
|
+
registry.services.put(consult.id, consult);
|
|
5601
6280
|
const tags = [
|
|
5602
|
-
{ id: "tag_signature", business_id:
|
|
5603
|
-
{ id: "tag_vegan", business_id:
|
|
5604
|
-
{ id: "tag_gluten_free", business_id:
|
|
5605
|
-
{ id: "tag_seasonal", business_id:
|
|
6281
|
+
{ id: "tag_signature", business_id: BUSINESS_ID, name: "Signature", slug: "signature", color: "#d97706", sort_order: 0, usage_count: 5, created_at: ISO, updated_at: ISO },
|
|
6282
|
+
{ id: "tag_vegan", business_id: BUSINESS_ID, name: "Vegan", slug: "vegan", color: "#16a34a", sort_order: 1, usage_count: 4, created_at: ISO, updated_at: ISO },
|
|
6283
|
+
{ id: "tag_gluten_free", business_id: BUSINESS_ID, name: "Gluten-Free", slug: "gluten-free", color: "#2563eb", sort_order: 2, usage_count: 0, created_at: ISO, updated_at: ISO },
|
|
6284
|
+
{ id: "tag_seasonal", business_id: BUSINESS_ID, name: "Seasonal", slug: "seasonal", color: "#dc2626", sort_order: 3, usage_count: 1, created_at: ISO, updated_at: ISO }
|
|
5606
6285
|
];
|
|
5607
6286
|
for (const t of tags) registry.tags.put(t.id, t);
|
|
5608
6287
|
const baked = {
|
|
5609
6288
|
id: "tax_baked_goods",
|
|
5610
|
-
business_id:
|
|
6289
|
+
business_id: BUSINESS_ID,
|
|
5611
6290
|
name: "Baked Goods",
|
|
5612
6291
|
slug: "baked-goods",
|
|
5613
6292
|
path: ["Food", "Baked Goods"],
|
|
@@ -5617,7 +6296,7 @@ function seedDefault(registry) {
|
|
|
5617
6296
|
};
|
|
5618
6297
|
const beverages = {
|
|
5619
6298
|
id: "tax_beverages",
|
|
5620
|
-
business_id:
|
|
6299
|
+
business_id: BUSINESS_ID,
|
|
5621
6300
|
name: "Beverages",
|
|
5622
6301
|
slug: "beverages",
|
|
5623
6302
|
path: ["Food", "Beverages"],
|
|
@@ -5630,13 +6309,13 @@ function seedDefault(registry) {
|
|
|
5630
6309
|
const attrs = [
|
|
5631
6310
|
{
|
|
5632
6311
|
id: "attr_allergens",
|
|
5633
|
-
business_id:
|
|
6312
|
+
business_id: BUSINESS_ID,
|
|
5634
6313
|
namespace: "core",
|
|
5635
6314
|
name: "Allergens",
|
|
5636
6315
|
slug: "allergens",
|
|
5637
6316
|
description: "Common allergens present in this item.",
|
|
5638
6317
|
attribute_type: "multi_select",
|
|
5639
|
-
options: ["wheat", "eggs", "dairy", "nuts", "soy"],
|
|
6318
|
+
options: ["wheat", "gluten", "eggs", "dairy", "nuts", "soy", "sesame"],
|
|
5640
6319
|
is_required: false,
|
|
5641
6320
|
is_filterable: true,
|
|
5642
6321
|
visibility: "public",
|
|
@@ -5647,7 +6326,7 @@ function seedDefault(registry) {
|
|
|
5647
6326
|
},
|
|
5648
6327
|
{
|
|
5649
6328
|
id: "attr_shelf_life",
|
|
5650
|
-
business_id:
|
|
6329
|
+
business_id: BUSINESS_ID,
|
|
5651
6330
|
namespace: "core",
|
|
5652
6331
|
name: "Shelf life",
|
|
5653
6332
|
slug: "shelf-life",
|
|
@@ -5662,7 +6341,7 @@ function seedDefault(registry) {
|
|
|
5662
6341
|
},
|
|
5663
6342
|
{
|
|
5664
6343
|
id: "attr_caffeine",
|
|
5665
|
-
business_id:
|
|
6344
|
+
business_id: BUSINESS_ID,
|
|
5666
6345
|
namespace: "core",
|
|
5667
6346
|
name: "Caffeine",
|
|
5668
6347
|
slug: "caffeine",
|
|
@@ -5677,7 +6356,7 @@ function seedDefault(registry) {
|
|
|
5677
6356
|
},
|
|
5678
6357
|
{
|
|
5679
6358
|
id: "attr_seasonal",
|
|
5680
|
-
business_id:
|
|
6359
|
+
business_id: BUSINESS_ID,
|
|
5681
6360
|
namespace: "core",
|
|
5682
6361
|
name: "Seasonal",
|
|
5683
6362
|
slug: "seasonal",
|
|
@@ -5695,10 +6374,10 @@ function seedDefault(registry) {
|
|
|
5695
6374
|
const articles = [
|
|
5696
6375
|
{
|
|
5697
6376
|
id: "kb_delivery",
|
|
5698
|
-
business_id:
|
|
5699
|
-
title: "Delivery &
|
|
6377
|
+
business_id: BUSINESS_ID,
|
|
6378
|
+
title: "Delivery & pickup",
|
|
5700
6379
|
slug: "delivery-pickup",
|
|
5701
|
-
content: "Same-day delivery within Accra for orders placed before 2pm. Pickup available 8am\u20138pm.",
|
|
6380
|
+
content: "Same-day delivery within Greater Accra for orders placed before 2pm. Pickup available 8am\u20138pm at our East Legon location.",
|
|
5702
6381
|
category: "logistics",
|
|
5703
6382
|
tags: ["delivery", "pickup"],
|
|
5704
6383
|
created_at: ISO,
|
|
@@ -5706,10 +6385,10 @@ function seedDefault(registry) {
|
|
|
5706
6385
|
},
|
|
5707
6386
|
{
|
|
5708
6387
|
id: "kb_custom_cakes",
|
|
5709
|
-
business_id:
|
|
5710
|
-
title: "
|
|
6388
|
+
business_id: BUSINESS_ID,
|
|
6389
|
+
title: "Custom cakes & celebrations",
|
|
5711
6390
|
slug: "custom-cakes",
|
|
5712
|
-
content: "Custom cakes require 48 hours notice. Book a consultation through the bookings page.",
|
|
6391
|
+
content: "Custom cakes require 48 hours notice. Book a consultation through the bookings page to walk through flavours, sizes, and decoration.",
|
|
5713
6392
|
category: "products",
|
|
5714
6393
|
tags: ["cakes", "custom"],
|
|
5715
6394
|
created_at: ISO,
|
|
@@ -5717,10 +6396,10 @@ function seedDefault(registry) {
|
|
|
5717
6396
|
},
|
|
5718
6397
|
{
|
|
5719
6398
|
id: "kb_allergens",
|
|
5720
|
-
business_id:
|
|
5721
|
-
title: "
|
|
6399
|
+
business_id: BUSINESS_ID,
|
|
6400
|
+
title: "Allergen information",
|
|
5722
6401
|
slug: "allergens",
|
|
5723
|
-
content: "Our kitchen handles wheat, dairy, eggs, and nuts. Cross-contamination is possible.",
|
|
6402
|
+
content: "Our kitchen handles wheat, dairy, eggs, and nuts. Cross-contamination is possible \u2014 let us know about severe allergies before ordering.",
|
|
5724
6403
|
category: "products",
|
|
5725
6404
|
tags: ["allergens", "safety"],
|
|
5726
6405
|
created_at: ISO,
|
|
@@ -5728,7 +6407,7 @@ function seedDefault(registry) {
|
|
|
5728
6407
|
}
|
|
5729
6408
|
];
|
|
5730
6409
|
for (const a of articles) registry.knowledgeArticles.put(a.id, a);
|
|
5731
|
-
return { businessId };
|
|
6410
|
+
return { businessId: BUSINESS_ID };
|
|
5732
6411
|
}
|
|
5733
6412
|
|
|
5734
6413
|
// src/mock/seeds/empty.ts
|
|
@@ -5774,7 +6453,7 @@ function seedReesa(registry) {
|
|
|
5774
6453
|
default_address: "5 Allen Avenue, Ikeja, Lagos",
|
|
5775
6454
|
default_offers_table_service: false,
|
|
5776
6455
|
default_accepts_online_orders: true,
|
|
5777
|
-
image: "
|
|
6456
|
+
image: "/img/reesa-logo.png",
|
|
5778
6457
|
status: "active",
|
|
5779
6458
|
created_at: ISO3,
|
|
5780
6459
|
updated_at: ISO3,
|
|
@@ -5830,8 +6509,8 @@ function seedReesa(registry) {
|
|
|
5830
6509
|
product_type: "product",
|
|
5831
6510
|
base_price: p.price,
|
|
5832
6511
|
currency: "NGN",
|
|
5833
|
-
image:
|
|
5834
|
-
images: [
|
|
6512
|
+
image: `/img/reesa/${p.slug}.jpg`,
|
|
6513
|
+
images: [`/img/reesa/${p.slug}.jpg`],
|
|
5835
6514
|
is_available: true,
|
|
5836
6515
|
category_ids: [catId],
|
|
5837
6516
|
collection_ids: [],
|
|
@@ -6054,8 +6733,8 @@ function seedReesa(registry) {
|
|
|
6054
6733
|
product_type: "bundle",
|
|
6055
6734
|
base_price: "55000.00",
|
|
6056
6735
|
currency: "NGN",
|
|
6057
|
-
image: "
|
|
6058
|
-
images: ["
|
|
6736
|
+
image: "/img/reesa/owambe.jpg",
|
|
6737
|
+
images: ["/img/reesa/owambe.jpg"],
|
|
6059
6738
|
is_available: true,
|
|
6060
6739
|
category_ids: [catIdsBySlug.get("apparel")],
|
|
6061
6740
|
collection_ids: [],
|
|
@@ -6186,7 +6865,7 @@ function seedRestaurant(registry) {
|
|
|
6186
6865
|
default_address: "8 Ring Road, East Legon, Accra",
|
|
6187
6866
|
default_offers_table_service: true,
|
|
6188
6867
|
default_accepts_online_orders: true,
|
|
6189
|
-
image: "
|
|
6868
|
+
image: "/img/mamas-logo.png",
|
|
6190
6869
|
status: "active",
|
|
6191
6870
|
created_at: ISO4,
|
|
6192
6871
|
updated_at: ISO4,
|
|
@@ -6257,8 +6936,8 @@ function seedRestaurant(registry) {
|
|
|
6257
6936
|
product_type: "product",
|
|
6258
6937
|
base_price: p.price,
|
|
6259
6938
|
currency: "GHS",
|
|
6260
|
-
image:
|
|
6261
|
-
images: [
|
|
6939
|
+
image: `/img/mamas/${p.slug}.jpg`,
|
|
6940
|
+
images: [`/img/mamas/${p.slug}.jpg`],
|
|
6262
6941
|
is_available: true,
|
|
6263
6942
|
category_ids: [catId],
|
|
6264
6943
|
collection_ids: [],
|
|
@@ -6496,8 +7175,8 @@ function seedRestaurant(registry) {
|
|
|
6496
7175
|
product_type: "bundle",
|
|
6497
7176
|
base_price: "180.00",
|
|
6498
7177
|
currency: "GHS",
|
|
6499
|
-
image: "
|
|
6500
|
-
images: ["
|
|
7178
|
+
image: "/img/mamas/feast.jpg",
|
|
7179
|
+
images: ["/img/mamas/feast.jpg"],
|
|
6501
7180
|
is_available: true,
|
|
6502
7181
|
category_ids: [catIds.get("mains")],
|
|
6503
7182
|
collection_ids: [],
|
|
@@ -6532,8 +7211,8 @@ function seedRestaurant(registry) {
|
|
|
6532
7211
|
product_type: "composite",
|
|
6533
7212
|
base_price: "0.00",
|
|
6534
7213
|
currency: "GHS",
|
|
6535
|
-
image: "
|
|
6536
|
-
images: ["
|
|
7214
|
+
image: "/img/mamas/byo.jpg",
|
|
7215
|
+
images: ["/img/mamas/byo.jpg"],
|
|
6537
7216
|
is_available: true,
|
|
6538
7217
|
category_ids: [catIds.get("mains")],
|
|
6539
7218
|
collection_ids: [],
|
|
@@ -6645,7 +7324,7 @@ function seedRetail(registry) {
|
|
|
6645
7324
|
default_address: "Atomic Junction, Accra",
|
|
6646
7325
|
default_offers_table_service: false,
|
|
6647
7326
|
default_accepts_online_orders: true,
|
|
6648
|
-
image: "
|
|
7327
|
+
image: "/img/currents-logo.png",
|
|
6649
7328
|
status: "active",
|
|
6650
7329
|
created_at: ISO5,
|
|
6651
7330
|
updated_at: ISO5,
|
|
@@ -6715,8 +7394,8 @@ function seedRetail(registry) {
|
|
|
6715
7394
|
product_type: "product",
|
|
6716
7395
|
base_price: p.price,
|
|
6717
7396
|
currency: "GHS",
|
|
6718
|
-
image:
|
|
6719
|
-
images: [
|
|
7397
|
+
image: `/img/currents/${p.slug}.jpg`,
|
|
7398
|
+
images: [`/img/currents/${p.slug}.jpg`],
|
|
6720
7399
|
is_available: true,
|
|
6721
7400
|
category_ids: [catId],
|
|
6722
7401
|
collection_ids: [],
|
|
@@ -6979,8 +7658,8 @@ function seedRetail(registry) {
|
|
|
6979
7658
|
product_type: "bundle",
|
|
6980
7659
|
base_price: "11910.00",
|
|
6981
7660
|
currency: "GHS",
|
|
6982
|
-
image: "
|
|
6983
|
-
images: ["
|
|
7661
|
+
image: "/img/currents/wfh.jpg",
|
|
7662
|
+
images: ["/img/currents/wfh.jpg"],
|
|
6984
7663
|
is_available: true,
|
|
6985
7664
|
category_ids: [catIds.get("laptops")],
|
|
6986
7665
|
collection_ids: [],
|
|
@@ -7013,8 +7692,8 @@ function seedRetail(registry) {
|
|
|
7013
7692
|
product_type: "bundle",
|
|
7014
7693
|
base_price: "4200.00",
|
|
7015
7694
|
currency: "GHS",
|
|
7016
|
-
image: "
|
|
7017
|
-
images: ["
|
|
7695
|
+
image: "/img/currents/game-pack.jpg",
|
|
7696
|
+
images: ["/img/currents/game-pack.jpg"],
|
|
7018
7697
|
is_available: true,
|
|
7019
7698
|
category_ids: [catIds.get("gaming")],
|
|
7020
7699
|
collection_ids: [],
|
|
@@ -7117,7 +7796,7 @@ function seedServices(registry) {
|
|
|
7117
7796
|
default_address: "12 Cantonments Road, Accra",
|
|
7118
7797
|
default_offers_table_service: false,
|
|
7119
7798
|
default_accepts_online_orders: true,
|
|
7120
|
-
image: "
|
|
7799
|
+
image: "/img/serene-logo.png",
|
|
7121
7800
|
status: "active",
|
|
7122
7801
|
created_at: ISO6,
|
|
7123
7802
|
updated_at: ISO6,
|
|
@@ -7185,8 +7864,8 @@ function seedServices(registry) {
|
|
|
7185
7864
|
product_type: "service",
|
|
7186
7865
|
base_price: s.price,
|
|
7187
7866
|
currency: "GHS",
|
|
7188
|
-
image:
|
|
7189
|
-
images: [
|
|
7867
|
+
image: `/img/serene/${s.slug}.jpg`,
|
|
7868
|
+
images: [`/img/serene/${s.slug}.jpg`],
|
|
7190
7869
|
is_available: true,
|
|
7191
7870
|
category_ids: [catId],
|
|
7192
7871
|
collection_ids: [],
|
|
@@ -7322,8 +8001,8 @@ function seedServices(registry) {
|
|
|
7322
8001
|
product_type: "bundle",
|
|
7323
8002
|
base_price: "480.00",
|
|
7324
8003
|
currency: "GHS",
|
|
7325
|
-
image: "
|
|
7326
|
-
images: ["
|
|
8004
|
+
image: "/img/serene/spa-day.jpg",
|
|
8005
|
+
images: ["/img/serene/spa-day.jpg"],
|
|
7327
8006
|
is_available: true,
|
|
7328
8007
|
category_ids: [catIds.get("wellness")],
|
|
7329
8008
|
collection_ids: [],
|
|
@@ -7356,8 +8035,8 @@ function seedServices(registry) {
|
|
|
7356
8035
|
product_type: "bundle",
|
|
7357
8036
|
base_price: "850.50",
|
|
7358
8037
|
currency: "GHS",
|
|
7359
|
-
image: "
|
|
7360
|
-
images: ["
|
|
8038
|
+
image: "/img/serene/couples.jpg",
|
|
8039
|
+
images: ["/img/serene/couples.jpg"],
|
|
7361
8040
|
is_available: true,
|
|
7362
8041
|
category_ids: [catIds.get("wellness")],
|
|
7363
8042
|
collection_ids: [],
|
|
@@ -7392,8 +8071,8 @@ function seedServices(registry) {
|
|
|
7392
8071
|
product_type: "composite",
|
|
7393
8072
|
base_price: "0.00",
|
|
7394
8073
|
currency: "GHS",
|
|
7395
|
-
image: "
|
|
7396
|
-
images: ["
|
|
8074
|
+
image: "/img/serene/custom.jpg",
|
|
8075
|
+
images: ["/img/serene/custom.jpg"],
|
|
7397
8076
|
is_available: true,
|
|
7398
8077
|
category_ids: [catIds.get("wellness")],
|
|
7399
8078
|
collection_ids: [],
|
|
@@ -7496,7 +8175,7 @@ function seedGrocery(registry) {
|
|
|
7496
8175
|
default_address: "Spintex Road, Accra",
|
|
7497
8176
|
default_offers_table_service: false,
|
|
7498
8177
|
default_accepts_online_orders: true,
|
|
7499
|
-
image: "
|
|
8178
|
+
image: "/img/freshmart-logo.png",
|
|
7500
8179
|
status: "active",
|
|
7501
8180
|
created_at: ISO7,
|
|
7502
8181
|
updated_at: ISO7,
|
|
@@ -7571,8 +8250,8 @@ function seedGrocery(registry) {
|
|
|
7571
8250
|
product_type: "product",
|
|
7572
8251
|
base_price: p.price,
|
|
7573
8252
|
currency: "GHS",
|
|
7574
|
-
image:
|
|
7575
|
-
images: [
|
|
8253
|
+
image: `/img/freshmart/${p.slug}.jpg`,
|
|
8254
|
+
images: [`/img/freshmart/${p.slug}.jpg`],
|
|
7576
8255
|
is_available: true,
|
|
7577
8256
|
category_ids: [catId],
|
|
7578
8257
|
collection_ids: [],
|
|
@@ -7655,8 +8334,8 @@ function seedGrocery(registry) {
|
|
|
7655
8334
|
product_type: "bundle",
|
|
7656
8335
|
base_price: "199.50",
|
|
7657
8336
|
currency: "GHS",
|
|
7658
|
-
image: "
|
|
7659
|
-
images: ["
|
|
8337
|
+
image: "/img/freshmart/essentials.jpg",
|
|
8338
|
+
images: ["/img/freshmart/essentials.jpg"],
|
|
7660
8339
|
is_available: true,
|
|
7661
8340
|
category_ids: [catIds.get("pantry")],
|
|
7662
8341
|
collection_ids: [],
|
|
@@ -7831,43 +8510,47 @@ function createDeps(options = {}) {
|
|
|
7831
8510
|
}
|
|
7832
8511
|
|
|
7833
8512
|
// src/mock/server.ts
|
|
8513
|
+
var PACKAGE_NAME = "@cimplify/sdk/mock";
|
|
8514
|
+
function corsMiddleware(allow) {
|
|
8515
|
+
return async (c, next) => {
|
|
8516
|
+
const origin = c.req.header("origin") ?? "";
|
|
8517
|
+
const allowOrigin = allow === "*" ? "*" : allow.includes(origin) ? origin : allow[0] ?? "*";
|
|
8518
|
+
const headers = {
|
|
8519
|
+
"Access-Control-Allow-Origin": allowOrigin,
|
|
8520
|
+
"Access-Control-Allow-Credentials": "true",
|
|
8521
|
+
"Access-Control-Allow-Headers": c.req.header("access-control-request-headers") ?? "content-type,authorization,x-session-token,idempotency-key",
|
|
8522
|
+
"Access-Control-Allow-Methods": "GET,POST,PATCH,DELETE,OPTIONS",
|
|
8523
|
+
"Access-Control-Expose-Headers": "x-session-token",
|
|
8524
|
+
"Access-Control-Max-Age": "86400",
|
|
8525
|
+
Vary: "Origin"
|
|
8526
|
+
};
|
|
8527
|
+
if (c.req.method === "OPTIONS") {
|
|
8528
|
+
return new Response(null, { status: 204, headers });
|
|
8529
|
+
}
|
|
8530
|
+
for (const [k, v] of Object.entries(headers)) c.header(k, v);
|
|
8531
|
+
await next();
|
|
8532
|
+
};
|
|
8533
|
+
}
|
|
7834
8534
|
function createMockApp(options = {}) {
|
|
7835
8535
|
const deps = createDeps(options);
|
|
7836
8536
|
const app = new Hono2();
|
|
7837
|
-
const
|
|
7838
|
-
if (
|
|
7839
|
-
app.use("*", async (c, next) => {
|
|
7840
|
-
const origin = c.req.header("origin") ?? "";
|
|
7841
|
-
const allow = corsOrigins === "*" ? "*" : corsOrigins.includes(origin) ? origin : corsOrigins[0] ?? "*";
|
|
7842
|
-
const headers = {
|
|
7843
|
-
"Access-Control-Allow-Origin": allow,
|
|
7844
|
-
"Access-Control-Allow-Credentials": "true",
|
|
7845
|
-
"Access-Control-Allow-Headers": c.req.header("access-control-request-headers") ?? "content-type,authorization,x-session-token,idempotency-key",
|
|
7846
|
-
"Access-Control-Allow-Methods": "GET,POST,PATCH,DELETE,OPTIONS",
|
|
7847
|
-
"Access-Control-Expose-Headers": "x-session-token",
|
|
7848
|
-
"Access-Control-Max-Age": "86400",
|
|
7849
|
-
Vary: "Origin"
|
|
7850
|
-
};
|
|
7851
|
-
if (c.req.method === "OPTIONS") {
|
|
7852
|
-
return new Response(null, { status: 204, headers });
|
|
7853
|
-
}
|
|
7854
|
-
for (const [k, v] of Object.entries(headers)) c.header(k, v);
|
|
7855
|
-
await next();
|
|
7856
|
-
});
|
|
7857
|
-
}
|
|
8537
|
+
const cors = options.cors ?? "*";
|
|
8538
|
+
if (cors) app.use("*", corsMiddleware(cors));
|
|
7858
8539
|
app.get(
|
|
7859
8540
|
"/",
|
|
7860
8541
|
(c) => c.json({
|
|
7861
|
-
name:
|
|
7862
|
-
version: "0.36.0",
|
|
8542
|
+
name: PACKAGE_NAME,
|
|
7863
8543
|
docs: "https://github.com/cimplify/cimplify",
|
|
7864
8544
|
api: "/api/v1",
|
|
7865
8545
|
admin: "/_mock",
|
|
7866
8546
|
elements: "/elements",
|
|
7867
|
-
|
|
8547
|
+
images: "/img/<slug>",
|
|
8548
|
+
seed_business_id: deps.defaultBusinessId,
|
|
8549
|
+
time: deps.clock.iso()
|
|
7868
8550
|
})
|
|
7869
8551
|
);
|
|
7870
8552
|
app.get("/healthz", (c) => c.json({ ok: true, time: deps.clock.iso() }));
|
|
8553
|
+
mountImageRoutes(app);
|
|
7871
8554
|
app.route("/api/v1", buildV1(deps));
|
|
7872
8555
|
app.route("/_mock", buildAdmin(deps));
|
|
7873
8556
|
app.route("/elements", buildElements(deps));
|
|
@@ -7875,7 +8558,11 @@ function createMockApp(options = {}) {
|
|
|
7875
8558
|
(c) => c.json(
|
|
7876
8559
|
{
|
|
7877
8560
|
success: false,
|
|
7878
|
-
error: {
|
|
8561
|
+
error: {
|
|
8562
|
+
code: "ROUTE_NOT_REGISTERED",
|
|
8563
|
+
message: `Route ${c.req.method} ${c.req.path} not handled by mock`,
|
|
8564
|
+
retryable: false
|
|
8565
|
+
},
|
|
7879
8566
|
hint: "See /api/v1 for the SDK surface and /_mock for the admin surface."
|
|
7880
8567
|
},
|
|
7881
8568
|
501
|