This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
app/apps/web/src/pages/Services.tsx
T
Groom Book CTO a36436d128 Bootstrap monorepo: Hono API, React PWA, Drizzle DB, CI/CD
Sets up the initial project structure for groombook/groombook:

- pnpm monorepo with apps/api (Hono + TypeScript), apps/web (React + Vite + PWA), packages/db (Drizzle ORM), packages/types (shared types)
- Core DB schema: clients, pets, services, appointments, staff with CNPG-compatible Postgres
- REST API routes for clients, pets, services, appointments with Zod validation
- OIDC auth middleware for Authentik integration
- React PWA with vite-plugin-pwa, service worker, offline caching, installable manifest
- GitHub Actions CI: lint, typecheck, test, build, Docker image build (groombook-runners)
- Dockerfiles for API (Node.js) and Web (nginx)
- docker-compose.yml for local development

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-17 16:11:04 +00:00

43 lines
1.1 KiB
TypeScript

import { useEffect, useState } from "react";
import type { Service } from "@groombook/types";
export function ServicesPage() {
const [services, setServices] = useState<Service[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetch("/api/services")
.then((r) => {
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json() as Promise<Service[]>;
})
.then(setServices)
.catch((e: unknown) =>
setError(e instanceof Error ? e.message : "Unknown error")
)
.finally(() => setLoading(false));
}, []);
if (loading) return <p>Loading services</p>;
if (error) return <p style={{ color: "red" }}>Error: {error}</p>;
return (
<div>
<h1>Services</h1>
{services.length === 0 ? (
<p>No services configured yet.</p>
) : (
<ul>
{services.map((s) => (
<li key={s.id}>
{s.name} ${(s.basePriceCents / 100).toFixed(2)} /{" "}
{s.durationMinutes} min
</li>
))}
</ul>
)}
</div>
);
}