Same pattern as customers. Each service belongs to one customer (FK), has
unit price (TRY), billing period (monthly/yearly/onetime), and recurring
flag.
- lib/validation/services.ts: Zod schema with TRY price coercion (accepts
comma decimal), enum billing period, recurring boolean coercion.
- lib/appwrite/service-actions.ts: createServiceAction, updateServiceAction,
deleteServiceAction. Same tenant guard, audit log, team-scoped row perms.
- lib/appwrite/service-queries.ts: listServices, listServicesByCustomer.
- lib/format.ts: formatTRY (Intl.NumberFormat tr-TR), formatDate/DateTime,
BILLING_PERIOD_LABEL mapping (Aylık/Yıllık/Tek seferlik).
- /services page (server): joins services with customer names via
in-memory map.
- ServicesClient: TanStack table with global filter (name/customer/desc),
Repeat icon next to recurring badges, formatted TRY column.
- ServiceFormSheet: customer dropdown (disabled if no customers exist),
unit price + billing period + recurring switch.
- DeleteServiceDialog: same destructive confirmation pattern.
Empty state on /services CTA's user back to add a customer first if none
exist.