272 lines
10 KiB
JavaScript
272 lines
10 KiB
JavaScript
/**
|
|
* Generates Kottgard-Website-Guide.docx
|
|
* Run: node docs/generate-guide-docx.mjs
|
|
*/
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import {
|
|
Document,
|
|
Packer,
|
|
Paragraph,
|
|
TextRun,
|
|
Table,
|
|
TableRow,
|
|
TableCell,
|
|
HeadingLevel,
|
|
AlignmentType,
|
|
BorderStyle,
|
|
WidthType,
|
|
ShadingType,
|
|
PageBreak,
|
|
LevelFormat,
|
|
} from 'docx';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const outPath = path.join(__dirname, 'Kottgard-Website-Guide.docx');
|
|
|
|
const border = { style: BorderStyle.SINGLE, size: 1, color: 'CCCCCC' };
|
|
const borders = { top: border, bottom: border, left: border, right: border };
|
|
const tableWidth = 9360;
|
|
|
|
function cell(text, width, fill = 'FFFFFF') {
|
|
return new TableCell({
|
|
borders,
|
|
width: { size: width, type: WidthType.DXA },
|
|
shading: { fill, type: ShadingType.CLEAR },
|
|
margins: { top: 80, bottom: 80, left: 120, right: 120 },
|
|
children: [new Paragraph({ children: [new TextRun(text)] })],
|
|
});
|
|
}
|
|
|
|
function headerRow(cols, widths) {
|
|
return new TableRow({
|
|
children: cols.map((t, i) => cell(t, widths[i], 'D5E8F0')),
|
|
});
|
|
}
|
|
|
|
function dataRow(cols, widths) {
|
|
return new TableRow({
|
|
children: cols.map((t, i) => cell(t, widths[i])),
|
|
});
|
|
}
|
|
|
|
function table(columnWidths, rows) {
|
|
return new Table({
|
|
width: { size: tableWidth, type: WidthType.DXA },
|
|
columnWidths,
|
|
rows,
|
|
});
|
|
}
|
|
|
|
function h1(text) {
|
|
return new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun(text)] });
|
|
}
|
|
function h2(text) {
|
|
return new Paragraph({ heading: HeadingLevel.HEADING_2, children: [new TextRun(text)] });
|
|
}
|
|
function p(text) {
|
|
return new Paragraph({ spacing: { after: 200 }, children: [new TextRun(text)] });
|
|
}
|
|
function bullet(ref, text) {
|
|
return new Paragraph({
|
|
numbering: { reference: ref, level: 0 },
|
|
children: [new TextRun(text)],
|
|
});
|
|
}
|
|
|
|
const doc = new Document({
|
|
styles: {
|
|
default: { document: { run: { font: 'Arial', size: 22 } } },
|
|
paragraphStyles: [
|
|
{
|
|
id: 'Heading1',
|
|
name: 'Heading 1',
|
|
basedOn: 'Normal',
|
|
next: 'Normal',
|
|
quickFormat: true,
|
|
run: { size: 32, bold: true, font: 'Arial' },
|
|
paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0 },
|
|
},
|
|
{
|
|
id: 'Heading2',
|
|
name: 'Heading 2',
|
|
basedOn: 'Normal',
|
|
next: 'Normal',
|
|
quickFormat: true,
|
|
run: { size: 26, bold: true, font: 'Arial' },
|
|
paragraph: { spacing: { before: 180, after: 180 }, outlineLevel: 1 },
|
|
},
|
|
],
|
|
},
|
|
numbering: {
|
|
config: [
|
|
{
|
|
reference: 'bullets',
|
|
levels: [
|
|
{
|
|
level: 0,
|
|
format: LevelFormat.BULLET,
|
|
text: '\u2022',
|
|
alignment: AlignmentType.LEFT,
|
|
style: { paragraph: { indent: { left: 720, hanging: 360 } } },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
sections: [
|
|
{
|
|
properties: {
|
|
page: {
|
|
size: { width: 12240, height: 15840 },
|
|
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
|
|
},
|
|
},
|
|
children: [
|
|
new Paragraph({
|
|
alignment: AlignmentType.CENTER,
|
|
spacing: { after: 400 },
|
|
children: [
|
|
new TextRun({ text: 'Kött Gård', bold: true, size: 48 }),
|
|
],
|
|
}),
|
|
new Paragraph({
|
|
alignment: AlignmentType.CENTER,
|
|
spacing: { after: 600 },
|
|
children: [
|
|
new TextRun({
|
|
text: 'Website Visual & Programming Guide',
|
|
size: 32,
|
|
color: '8B1F1F',
|
|
}),
|
|
],
|
|
}),
|
|
p('Premium Halal meat e-commerce — Märsta, Sweden'),
|
|
p('Document version: June 2026'),
|
|
p('Companion files: docs/annotated/ (line-by-line code comments)'),
|
|
|
|
new Paragraph({ children: [new PageBreak()] }),
|
|
|
|
h1('1. What is this website?'),
|
|
p(
|
|
'Kött Gård is a Next.js e-commerce front-end for ordering fresh Halal chicken, beef, lamb, and fish. Customers browse products, customize cuts (Nihari, Karahi, piece counts), add to cart, checkout, and view order history. Languages: Swedish, English, Urdu (RTL).'
|
|
),
|
|
|
|
h2('Demo login'),
|
|
table(
|
|
[3120, 6240],
|
|
[
|
|
headerRow(['Field', 'Value'], [3120, 6240]),
|
|
dataRow(['Email', 'demo@kottgard.se'], [3120, 6240]),
|
|
dataRow(['Password', 'demo123'], [3120, 6240]),
|
|
dataRow(['Orders', '3 sample orders in Account'], [3120, 6240]),
|
|
]
|
|
),
|
|
|
|
h1('2. Homepage layout (visual map)'),
|
|
p('When a customer opens https://kottgard.se/, they scroll through these sections:'),
|
|
table(
|
|
[800, 2200, 3160, 3200],
|
|
[
|
|
headerRow(['#', 'Section', 'File', 'Customer action'], [800, 2200, 3160, 3200]),
|
|
dataRow(['1', 'Hero', 'Hero.tsx', 'Shop now / WhatsApp'], [800, 2200, 3160, 3200]),
|
|
dataRow(['2', 'Trust badges', 'TrustBadges.tsx', 'Click → About'], [800, 2200, 3160, 3200]),
|
|
dataRow(['3', 'About preview', 'AboutPreview.tsx', 'Read more → /about'], [800, 2200, 3160, 3200]),
|
|
dataRow(['4', 'Categories', 'CategoryGrid.tsx', 'Chicken/Beef/Lamb/Fish'], [800, 2200, 3160, 3200]),
|
|
dataRow(['5', 'Featured', 'FeaturedProducts.tsx', 'Product cards'], [800, 2200, 3160, 3200]),
|
|
dataRow(['6', 'How it works', 'HowItWorks.tsx', 'Order steps'], [800, 2200, 3160, 3200]),
|
|
dataRow(['7', 'Weekly offers', 'WeeklyOffers.tsx', 'Deals → products'], [800, 2200, 3160, 3200]),
|
|
dataRow(['8', 'CTA', 'CTA.tsx', 'Browse selection'], [800, 2200, 3160, 3200]),
|
|
dataRow(['9', 'Social', 'SocialFollow.tsx', 'Facebook / Instagram'], [800, 2200, 3160, 3200]),
|
|
dataRow(['10', 'Contact', 'ContactPreview.tsx', 'Map / phone / about'], [800, 2200, 3160, 3200]),
|
|
]
|
|
),
|
|
|
|
h1('3. All pages & routes'),
|
|
table(
|
|
[2800, 3360, 3200],
|
|
[
|
|
headerRow(['URL', 'File', 'Description'], [2800, 3360, 3200]),
|
|
dataRow(['/', 'app/page.tsx', 'Homepage'], [2800, 3360, 3200]),
|
|
dataRow(['/shop', 'app/shop/page.tsx', 'Catalog + search + filters'], [2800, 3360, 3200]),
|
|
dataRow(['/product/[slug]', 'app/product/[slug]/page.tsx', 'Detail + customize'], [2800, 3360, 3200]),
|
|
dataRow(['/cart', 'app/cart/page.tsx', 'Shopping cart'], [2800, 3360, 3200]),
|
|
dataRow(['/checkout', 'app/checkout/page.tsx', 'Place order (demo)'], [2800, 3360, 3200]),
|
|
dataRow(['/login', 'app/login/page.tsx', 'Auth'], [2800, 3360, 3200]),
|
|
dataRow(['/account', 'app/account/page.tsx', 'Profile + orders'], [2800, 3360, 3200]),
|
|
dataRow(['/wishlist', 'app/wishlist/page.tsx', 'Saved items'], [2800, 3360, 3200]),
|
|
dataRow(['/about', 'app/about/page.tsx', 'Company + privacy + terms'], [2800, 3360, 3200]),
|
|
]
|
|
),
|
|
|
|
new Paragraph({ children: [new PageBreak()] }),
|
|
|
|
h1('4. Programming languages & frameworks'),
|
|
h2('TypeScript'),
|
|
p(
|
|
'TypeScript adds types to JavaScript. Example: Product interface ensures every product has id, slug, price. The compiler errors if you typo product.slugg.'
|
|
),
|
|
h2('React'),
|
|
p(
|
|
'React builds UI from components (functions returning JSX). State (useState) updates what users see. Props pass data parent → child.'
|
|
),
|
|
h2('Next.js 13 App Router'),
|
|
bullet('bullets', 'app/ folder = routes. page.tsx = page, layout.tsx = wrapper.'),
|
|
bullet('bullets', 'Server Components default — less JavaScript sent to browser.'),
|
|
bullet('bullets', "'use client' = component runs in browser (hooks, clicks)."),
|
|
h2('Zustand'),
|
|
p(
|
|
'Global stores: useCartStore, useAuthStore, useWishlistStore. persist middleware saves to localStorage so cart survives refresh.'
|
|
),
|
|
h2('Tailwind CSS'),
|
|
p(
|
|
'Classes like bg-brand-700 text-white px-4 py-2 style elements. Colors defined in tailwind.config.ts (burgundy, cream, gold).'
|
|
),
|
|
|
|
h1('5. Data flow diagram (text)'),
|
|
p('Homepage → Shop (filter URL) → Product page → addItem() → Cart store → Checkout → Order in Auth store → Account page'),
|
|
p('Images: lib/images.ts → products.ts → ProductCard / Hero → AppImage → next/image → Unsplash CDN'),
|
|
p('Text: i18n/locales/sv|en|ur.ts → useTranslation() → t("key") in components'),
|
|
|
|
h1('6. Annotated code (line-by-line)'),
|
|
p('Location: Kottgard/docs/annotated/'),
|
|
table(
|
|
[4200, 5160],
|
|
[
|
|
headerRow(['File', 'Explains'], [4200, 5160]),
|
|
dataRow(['01-homepage.annotated.tsx', 'How homepage composes sections'], [4200, 5160]),
|
|
dataRow(['02-root-layout.annotated.tsx', 'Fonts, SEO, shell layout'], [4200, 5160]),
|
|
dataRow(['03-shop-page.annotated.tsx', 'URL sync, useMemo filtering'], [4200, 5160]),
|
|
dataRow(['04-header.annotated.tsx', 'Navigation + Zustand badges'], [4200, 5160]),
|
|
dataRow(['05-images.annotated.ts', 'Unsplash URL builder'], [4200, 5160]),
|
|
dataRow(['06-cart-store.annotated.ts', 'Cart logic + persist'], [4200, 5160]),
|
|
]
|
|
),
|
|
p(
|
|
'Each line has comments explaining WHAT the code does and WHY. These are learning copies — not imported by the live app.'
|
|
),
|
|
|
|
h1('7. Brand colors'),
|
|
table(
|
|
[3120, 3120, 3120],
|
|
[
|
|
headerRow(['Name', 'Hex', 'Use'], [3120, 3120, 3120]),
|
|
dataRow(['Burgundy (brand)', '#8B1F1F', 'Header, buttons, footer'], [3120, 3120, 3120]),
|
|
dataRow(['Cream', 'Warm off-white', 'Page backgrounds'], [3120, 3120, 3120]),
|
|
dataRow(['Gold', 'Accent', 'Prices, badges, CTAs'], [3120, 3120, 3120]),
|
|
]
|
|
),
|
|
|
|
h1('8. How to run locally'),
|
|
bullet('bullets', 'npm install'),
|
|
bullet('bullets', 'npm run dev → http://localhost:3000'),
|
|
bullet('bullets', 'npm run build → production check'),
|
|
],
|
|
},
|
|
],
|
|
});
|
|
|
|
const buffer = await Packer.toBuffer(doc);
|
|
fs.writeFileSync(outPath, buffer);
|
|
console.log('Written:', outPath); |