Files
NextChair/src/app/style-guide/interactive.tsx
T
Stefan Lazic 090646fdfd Phase 2
Base app architecture:

    Route groups for customer, staff and owner.
* Reusable design system with around 20 UI components.
* Complete Prisma schema.
* Authentication with email/password and Google.
* Role and permission model.
* Protected routes.
* Clean separation between features, services, components and shared utilities.
2026-08-02 17:47:23 +01:00

104 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { BottomSheet } from "@/components/ui/bottom-sheet";
import { Card } from "@/components/ui/card";
import { ErrorState } from "@/components/feedback/error-state";
import { Modal } from "@/components/ui/modal";
import { MonthCalendar } from "@/components/ui/month-calendar";
import { ProfileCard } from "@/components/ui/profile-card";
import { Switch } from "@/components/ui/switch";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
/**
* Client-only demo for components that need interaction state — kept
* separate so the style guide page itself stays a Server Component. This
* is also the reason `ProfileCard`'s `onEdit` and `ErrorState`'s
* `onRetry` are demonstrated here rather than in page.tsx: a Server
* Component can't pass a live event-handler closure as a prop to a
* Client Component (Next.js build error "Event handlers cannot be passed
* to Client Component props") — the handler has to originate from
* somewhere already client-side, which this component is.
*/
export function StyleGuideInteractive() {
const [switchOn, setSwitchOn] = useState(true);
const [modalOpen, setModalOpen] = useState(false);
const [sheetOpen, setSheetOpen] = useState(false);
const [month, setMonth] = useState(() => new Date());
const [selectedDate, setSelectedDate] = useState<Date | undefined>();
const [editCount, setEditCount] = useState(0);
const [retryCount, setRetryCount] = useState(0);
return (
<div className="space-y-8">
<Card className="space-y-4 p-6">
<Switch
checked={switchOn}
onCheckedChange={setSwitchOn}
label="Booking confirmations"
description="Email when a booking is confirmed"
/>
<Switch defaultChecked={false} label="Require staff approval" />
</Card>
<Tabs defaultValue="upcoming">
<TabsList>
<TabsTrigger value="upcoming">Upcoming</TabsTrigger>
<TabsTrigger value="past">Past</TabsTrigger>
</TabsList>
<TabsContent value="upcoming">
<p className="text-sm text-muted">Upcoming appointments render here.</p>
</TabsContent>
<TabsContent value="past">
<p className="text-sm text-muted">Past appointments render here.</p>
</TabsContent>
</Tabs>
<div className="flex flex-wrap gap-3">
<Button variant="secondary" onClick={() => setModalOpen(true)}>
Open Modal
</Button>
<Button variant="secondary" onClick={() => setSheetOpen(true)}>
Open Bottom Sheet
</Button>
</div>
<Modal open={modalOpen} onClose={() => setModalOpen(false)} title="Cancel Appointment?">
<p className="mb-6 text-sm text-muted">
This is within the free cancellation window. Are you sure you want to cancel?
</p>
<div className="flex gap-3">
<Button variant="secondary" className="flex-1" onClick={() => setModalOpen(false)}>
Keep it
</Button>
<Button variant="destructive" className="flex-1" onClick={() => setModalOpen(false)}>
Cancel it
</Button>
</div>
</Modal>
<BottomSheet open={sheetOpen} onClose={() => setSheetOpen(false)} title="Add a Note">
<Textarea placeholder="e.g. skin sensitivity, specific style requests…" />
<Button className="mt-4 w-full" onClick={() => setSheetOpen(false)}>
Save Note
</Button>
</BottomSheet>
<MonthCalendar month={month} onMonthChange={setMonth} selectedDate={selectedDate} onSelectDate={setSelectedDate} />
<div className="grid gap-4 sm:grid-cols-2">
<div>
<ProfileCard
name="Alex Chen"
meta={`Edited ${editCount}×`}
onEdit={() => setEditCount((c) => c + 1)}
/>
</div>
<ErrorState onRetry={() => setRetryCount((c) => c + 1)} description={`Retried ${retryCount}×`} />
</div>
</div>
);
}