Dev/v1.0 (#1)

Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-07-23 10:51:04 +08:00
parent 3189e3a1e3
commit 85b31e9528
106 changed files with 19492 additions and 401 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+24
View File
@@ -0,0 +1,24 @@
@import "tailwindcss";
:root {
--background: #fafafa;
--foreground: #171717;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
html,
body {
min-height: 100%;
}
body {
background: var(--background);
color: var(--foreground);
font-family: var(--font-geist-sans), Arial, Helvetica, sans-serif;
}
+33
View File
@@ -0,0 +1,33 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "MyKOPKB QMS-Customer App",
description: "MyKOPKB QMS-Customer App",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">{children}</body>
</html>
);
}
+9
View File
@@ -0,0 +1,9 @@
import CustomerTicketFlow from "@/components/CustomerTicketFlow";
export default function Home() {
return (
<div className="flex min-h-full flex-1 flex-col bg-zinc-50">
<CustomerTicketFlow />
</div>
);
}
@@ -0,0 +1,42 @@
import Link from "next/link";
import CustomerTicketFlow from "@/components/CustomerTicketFlow";
import { decodeBranchQrToken } from "@/lib/branchToken";
type PageProps = {
params: Promise<{
token: string;
}>;
};
export default async function BranchQrPage({ params }: PageProps) {
const { token } = await params;
const payload = decodeBranchQrToken(token);
if (!payload) {
return (
<div className="flex min-h-full flex-1 flex-col bg-zinc-50">
<main className="mx-auto flex w-full max-w-xl flex-col gap-4 px-6 py-12">
<h1 className="text-2xl font-semibold text-zinc-900">Invalid link</h1>
<p className="text-zinc-600">
This branch QR link is not valid. Scan again or enter a company code.
</p>
<Link
href="/"
className="text-sm text-zinc-900 underline-offset-2 hover:underline"
>
Enter company code
</Link>
</main>
</div>
);
}
return (
<div className="flex min-h-full flex-1 flex-col bg-zinc-50">
<CustomerTicketFlow
initialTenantCode={payload.tenantCode}
initialBranchId={payload.branchId}
/>
</div>
);
}
@@ -0,0 +1,42 @@
import Link from "next/link";
import CustomerTicketFlow from "@/components/CustomerTicketFlow";
type PageProps = {
params: Promise<{
tenantCode: string;
branchId: string;
}>;
};
export default async function BranchTicketPage({ params }: PageProps) {
const { tenantCode, branchId: branchIdParam } = await params;
const branchId = Number(branchIdParam);
if (!Number.isFinite(branchId)) {
return (
<div className="flex min-h-full flex-1 flex-col bg-zinc-50">
<main className="mx-auto flex w-full max-w-xl flex-col gap-4 px-6 py-12">
<h1 className="text-2xl font-semibold text-zinc-900">Invalid link</h1>
<p className="text-zinc-600">
This branch QR link is not valid. Scan again or enter a company code.
</p>
<Link
href="/"
className="text-sm text-zinc-900 underline-offset-2 hover:underline"
>
Enter company code
</Link>
</main>
</div>
);
}
return (
<div className="flex min-h-full flex-1 flex-col bg-zinc-50">
<CustomerTicketFlow
initialTenantCode={tenantCode}
initialBranchId={branchId}
/>
</div>
);
}