81a348a2f4
Implements the customer-facing portal for pet parents with: - Dashboard showing upcoming appointments, pet cards, loyalty rewards - Multi-step appointment booking flow with recurring scheduling - Pet profiles with medical/behavioral notes and vaccination tracking - Grooming report cards with before/after, behavior assessment, sharing - Billing & payments with invoices, saved methods, autopay, tips, packages - Communication with chat-style messaging and notification preferences - Account settings with personal info, password, pet management, agreements - Staff impersonation mode with required reason, 30-min session timer, non-dismissable banner, viewport border, watermark, read-only enforcement, and full audit trail viewer Also adds Tailwind CSS, lucide-react, and recharts as dependencies. Closes #53 Co-Authored-By: Paperclip <noreply@paperclip.ing>
91 lines
3.0 KiB
TypeScript
91 lines
3.0 KiB
TypeScript
import { Routes, Route, Link, useLocation } from "react-router-dom";
|
|
import { AppointmentsPage } from "./pages/Appointments.js";
|
|
import { ClientsPage } from "./pages/Clients.js";
|
|
import { ServicesPage } from "./pages/Services.js";
|
|
import { StaffPage } from "./pages/Staff.js";
|
|
import { InvoicesPage } from "./pages/Invoices.js";
|
|
import { BookPage } from "./pages/Book.js";
|
|
import { ReportsPage } from "./pages/Reports.js";
|
|
import { GroupBookingPage } from "./pages/GroupBooking.js";
|
|
import { CustomerPortal } from "./portal/CustomerPortal.js";
|
|
|
|
const NAV_LINKS = [
|
|
{ to: "/", label: "Appointments" },
|
|
{ to: "/clients", label: "Clients" },
|
|
{ to: "/services", label: "Services" },
|
|
{ to: "/staff", label: "Staff" },
|
|
{ to: "/invoices", label: "Invoices" },
|
|
{ to: "/group-bookings", label: "Group Bookings" },
|
|
{ to: "/reports", label: "Reports" },
|
|
{ to: "/portal", label: "Customer Portal" },
|
|
];
|
|
|
|
export function App() {
|
|
const location = useLocation();
|
|
return (
|
|
<div style={{ minHeight: "100vh", fontFamily: "system-ui, sans-serif" }}>
|
|
<nav
|
|
style={{
|
|
padding: "0.75rem 1rem",
|
|
borderBottom: "1px solid #e2e8f0",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: "0.25rem",
|
|
background: "#fff",
|
|
}}
|
|
>
|
|
<strong style={{ marginRight: "1rem", fontSize: 16 }}>Groom Book</strong>
|
|
<Link
|
|
to="/book"
|
|
style={{
|
|
padding: "0.35rem 0.75rem",
|
|
borderRadius: 4,
|
|
textDecoration: "none",
|
|
fontSize: 14,
|
|
fontWeight: 600,
|
|
color: "#fff",
|
|
background: "#4f8a6f",
|
|
marginRight: "0.5rem",
|
|
}}
|
|
>
|
|
Book
|
|
</Link>
|
|
{NAV_LINKS.map(({ to, label }) => {
|
|
const active =
|
|
to === "/" ? location.pathname === "/" : location.pathname.startsWith(to);
|
|
return (
|
|
<Link
|
|
key={to}
|
|
to={to}
|
|
style={{
|
|
padding: "0.35rem 0.75rem",
|
|
borderRadius: 4,
|
|
textDecoration: "none",
|
|
fontSize: 14,
|
|
fontWeight: active ? 600 : 400,
|
|
color: active ? "#1d4ed8" : "#374151",
|
|
background: active ? "#eff6ff" : "transparent",
|
|
}}
|
|
>
|
|
{label}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
<main style={{ padding: "1rem 1.5rem" }}>
|
|
<Routes>
|
|
<Route path="/" element={<AppointmentsPage />} />
|
|
<Route path="/clients" element={<ClientsPage />} />
|
|
<Route path="/services" element={<ServicesPage />} />
|
|
<Route path="/staff" element={<StaffPage />} />
|
|
<Route path="/invoices" element={<InvoicesPage />} />
|
|
<Route path="/book" element={<BookPage />} />
|
|
<Route path="/group-bookings" element={<GroupBookingPage />} />
|
|
<Route path="/reports" element={<ReportsPage />} />
|
|
<Route path="/portal" element={<CustomerPortal />} />
|
|
</Routes>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|