This commit is contained in:
Stefan Lazic committed 2026-08-04 09:57:58 +01:00
1 parent 546613e677
commit 4785510144
82 files changed
+4903 -287

No files matched your search

+47 -10
View File
@@ -3,21 +3,49 @@ import Link from "next/link";
import { redirect } from "next/navigation";
import { auth } from "@/auth";
import { Button } from "@/components/ui/button";
import { getBranding } from "@/lib/business/branding";
import { getPrimaryBusiness } from "@/lib/business/get-primary-business";
import { hasPermission } from "@/lib/auth/permissions";
import { Permission } from "@/generated/prisma/enums";
import { prisma } from "@/lib/db/prisma";
import { WelcomeInstallCard } from "@/features/pwa/components/welcome-install-card";
/**
* The unauthenticated entry point — always shown at `/` before
* authentication, per the brief's "Welcome routing" requirements.
* Authenticated visitors never see it: they're routed straight to their
* role's home (customer → /home, barber → /staff, owner → /owner) below.
* Unreachable at all before installation completes — src/proxy.ts
* redirects every non-exempt route (including this one) to /install
* until ApplicationInstallation flips to INSTALLED.
*/
export default async function WelcomePage() {
const session = await auth();
if (session?.user) {
const staffProfile = await prisma.staffProfile.findUnique({ where: { userId: session.user.id } });
if (staffProfile) {
const canManageStaff = await hasPermission(session.user.id, Permission.MANAGE_STAFF);
redirect(canManageStaff ? "/owner" : "/staff");
}
redirect("/home");
}
const [branding, business] = await Promise.all([getBranding(), getPrimaryBusiness()]);
const guestBookingEnabled = business.bookingRule?.allowGuestBooking ?? false;
return (
<div id="main-content" className="flex min-h-screen flex-col justify-end bg-ink px-8 pb-16 text-white">
<div className="mb-8 flex h-16 w-16 items-center justify-center rounded-2xl bg-white text-ink">
<Scissors className="h-7 w-7" aria-hidden="true" />
{branding.logoUrl ? (
// eslint-disable-next-line @next/next/no-img-element -- external, business-supplied URL; not a build-time-known local asset for next/image.
<img src={branding.logoUrl} alt="" className="h-full w-full rounded-2xl object-cover" />
) : (
<Scissors className="h-7 w-7" aria-hidden="true" />
)}
</div>
<h1 className="mb-4 text-5xl font-bold tracking-tight">NextChair</h1>
<h1 className="mb-4 text-5xl font-bold tracking-tight">{branding.name}</h1>
<p className="mb-12 max-w-xs text-lg text-white/70">
Precision grooming at your fingertips. Book your next visit in seconds.
Book your next visit in seconds — services, staff and loyalty rewards in one place.
</p>
<div className="space-y-4">
@@ -32,15 +60,24 @@ export default async function WelcomePage() {
>
<Link href="/sign-in">Sign In</Link>
</Button>
{guestBookingEnabled && (
<Button
asChild
variant="ghost"
size="lg"
className="w-full text-white/70 hover:bg-white/10 hover:text-white"
>
<Link href="/book">Continue as Guest</Link>
</Button>
)}
<Button asChild variant="ghost" size="lg" className="w-full text-white/70 hover:bg-white/10 hover:text-white">
<Link href="/home">Browse Services</Link>
</Button>
</div>
{/*
The templates' "Continue as Guest" goes straight to the business
home/booking flow (template 20 → 15). That flow isn't built yet
(Phase 1: Customer Booking), so this button isn't shown rather than
linking somewhere that doesn't work yet — see
docs/implementation-plan.md §16 for the phase it belongs to.
*/}
<div className="mt-6">
<WelcomeInstallCard businessName={branding.name} />
</div>
</div>
);
}