@cimplify/sdk 0.40.0 → 0.41.0
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 +3 -2
- package/dist/mock/cli.mjs +30 -6
- package/dist/mock/library.js +30 -6
- package/dist/mock/library.mjs +30 -6
- package/dist/mock/msw.js +30 -6
- package/dist/mock/msw.mjs +30 -6
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/templates/storefront-bakery/.env.example +5 -0
- package/templates/storefront-bakery/AGENTS.md +57 -0
- package/templates/storefront-bakery/app/about/page.tsx +21 -44
- package/templates/storefront-bakery/app/categories/[slug]/page.tsx +2 -1
- package/templates/storefront-bakery/app/collections/[slug]/page.tsx +2 -1
- package/templates/storefront-bakery/app/error.tsx +60 -0
- package/templates/storefront-bakery/app/faq/page.tsx +13 -89
- package/templates/storefront-bakery/app/layout.tsx +37 -2
- package/templates/storefront-bakery/app/llms.txt/route.ts +94 -0
- package/templates/storefront-bakery/app/not-found.tsx +39 -0
- package/templates/storefront-bakery/app/page.tsx +6 -6
- package/templates/storefront-bakery/app/privacy/page.tsx +24 -68
- package/templates/storefront-bakery/app/robots.ts +18 -0
- package/templates/storefront-bakery/app/shop/page.tsx +3 -2
- package/templates/storefront-bakery/app/sitemap.ts +62 -0
- package/templates/storefront-bakery/app/terms/page.tsx +24 -82
- package/templates/storefront-bakery/components/footer.tsx +88 -130
- package/templates/storefront-bakery/components/header.tsx +8 -16
- package/templates/storefront-bakery/lib/brand.ts +453 -0
- package/templates/storefront-fashion/.env.example +16 -0
- package/templates/storefront-fashion/AGENTS.md +52 -0
- package/templates/storefront-fashion/README.md +77 -0
- package/templates/storefront-fashion/app/about/page.tsx +41 -0
- package/templates/storefront-fashion/app/cart/page.tsx +9 -0
- package/templates/storefront-fashion/app/categories/[slug]/listing-client.tsx +19 -0
- package/templates/storefront-fashion/app/categories/[slug]/page.tsx +130 -0
- package/templates/storefront-fashion/app/checkout/page.tsx +17 -0
- package/templates/storefront-fashion/app/collections/[slug]/listing-client.tsx +20 -0
- package/templates/storefront-fashion/app/collections/[slug]/page.tsx +130 -0
- package/templates/storefront-fashion/app/error.tsx +61 -0
- package/templates/storefront-fashion/app/faq/page.tsx +46 -0
- package/templates/storefront-fashion/app/globals.css +46 -0
- package/templates/storefront-fashion/app/layout.tsx +76 -0
- package/templates/storefront-fashion/app/llms.txt/route.ts +94 -0
- package/templates/storefront-fashion/app/not-found.tsx +39 -0
- package/templates/storefront-fashion/app/orders/[id]/page.tsx +24 -0
- package/templates/storefront-fashion/app/page.tsx +183 -0
- package/templates/storefront-fashion/app/privacy/page.tsx +44 -0
- package/templates/storefront-fashion/app/products/[slug]/page.tsx +165 -0
- package/templates/storefront-fashion/app/products/[slug]/product-detail.tsx +71 -0
- package/templates/storefront-fashion/app/robots.ts +18 -0
- package/templates/storefront-fashion/app/shop/page.tsx +63 -0
- package/templates/storefront-fashion/app/shop/shop-client.tsx +32 -0
- package/templates/storefront-fashion/app/sitemap.ts +59 -0
- package/templates/storefront-fashion/app/terms/page.tsx +44 -0
- package/templates/storefront-fashion/components/brand-marquee.tsx +27 -0
- package/templates/storefront-fashion/components/cart-pill.tsx +32 -0
- package/templates/storefront-fashion/components/category-grid.tsx +28 -0
- package/templates/storefront-fashion/components/category-tiles.tsx +104 -0
- package/templates/storefront-fashion/components/collection-strip.tsx +45 -0
- package/templates/storefront-fashion/components/feature-hero.tsx +82 -0
- package/templates/storefront-fashion/components/footer.tsx +153 -0
- package/templates/storefront-fashion/components/header.tsx +43 -0
- package/templates/storefront-fashion/components/hero.tsx +28 -0
- package/templates/storefront-fashion/components/nav-link.tsx +20 -0
- package/templates/storefront-fashion/components/newsletter.tsx +50 -0
- package/templates/storefront-fashion/components/promo-banner.tsx +41 -0
- package/templates/storefront-fashion/components/providers.tsx +31 -0
- package/templates/storefront-fashion/components/section-heading.tsx +37 -0
- package/templates/storefront-fashion/components/store-product-card.tsx +83 -0
- package/templates/storefront-fashion/components/trade-in-cta.tsx +54 -0
- package/templates/storefront-fashion/components/trust-bar.tsx +66 -0
- package/templates/storefront-fashion/lib/brand.ts +421 -0
- package/templates/storefront-fashion/lib/cart.ts +27 -0
- package/templates/storefront-fashion/next.config.ts +40 -0
- package/templates/storefront-fashion/package.json +28 -0
- package/templates/storefront-fashion/postcss.config.mjs +7 -0
- package/templates/storefront-fashion/tsconfig.json +23 -0
- package/templates/storefront-grocery/.env.example +16 -0
- package/templates/storefront-grocery/AGENTS.md +39 -0
- package/templates/storefront-grocery/README.md +73 -0
- package/templates/storefront-grocery/app/about/page.tsx +38 -0
- package/templates/storefront-grocery/app/cart/page.tsx +9 -0
- package/templates/storefront-grocery/app/categories/[slug]/listing-client.tsx +19 -0
- package/templates/storefront-grocery/app/categories/[slug]/page.tsx +118 -0
- package/templates/storefront-grocery/app/checkout/page.tsx +17 -0
- package/templates/storefront-grocery/app/collections/[slug]/listing-client.tsx +20 -0
- package/templates/storefront-grocery/app/collections/[slug]/page.tsx +118 -0
- package/templates/storefront-grocery/app/error.tsx +60 -0
- package/templates/storefront-grocery/app/faq/page.tsx +46 -0
- package/templates/storefront-grocery/app/globals.css +45 -0
- package/templates/storefront-grocery/app/layout.tsx +75 -0
- package/templates/storefront-grocery/app/llms.txt/route.ts +94 -0
- package/templates/storefront-grocery/app/not-found.tsx +39 -0
- package/templates/storefront-grocery/app/orders/[id]/page.tsx +21 -0
- package/templates/storefront-grocery/app/page.tsx +97 -0
- package/templates/storefront-grocery/app/privacy/page.tsx +44 -0
- package/templates/storefront-grocery/app/robots.ts +18 -0
- package/templates/storefront-grocery/app/shop/page.tsx +31 -0
- package/templates/storefront-grocery/app/shop/shop-client.tsx +27 -0
- package/templates/storefront-grocery/app/sitemap.ts +62 -0
- package/templates/storefront-grocery/app/terms/page.tsx +44 -0
- package/templates/storefront-grocery/components/cart-pill.tsx +32 -0
- package/templates/storefront-grocery/components/category-grid.tsx +28 -0
- package/templates/storefront-grocery/components/collection-strip.tsx +45 -0
- package/templates/storefront-grocery/components/footer.tsx +148 -0
- package/templates/storefront-grocery/components/header.tsx +43 -0
- package/templates/storefront-grocery/components/hero.tsx +25 -0
- package/templates/storefront-grocery/components/nav-link.tsx +20 -0
- package/templates/storefront-grocery/components/providers.tsx +31 -0
- package/templates/storefront-grocery/components/store-product-card.tsx +83 -0
- package/templates/storefront-grocery/lib/brand.ts +284 -0
- package/templates/storefront-grocery/lib/cart.ts +27 -0
- package/templates/storefront-grocery/next.config.ts +40 -0
- package/templates/storefront-grocery/package.json +28 -0
- package/templates/storefront-grocery/postcss.config.mjs +7 -0
- package/templates/storefront-grocery/tsconfig.json +23 -0
- package/templates/storefront-restaurant/.env.example +16 -0
- package/templates/storefront-restaurant/AGENTS.md +39 -0
- package/templates/storefront-restaurant/README.md +73 -0
- package/templates/storefront-restaurant/app/about/page.tsx +38 -0
- package/templates/storefront-restaurant/app/cart/page.tsx +9 -0
- package/templates/storefront-restaurant/app/categories/[slug]/listing-client.tsx +19 -0
- package/templates/storefront-restaurant/app/categories/[slug]/page.tsx +118 -0
- package/templates/storefront-restaurant/app/checkout/page.tsx +17 -0
- package/templates/storefront-restaurant/app/collections/[slug]/listing-client.tsx +20 -0
- package/templates/storefront-restaurant/app/collections/[slug]/page.tsx +118 -0
- package/templates/storefront-restaurant/app/error.tsx +60 -0
- package/templates/storefront-restaurant/app/faq/page.tsx +46 -0
- package/templates/storefront-restaurant/app/globals.css +44 -0
- package/templates/storefront-restaurant/app/layout.tsx +80 -0
- package/templates/storefront-restaurant/app/llms.txt/route.ts +94 -0
- package/templates/storefront-restaurant/app/not-found.tsx +39 -0
- package/templates/storefront-restaurant/app/orders/[id]/page.tsx +21 -0
- package/templates/storefront-restaurant/app/page.tsx +97 -0
- package/templates/storefront-restaurant/app/privacy/page.tsx +44 -0
- package/templates/storefront-restaurant/app/robots.ts +18 -0
- package/templates/storefront-restaurant/app/shop/page.tsx +31 -0
- package/templates/storefront-restaurant/app/shop/shop-client.tsx +27 -0
- package/templates/storefront-restaurant/app/sitemap.ts +62 -0
- package/templates/storefront-restaurant/app/terms/page.tsx +44 -0
- package/templates/storefront-restaurant/components/cart-pill.tsx +32 -0
- package/templates/storefront-restaurant/components/category-grid.tsx +28 -0
- package/templates/storefront-restaurant/components/collection-strip.tsx +45 -0
- package/templates/storefront-restaurant/components/footer.tsx +148 -0
- package/templates/storefront-restaurant/components/header.tsx +43 -0
- package/templates/storefront-restaurant/components/hero.tsx +25 -0
- package/templates/storefront-restaurant/components/nav-link.tsx +20 -0
- package/templates/storefront-restaurant/components/product-modal.tsx +105 -0
- package/templates/storefront-restaurant/components/providers.tsx +31 -0
- package/templates/storefront-restaurant/components/store-product-card.tsx +83 -0
- package/templates/storefront-restaurant/lib/brand.ts +287 -0
- package/templates/storefront-restaurant/lib/cart.ts +27 -0
- package/templates/storefront-restaurant/next.config.ts +40 -0
- package/templates/storefront-restaurant/package.json +28 -0
- package/templates/storefront-restaurant/postcss.config.mjs +7 -0
- package/templates/storefront-restaurant/tsconfig.json +23 -0
- package/templates/storefront-retail/.env.example +5 -0
- package/templates/storefront-retail/AGENTS.md +64 -0
- package/templates/storefront-retail/app/about/page.tsx +24 -54
- package/templates/storefront-retail/app/categories/[slug]/page.tsx +50 -37
- package/templates/storefront-retail/app/collections/[slug]/page.tsx +50 -37
- package/templates/storefront-retail/app/error.tsx +61 -0
- package/templates/storefront-retail/app/faq/page.tsx +11 -92
- package/templates/storefront-retail/app/layout.tsx +37 -8
- package/templates/storefront-retail/app/llms.txt/route.ts +94 -0
- package/templates/storefront-retail/app/not-found.tsx +39 -0
- package/templates/storefront-retail/app/page.tsx +111 -26
- package/templates/storefront-retail/app/privacy/page.tsx +24 -81
- package/templates/storefront-retail/app/products/[slug]/page.tsx +165 -0
- package/templates/storefront-retail/app/products/[slug]/product-detail.tsx +71 -0
- package/templates/storefront-retail/app/robots.ts +18 -0
- package/templates/storefront-retail/app/shop/page.tsx +36 -3
- package/templates/storefront-retail/app/shop/shop-client.tsx +6 -1
- package/templates/storefront-retail/app/sitemap.ts +59 -0
- package/templates/storefront-retail/app/terms/page.tsx +24 -97
- package/templates/storefront-retail/components/brand-marquee.tsx +27 -0
- package/templates/storefront-retail/components/category-tiles.tsx +104 -0
- package/templates/storefront-retail/components/feature-hero.tsx +84 -0
- package/templates/storefront-retail/components/footer.tsx +85 -120
- package/templates/storefront-retail/components/header.tsx +10 -12
- package/templates/storefront-retail/components/newsletter.tsx +50 -0
- package/templates/storefront-retail/components/promo-banner.tsx +41 -0
- package/templates/storefront-retail/components/section-heading.tsx +37 -0
- package/templates/storefront-retail/components/store-product-card.tsx +7 -7
- package/templates/storefront-retail/components/trade-in-cta.tsx +54 -0
- package/templates/storefront-retail/components/trust-bar.tsx +66 -0
- package/templates/storefront-retail/lib/brand.ts +567 -0
- package/templates/storefront-services/.env.example +16 -0
- package/templates/storefront-services/AGENTS.md +41 -0
- package/templates/storefront-services/README.md +73 -0
- package/templates/storefront-services/app/about/page.tsx +38 -0
- package/templates/storefront-services/app/cart/page.tsx +9 -0
- package/templates/storefront-services/app/categories/[slug]/listing-client.tsx +19 -0
- package/templates/storefront-services/app/categories/[slug]/page.tsx +118 -0
- package/templates/storefront-services/app/checkout/page.tsx +17 -0
- package/templates/storefront-services/app/collections/[slug]/listing-client.tsx +20 -0
- package/templates/storefront-services/app/collections/[slug]/page.tsx +118 -0
- package/templates/storefront-services/app/error.tsx +60 -0
- package/templates/storefront-services/app/faq/page.tsx +46 -0
- package/templates/storefront-services/app/globals.css +45 -0
- package/templates/storefront-services/app/layout.tsx +80 -0
- package/templates/storefront-services/app/llms.txt/route.ts +94 -0
- package/templates/storefront-services/app/not-found.tsx +39 -0
- package/templates/storefront-services/app/orders/[id]/page.tsx +21 -0
- package/templates/storefront-services/app/page.tsx +97 -0
- package/templates/storefront-services/app/privacy/page.tsx +44 -0
- package/templates/storefront-services/app/robots.ts +18 -0
- package/templates/storefront-services/app/shop/page.tsx +31 -0
- package/templates/storefront-services/app/shop/shop-client.tsx +27 -0
- package/templates/storefront-services/app/sitemap.ts +62 -0
- package/templates/storefront-services/app/terms/page.tsx +44 -0
- package/templates/storefront-services/components/cart-pill.tsx +32 -0
- package/templates/storefront-services/components/category-grid.tsx +28 -0
- package/templates/storefront-services/components/collection-strip.tsx +45 -0
- package/templates/storefront-services/components/footer.tsx +148 -0
- package/templates/storefront-services/components/header.tsx +43 -0
- package/templates/storefront-services/components/hero.tsx +25 -0
- package/templates/storefront-services/components/nav-link.tsx +20 -0
- package/templates/storefront-services/components/product-modal.tsx +105 -0
- package/templates/storefront-services/components/providers.tsx +31 -0
- package/templates/storefront-services/components/store-product-card.tsx +83 -0
- package/templates/storefront-services/lib/brand.ts +303 -0
- package/templates/storefront-services/lib/cart.ts +27 -0
- package/templates/storefront-services/next.config.ts +40 -0
- package/templates/storefront-services/package.json +28 -0
- package/templates/storefront-services/postcss.config.mjs +7 -0
- package/templates/storefront-services/tsconfig.json +23 -0
- /package/templates/{storefront-retail → storefront-grocery}/components/product-modal.tsx +0 -0
|
@@ -2,10 +2,11 @@ import type { Metadata } from "next";
|
|
|
2
2
|
import { cacheTag, cacheLife } from "next/cache";
|
|
3
3
|
import { getServerClient, tags } from "@cimplify/sdk/server";
|
|
4
4
|
import { ShopClient } from "./shop-client";
|
|
5
|
+
import { brand } from "@/lib/brand";
|
|
5
6
|
|
|
6
7
|
export const metadata: Metadata = {
|
|
7
|
-
title:
|
|
8
|
-
description:
|
|
8
|
+
title: `Shop — ${brand.name}`,
|
|
9
|
+
description: brand.description,
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
async function getShopData() {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { MetadataRoute } from "next";
|
|
2
|
+
import { getServerClient, type Product } from "@cimplify/sdk/server";
|
|
3
|
+
|
|
4
|
+
const SITE_URL =
|
|
5
|
+
process.env.NEXT_PUBLIC_SITE_URL?.trim() || "https://example.com";
|
|
6
|
+
|
|
7
|
+
const STATIC_ROUTES: { path: string; priority: number; changeFrequency: "daily" | "weekly" | "monthly" }[] = [
|
|
8
|
+
{ path: "/", priority: 1.0, changeFrequency: "daily" },
|
|
9
|
+
{ path: "/shop", priority: 0.9, changeFrequency: "daily" },
|
|
10
|
+
{ path: "/about", priority: 0.5, changeFrequency: "monthly" },
|
|
11
|
+
{ path: "/faq", priority: 0.4, changeFrequency: "monthly" },
|
|
12
|
+
{ path: "/terms", priority: 0.2, changeFrequency: "monthly" },
|
|
13
|
+
{ path: "/privacy", priority: 0.2, changeFrequency: "monthly" },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
17
|
+
const now = new Date();
|
|
18
|
+
const client = getServerClient();
|
|
19
|
+
|
|
20
|
+
const [productsRes, categoriesRes, collectionsRes] = await Promise.all([
|
|
21
|
+
client.catalogue.getProducts({ limit: 500 }),
|
|
22
|
+
client.catalogue.getCategories(),
|
|
23
|
+
client.catalogue.getCollections(),
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
const products = productsRes.ok ? productsRes.value.items : [];
|
|
27
|
+
const categories = categoriesRes.ok ? categoriesRes.value : [];
|
|
28
|
+
const collections = collectionsRes.ok ? collectionsRes.value : [];
|
|
29
|
+
|
|
30
|
+
const staticEntries: MetadataRoute.Sitemap = STATIC_ROUTES.map((r) => ({
|
|
31
|
+
url: `${SITE_URL}${r.path}`,
|
|
32
|
+
lastModified: now,
|
|
33
|
+
changeFrequency: r.changeFrequency,
|
|
34
|
+
priority: r.priority,
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
// Bakery uses a URL-driven product modal (`?product=<slug>` on the home or
|
|
38
|
+
// shop pages). Emit those as deep-linkable URLs so search engines and LLMs
|
|
39
|
+
// can index each product canonically.
|
|
40
|
+
const productEntries: MetadataRoute.Sitemap = products.map((p: Product) => ({
|
|
41
|
+
url: `${SITE_URL}/shop?product=${encodeURIComponent(p.slug ?? p.id)}`,
|
|
42
|
+
lastModified: p.updated_at ? new Date(p.updated_at) : now,
|
|
43
|
+
changeFrequency: "weekly",
|
|
44
|
+
priority: 0.7,
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
const categoryEntries: MetadataRoute.Sitemap = categories.map((c) => ({
|
|
48
|
+
url: `${SITE_URL}/categories/${c.slug}`,
|
|
49
|
+
lastModified: now,
|
|
50
|
+
changeFrequency: "weekly",
|
|
51
|
+
priority: 0.6,
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
const collectionEntries: MetadataRoute.Sitemap = collections.map((c) => ({
|
|
55
|
+
url: `${SITE_URL}/collections/${c.slug}`,
|
|
56
|
+
lastModified: now,
|
|
57
|
+
changeFrequency: "weekly",
|
|
58
|
+
priority: 0.6,
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
return [...staticEntries, ...categoryEntries, ...collectionEntries, ...productEntries];
|
|
62
|
+
}
|
|
@@ -1,101 +1,43 @@
|
|
|
1
1
|
import type { Metadata } from "next";
|
|
2
|
+
import { brand } from "@/lib/brand";
|
|
2
3
|
|
|
3
4
|
export const metadata: Metadata = {
|
|
4
|
-
title:
|
|
5
|
-
description:
|
|
5
|
+
title: `Terms of Service — ${brand.name}`,
|
|
6
|
+
description: `The rules of the road for ordering from ${brand.name}.`,
|
|
6
7
|
};
|
|
7
8
|
|
|
8
9
|
export default function TermsPage() {
|
|
10
|
+
const t = brand.terms;
|
|
9
11
|
return (
|
|
10
12
|
<article className="max-w-3xl mx-auto px-8 py-16 prose prose-lg max-w-none">
|
|
11
13
|
<p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-primary mb-2 not-prose">
|
|
12
|
-
|
|
14
|
+
{t.eyebrow}
|
|
13
15
|
</p>
|
|
14
16
|
<h1 className="font-serif text-[clamp(2.25rem,5vw,3.5rem)] font-semibold mb-2 -tracking-[0.02em]">
|
|
15
|
-
|
|
17
|
+
{t.title}
|
|
16
18
|
</h1>
|
|
17
19
|
<p className="text-sm text-muted-foreground not-prose mb-10">
|
|
18
|
-
Last updated:
|
|
20
|
+
Last updated: {t.lastUpdated}
|
|
19
21
|
</p>
|
|
20
22
|
|
|
21
23
|
<section className="space-y-5 leading-relaxed text-foreground/90">
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Payment is due at checkout via Mobile Money, card, or cash on delivery.
|
|
40
|
-
We reserve the right to correct typographical pricing errors before
|
|
41
|
-
fulfilment.
|
|
42
|
-
</p>
|
|
43
|
-
|
|
44
|
-
<h2 className="font-serif text-2xl font-semibold">4. Delivery and pickup</h2>
|
|
45
|
-
<p>
|
|
46
|
-
Delivery times are estimates, not guarantees. We're not liable for
|
|
47
|
-
delays caused by weather, traffic, or events outside our control. If
|
|
48
|
-
a rider can't reach you within 30 minutes of arrival, the order may be
|
|
49
|
-
returned to the kitchen — please make sure your address and phone are
|
|
50
|
-
correct.
|
|
51
|
-
</p>
|
|
52
|
-
|
|
53
|
-
<h2 className="font-serif text-2xl font-semibold">5. Custom cakes</h2>
|
|
54
|
-
<p>
|
|
55
|
-
Custom cake orders require a 50% deposit at booking and the balance
|
|
56
|
-
before pickup or dispatch. Cancellations more than 72 hours before the
|
|
57
|
-
event date receive a full refund of the deposit; within 72 hours,
|
|
58
|
-
the deposit is non-refundable.
|
|
59
|
-
</p>
|
|
60
|
-
|
|
61
|
-
<h2 className="font-serif text-2xl font-semibold">6. Allergens</h2>
|
|
62
|
-
<p>
|
|
63
|
-
Our kitchen handles wheat, dairy, eggs, and tree nuts. We can't
|
|
64
|
-
guarantee freedom from cross-contamination. Customers with severe
|
|
65
|
-
allergies order at their own risk.
|
|
66
|
-
</p>
|
|
67
|
-
|
|
68
|
-
<h2 className="font-serif text-2xl font-semibold">7. Returns</h2>
|
|
69
|
-
<p>
|
|
70
|
-
Baked goods are non-returnable for food-safety reasons. If something
|
|
71
|
-
is wrong with your order, contact us within 24 hours of delivery and
|
|
72
|
-
we'll replace it, refund it, or credit your account — your choice.
|
|
73
|
-
</p>
|
|
74
|
-
|
|
75
|
-
<h2 className="font-serif text-2xl font-semibold">8. Liability</h2>
|
|
76
|
-
<p>
|
|
77
|
-
To the maximum extent permitted by Ghanaian law, our liability for any
|
|
78
|
-
claim arising from this website or your order is limited to the value
|
|
79
|
-
of the order. We're not liable for indirect or consequential losses.
|
|
80
|
-
</p>
|
|
81
|
-
|
|
82
|
-
<h2 className="font-serif text-2xl font-semibold">9. Governing law</h2>
|
|
83
|
-
<p>
|
|
84
|
-
These terms are governed by the laws of Ghana. Disputes will be
|
|
85
|
-
resolved in the courts of Greater Accra.
|
|
86
|
-
</p>
|
|
87
|
-
|
|
88
|
-
<h2 className="font-serif text-2xl font-semibold">10. Contact</h2>
|
|
89
|
-
<p>
|
|
90
|
-
Questions? Email{" "}
|
|
91
|
-
<a
|
|
92
|
-
href="mailto:hello@akuasbakery.test"
|
|
93
|
-
className="text-primary font-semibold hover:underline"
|
|
94
|
-
>
|
|
95
|
-
hello@akuasbakery.test
|
|
96
|
-
</a>
|
|
97
|
-
.
|
|
98
|
-
</p>
|
|
24
|
+
{t.sections.map((s) => (
|
|
25
|
+
<div key={s.heading}>
|
|
26
|
+
<h2 className="font-serif text-2xl font-semibold mt-0">{s.heading}</h2>
|
|
27
|
+
{typeof s.body === "string" ? (
|
|
28
|
+
<p>{s.body}</p>
|
|
29
|
+
) : (
|
|
30
|
+
<>
|
|
31
|
+
<p>{s.body.intro}</p>
|
|
32
|
+
<ul className="list-disc pl-6 space-y-2">
|
|
33
|
+
{s.body.bullets.map((b) => (
|
|
34
|
+
<li key={b}>{b}</li>
|
|
35
|
+
))}
|
|
36
|
+
</ul>
|
|
37
|
+
</>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
))}
|
|
99
41
|
</section>
|
|
100
42
|
</article>
|
|
101
43
|
);
|
|
@@ -1,127 +1,84 @@
|
|
|
1
1
|
import Link from "next/link";
|
|
2
|
+
import { brand } from "@/lib/brand";
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
svg
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
label: "WhatsApp",
|
|
44
|
-
href: "https://wa.me/233244000000",
|
|
45
|
-
svg: (
|
|
46
|
-
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden className="w-5 h-5">
|
|
47
|
-
<path d="M12 2a10 10 0 0 0-8.6 15l-1.4 5 5.2-1.4A10 10 0 1 0 12 2zm5 14.2c-.2.6-1.2 1.2-1.7 1.2-.5.1-1.1.1-1.7-.1-.4-.1-.9-.3-1.5-.6a8.4 8.4 0 0 1-3.7-3.4c-.7-1-1-1.8-1-2.5 0-.7.4-1.1.6-1.3.2-.2.4-.2.5-.2h.4c.1 0 .3 0 .4.3l.6 1.4c.1.2 0 .3 0 .4l-.3.4-.3.3c-.1.1-.2.2-.1.4.2.4.7 1.1 1.4 1.8.9.8 1.7 1.1 1.9 1.2.2.1.3.1.5-.1l.6-.7c.2-.2.3-.2.5-.1l1.4.7c.2.1.3.2.4.3.1.2.1.6 0 1z" />
|
|
48
|
-
</svg>
|
|
49
|
-
),
|
|
50
|
-
},
|
|
51
|
-
];
|
|
4
|
+
const ICONS: Record<string, React.ReactNode> = {
|
|
5
|
+
instagram: (
|
|
6
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden className="w-5 h-5">
|
|
7
|
+
<rect x="3" y="3" width="18" height="18" rx="5" />
|
|
8
|
+
<circle cx="12" cy="12" r="4" />
|
|
9
|
+
<circle cx="17.5" cy="6.5" r="0.75" fill="currentColor" />
|
|
10
|
+
</svg>
|
|
11
|
+
),
|
|
12
|
+
x: (
|
|
13
|
+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden className="w-5 h-5">
|
|
14
|
+
<path d="M18 2h3l-7.5 8.6L22 22h-6.6l-5-6.5L4 22H1l8-9.2L1.4 2H8l4.6 6 5.4-6z" />
|
|
15
|
+
</svg>
|
|
16
|
+
),
|
|
17
|
+
tiktok: (
|
|
18
|
+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden className="w-5 h-5">
|
|
19
|
+
<path d="M16 3v3.5a4.5 4.5 0 0 0 4.5 4.5V14a7.5 7.5 0 0 1-4.5-1.5V16a5 5 0 1 1-5-5v3a2 2 0 1 0 2 2V3z" />
|
|
20
|
+
</svg>
|
|
21
|
+
),
|
|
22
|
+
facebook: (
|
|
23
|
+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden className="w-5 h-5">
|
|
24
|
+
<path d="M14 9V7a1 1 0 0 1 1-1h2V3h-3a4 4 0 0 0-4 4v2H8v3h2v9h3v-9h2.5l.5-3H13z" />
|
|
25
|
+
</svg>
|
|
26
|
+
),
|
|
27
|
+
youtube: (
|
|
28
|
+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden className="w-5 h-5">
|
|
29
|
+
<path d="M22.5 6.5a2.6 2.6 0 0 0-1.8-1.8C19 4.2 12 4.2 12 4.2s-7 0-8.7.5A2.6 2.6 0 0 0 1.5 6.5C1 8.2 1 12 1 12s0 3.8.5 5.5a2.6 2.6 0 0 0 1.8 1.8C5 19.8 12 19.8 12 19.8s7 0 8.7-.5a2.6 2.6 0 0 0 1.8-1.8c.5-1.7.5-5.5.5-5.5s0-3.8-.5-5.5zM10 15.5v-7l6 3.5-6 3.5z" />
|
|
30
|
+
</svg>
|
|
31
|
+
),
|
|
32
|
+
linkedin: (
|
|
33
|
+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden className="w-5 h-5">
|
|
34
|
+
<path d="M4 4h4v16H4zM6 2.5a2 2 0 1 0 0 4 2 2 0 0 0 0-4zM10 8h4v2.5h.1c.6-1.1 2-2.5 4-2.5 4 0 4.9 2.6 4.9 6V20h-4v-5c0-1.5-.5-3-2.3-3-1.7 0-2.5 1.3-2.5 3v5h-4z" />
|
|
35
|
+
</svg>
|
|
36
|
+
),
|
|
37
|
+
whatsapp: (
|
|
38
|
+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden className="w-5 h-5">
|
|
39
|
+
<path d="M12 2a10 10 0 0 0-8.6 15l-1.4 5 5.2-1.4A10 10 0 1 0 12 2zm5 14.2c-.2.6-1.2 1.2-1.7 1.2-.5.1-1.1.1-1.7-.1-.4-.1-.9-.3-1.5-.6a8.4 8.4 0 0 1-3.7-3.4c-.7-1-1-1.8-1-2.5 0-.7.4-1.1.6-1.3.2-.2.4-.2.5-.2h.4c.1 0 .3 0 .4.3l.6 1.4c.1.2 0 .3 0 .4l-.3.4-.3.3c-.1.1-.2.2-.1.4.2.4.7 1.1 1.4 1.8.9.8 1.7 1.1 1.9 1.2.2.1.3.1.5-.1l.6-.7c.2-.2.3-.2.5-.1l1.4.7c.2.1.3.2.4.3.1.2.1.6 0 1z" />
|
|
40
|
+
</svg>
|
|
41
|
+
),
|
|
42
|
+
};
|
|
52
43
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{ label: "Breads", href: "/categories/breads" },
|
|
59
|
-
{ label: "Pastries", href: "/categories/pastries" },
|
|
60
|
-
{ label: "Cakes", href: "/categories/cakes" },
|
|
61
|
-
{ label: "Drinks", href: "/categories/drinks" },
|
|
62
|
-
{ label: "Merch & More", href: "/categories/merch" },
|
|
63
|
-
{ label: "Classes & Stays", href: "/categories/classes" },
|
|
64
|
-
],
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
title: "Bakery",
|
|
68
|
-
links: [
|
|
69
|
-
{ label: "Our story", href: "/about" },
|
|
70
|
-
{ label: "FAQ", href: "/faq" },
|
|
71
|
-
{ label: "Visit us", href: "/about#visit" },
|
|
72
|
-
{ label: "Wholesale", href: "/categories/merch" },
|
|
73
|
-
],
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
title: "Help",
|
|
77
|
-
links: [
|
|
78
|
-
{ label: "Contact", href: "mailto:hello@akuasbakery.test" },
|
|
79
|
-
{ label: "Custom cakes", href: "/faq#custom-cakes--celebrations" },
|
|
80
|
-
{ label: "Allergens", href: "/faq#allergens--ingredients" },
|
|
81
|
-
{ label: "Delivery", href: "/faq#ordering--delivery" },
|
|
82
|
-
],
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
title: "Legal",
|
|
86
|
-
links: [
|
|
87
|
-
{ label: "Terms of Service", href: "/terms" },
|
|
88
|
-
{ label: "Privacy Policy", href: "/privacy" },
|
|
89
|
-
],
|
|
90
|
-
},
|
|
91
|
-
];
|
|
44
|
+
const FALLBACK_ICON = (
|
|
45
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden className="w-5 h-5">
|
|
46
|
+
<circle cx="12" cy="12" r="9" />
|
|
47
|
+
</svg>
|
|
48
|
+
);
|
|
92
49
|
|
|
93
50
|
export async function Footer() {
|
|
94
51
|
"use cache";
|
|
95
52
|
const year = new Date().getFullYear();
|
|
96
53
|
return (
|
|
97
|
-
<footer className="mt-
|
|
98
|
-
<div className="max-w-7xl mx-auto
|
|
54
|
+
<footer className="mt-12 px-8 py-10 text-xs text-muted-foreground border-t border-border bg-card">
|
|
55
|
+
<div className="max-w-7xl mx-auto">
|
|
99
56
|
<div className="grid gap-10 md:grid-cols-[1.4fr_repeat(4,1fr)]">
|
|
100
57
|
<div>
|
|
101
|
-
<p className="font-serif text-2xl text-foreground m-0 mb-2">
|
|
102
|
-
<p className="
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
</p>
|
|
106
|
-
<address className="not-italic text-muted-foreground space-y-1">
|
|
107
|
-
<p className="m-0">12 Independence Ave, Accra</p>
|
|
58
|
+
<p className="font-serif text-2xl text-foreground m-0 mb-2">{brand.name}</p>
|
|
59
|
+
<p className="leading-relaxed mb-4 max-w-sm">{brand.footer.blurb}</p>
|
|
60
|
+
<address className="not-italic space-y-1">
|
|
61
|
+
<p className="m-0">{brand.contact.address}</p>
|
|
108
62
|
<p className="m-0">
|
|
109
|
-
<a
|
|
110
|
-
|
|
63
|
+
<a
|
|
64
|
+
href={`tel:${brand.contact.phoneTel}`}
|
|
65
|
+
className="hover:text-foreground transition-colors"
|
|
66
|
+
>
|
|
67
|
+
{brand.contact.phone}
|
|
111
68
|
</a>
|
|
112
69
|
</p>
|
|
113
70
|
<p className="m-0">
|
|
114
71
|
<a
|
|
115
|
-
href=
|
|
72
|
+
href={`mailto:${brand.contact.email}`}
|
|
116
73
|
className="hover:text-foreground transition-colors"
|
|
117
74
|
>
|
|
118
|
-
|
|
75
|
+
{brand.contact.email}
|
|
119
76
|
</a>
|
|
120
77
|
</p>
|
|
121
|
-
<p className="m-0
|
|
78
|
+
<p className="m-0">{brand.contact.hours}</p>
|
|
122
79
|
</address>
|
|
123
80
|
<div className="flex items-center gap-3 mt-5">
|
|
124
|
-
{
|
|
81
|
+
{brand.socials.map((s) => (
|
|
125
82
|
<a
|
|
126
83
|
key={s.label}
|
|
127
84
|
href={s.href}
|
|
@@ -130,12 +87,12 @@ export async function Footer() {
|
|
|
130
87
|
rel="noopener noreferrer"
|
|
131
88
|
className="inline-flex items-center justify-center w-9 h-9 rounded-full border border-border text-muted-foreground hover:text-foreground hover:border-foreground transition-colors"
|
|
132
89
|
>
|
|
133
|
-
{s.
|
|
90
|
+
{(s.icon && ICONS[s.icon]) ?? FALLBACK_ICON}
|
|
134
91
|
</a>
|
|
135
92
|
))}
|
|
136
93
|
</div>
|
|
137
94
|
</div>
|
|
138
|
-
{
|
|
95
|
+
{brand.footer.sitemap.map((section) => (
|
|
139
96
|
<nav key={section.title} aria-labelledby={`footer-${section.title}`}>
|
|
140
97
|
<p
|
|
141
98
|
id={`footer-${section.title}`}
|
|
@@ -148,7 +105,7 @@ export async function Footer() {
|
|
|
148
105
|
<li key={link.label}>
|
|
149
106
|
<Link
|
|
150
107
|
href={link.href}
|
|
151
|
-
className="
|
|
108
|
+
className="hover:text-foreground transition-colors"
|
|
152
109
|
>
|
|
153
110
|
{link.label}
|
|
154
111
|
</Link>
|
|
@@ -158,31 +115,32 @@ export async function Footer() {
|
|
|
158
115
|
</nav>
|
|
159
116
|
))}
|
|
160
117
|
</div>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
>
|
|
173
|
-
<span className="font-semibold tracking-tight">Cimplify</span>
|
|
174
|
-
<svg
|
|
175
|
-
viewBox="0 0 12 12"
|
|
176
|
-
aria-hidden
|
|
177
|
-
className="w-3 h-3 opacity-70"
|
|
178
|
-
fill="none"
|
|
179
|
-
stroke="currentColor"
|
|
180
|
-
strokeWidth="1.5"
|
|
118
|
+
<div className="mt-12 pt-6 border-t border-border flex flex-col sm:flex-row items-center justify-between gap-3">
|
|
119
|
+
<p className="m-0">© {year} {brand.name}. All rights reserved.</p>
|
|
120
|
+
{brand.footer.poweredBy && (
|
|
121
|
+
<p className="m-0 inline-flex items-center gap-1.5">
|
|
122
|
+
<span className="text-muted-foreground/80">Powered by</span>
|
|
123
|
+
<a
|
|
124
|
+
href={brand.footer.poweredBy.href}
|
|
125
|
+
target="_blank"
|
|
126
|
+
rel="noopener noreferrer"
|
|
127
|
+
aria-label={brand.footer.poweredBy.label}
|
|
128
|
+
className="inline-flex items-center gap-1 font-serif text-foreground hover:text-primary transition-colors"
|
|
181
129
|
>
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
130
|
+
<span className="font-semibold tracking-tight">{brand.footer.poweredBy.label}</span>
|
|
131
|
+
<svg
|
|
132
|
+
viewBox="0 0 12 12"
|
|
133
|
+
aria-hidden
|
|
134
|
+
className="w-3 h-3 opacity-70"
|
|
135
|
+
fill="none"
|
|
136
|
+
stroke="currentColor"
|
|
137
|
+
strokeWidth="1.5"
|
|
138
|
+
>
|
|
139
|
+
<path d="M3 9L9 3M9 3H4M9 3v5" strokeLinecap="round" strokeLinejoin="round" />
|
|
140
|
+
</svg>
|
|
141
|
+
</a>
|
|
142
|
+
</p>
|
|
143
|
+
)}
|
|
186
144
|
</div>
|
|
187
145
|
</div>
|
|
188
146
|
</footer>
|
|
@@ -2,8 +2,7 @@ import Link from "next/link";
|
|
|
2
2
|
import { Suspense } from "react";
|
|
3
3
|
import { NavLink } from "./nav-link";
|
|
4
4
|
import { CartPill, CartPillSkeleton } from "./cart-pill";
|
|
5
|
-
|
|
6
|
-
const TAGLINE = "ACCRA · SINCE 2018";
|
|
5
|
+
import { brand } from "@/lib/brand";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Server-rendered header chrome. Brand mark + nav layout streams from the
|
|
@@ -15,25 +14,18 @@ export function Header() {
|
|
|
15
14
|
<header className="sticky top-0 z-30 flex items-center justify-between px-8 py-4 border-b border-border bg-background/90 backdrop-blur-md">
|
|
16
15
|
<Link href="/" className="flex items-baseline gap-2">
|
|
17
16
|
<span className="font-serif text-[22px] font-semibold -tracking-[0.02em]">
|
|
18
|
-
|
|
17
|
+
{brand.shortName}
|
|
19
18
|
</span>
|
|
20
19
|
<span className="text-[11px] font-medium uppercase tracking-[0.12em] text-muted-foreground">
|
|
21
|
-
{
|
|
20
|
+
{brand.microTag}
|
|
22
21
|
</span>
|
|
23
22
|
</Link>
|
|
24
23
|
<nav className="flex items-center gap-6">
|
|
25
|
-
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
</Suspense>
|
|
31
|
-
<Suspense fallback={<NavLinkFallback>About</NavLinkFallback>}>
|
|
32
|
-
<NavLink href="/about">About</NavLink>
|
|
33
|
-
</Suspense>
|
|
34
|
-
<Suspense fallback={<NavLinkFallback>FAQ</NavLinkFallback>}>
|
|
35
|
-
<NavLink href="/faq">FAQ</NavLink>
|
|
36
|
-
</Suspense>
|
|
24
|
+
{brand.header.nav.map((link) => (
|
|
25
|
+
<Suspense key={link.href} fallback={<NavLinkFallback>{link.label}</NavLinkFallback>}>
|
|
26
|
+
<NavLink href={link.href}>{link.label}</NavLink>
|
|
27
|
+
</Suspense>
|
|
28
|
+
))}
|
|
37
29
|
<Suspense fallback={<CartPillSkeleton />}>
|
|
38
30
|
<CartPill />
|
|
39
31
|
</Suspense>
|