feat: complete CMS v2 with Docker, Fischerei, Meetings, Verband modules + UX audit fixes
Major changes: - Docker Compose: full Supabase stack (11 services) equivalent to supabase CLI - Fischerei module: 16 DB tables, waters/species/stocking/catch books/competitions - Sitzungsprotokolle module: meeting protocols, agenda items, task tracking - Verbandsverwaltung module: federation management, member clubs, contacts, fees - Per-account module activation via Modules page toggle - Site Builder: live CMS data in Puck blocks (courses, events, membership registration) - Public registration APIs: course signup, event registration, membership application - Document generation: PDF member cards, Excel reports, HTML labels - Landing page: real Com.BISS content (no filler text) - UX audit fixes: AccountNotFound component, shared status badges, confirm dialog, pagination, duplicate heading removal, emoji→badge replacement, a11y fixes - QA: healthcheck fix, API auth fix, enum mismatch fix, password required attribute
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// =====================================================
|
||||
// Enum Schemas
|
||||
// =====================================================
|
||||
|
||||
export const associationTypeSchema = z.enum([
|
||||
'sportverein',
|
||||
'fischereiverein',
|
||||
'schuetzenverein',
|
||||
'musikverein',
|
||||
'kulturverein',
|
||||
'foerderverein',
|
||||
'jugendverein',
|
||||
'sonstige',
|
||||
]);
|
||||
|
||||
export const paymentMethodSchema = z.enum([
|
||||
'bar',
|
||||
'lastschrift',
|
||||
'ueberweisung',
|
||||
'paypal',
|
||||
]);
|
||||
|
||||
export const billingStatusSchema = z.enum([
|
||||
'offen',
|
||||
'bezahlt',
|
||||
'ueberfaellig',
|
||||
'storniert',
|
||||
]);
|
||||
|
||||
export const noteTypeSchema = z.enum([
|
||||
'notiz',
|
||||
'aufgabe',
|
||||
'erinnerung',
|
||||
]);
|
||||
|
||||
export const contactRoleSchema = z.enum([
|
||||
'vorsitzender',
|
||||
'stellvertreter',
|
||||
'kassier',
|
||||
'schriftfuehrer',
|
||||
'jugendwart',
|
||||
'sportwart',
|
||||
'beisitzer',
|
||||
'sonstige',
|
||||
]);
|
||||
|
||||
// =====================================================
|
||||
// Type Exports
|
||||
// =====================================================
|
||||
|
||||
export type AssociationType = z.infer<typeof associationTypeSchema>;
|
||||
export type PaymentMethod = z.infer<typeof paymentMethodSchema>;
|
||||
export type BillingStatus = z.infer<typeof billingStatusSchema>;
|
||||
export type NoteType = z.infer<typeof noteTypeSchema>;
|
||||
export type ContactRole = z.infer<typeof contactRoleSchema>;
|
||||
|
||||
// =====================================================
|
||||
// Member Clubs (Vereine)
|
||||
// =====================================================
|
||||
|
||||
export const CreateMemberClubSchema = z.object({
|
||||
accountId: z.string().uuid(),
|
||||
name: z.string().min(1, 'Name ist erforderlich').max(256),
|
||||
shortName: z.string().max(32).optional(),
|
||||
associationTypeId: z.string().uuid().optional(),
|
||||
memberCount: z.number().int().min(0).optional(),
|
||||
foundedYear: z.number().int().min(1800).max(2100).optional(),
|
||||
street: z.string().max(256).optional(),
|
||||
zip: z.string().max(10).optional(),
|
||||
city: z.string().max(128).optional(),
|
||||
phone: z.string().max(64).optional(),
|
||||
email: z.string().email().optional().or(z.literal('')),
|
||||
website: z.string().url().optional().or(z.literal('')),
|
||||
iban: z.string().max(34).optional(),
|
||||
bic: z.string().max(11).optional(),
|
||||
accountHolder: z.string().max(256).optional(),
|
||||
isArchived: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export type CreateMemberClubInput = z.infer<typeof CreateMemberClubSchema>;
|
||||
|
||||
export const UpdateMemberClubSchema = CreateMemberClubSchema.partial().extend({
|
||||
clubId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export type UpdateMemberClubInput = z.infer<typeof UpdateMemberClubSchema>;
|
||||
|
||||
// =====================================================
|
||||
// Club Contacts (Ansprechpartner)
|
||||
// =====================================================
|
||||
|
||||
export const CreateClubContactSchema = z.object({
|
||||
clubId: z.string().uuid(),
|
||||
firstName: z.string().min(1, 'Vorname ist erforderlich').max(128),
|
||||
lastName: z.string().min(1, 'Nachname ist erforderlich').max(128),
|
||||
role: contactRoleSchema.default('sonstige'),
|
||||
phone: z.string().max(64).optional(),
|
||||
email: z.string().email().optional().or(z.literal('')),
|
||||
isPrimary: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export type CreateClubContactInput = z.infer<typeof CreateClubContactSchema>;
|
||||
|
||||
export const UpdateClubContactSchema = CreateClubContactSchema.partial().extend({
|
||||
contactId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export type UpdateClubContactInput = z.infer<typeof UpdateClubContactSchema>;
|
||||
|
||||
// =====================================================
|
||||
// Club Roles (Funktionen im Verband)
|
||||
// =====================================================
|
||||
|
||||
export const CreateClubRoleSchema = z.object({
|
||||
accountId: z.string().uuid(),
|
||||
name: z.string().min(1, 'Name ist erforderlich').max(128),
|
||||
description: z.string().max(512).optional(),
|
||||
sortOrder: z.number().int().default(0),
|
||||
});
|
||||
|
||||
export type CreateClubRoleInput = z.infer<typeof CreateClubRoleSchema>;
|
||||
|
||||
// =====================================================
|
||||
// Association Types (Vereinstypen)
|
||||
// =====================================================
|
||||
|
||||
export const CreateAssociationTypeSchema = z.object({
|
||||
accountId: z.string().uuid(),
|
||||
name: z.string().min(1, 'Name ist erforderlich').max(128),
|
||||
description: z.string().max(512).optional(),
|
||||
sortOrder: z.number().int().default(0),
|
||||
});
|
||||
|
||||
export type CreateAssociationTypeInput = z.infer<typeof CreateAssociationTypeSchema>;
|
||||
|
||||
// =====================================================
|
||||
// Club Fee Types (Beitragsarten)
|
||||
// =====================================================
|
||||
|
||||
export const CreateClubFeeTypeSchema = z.object({
|
||||
accountId: z.string().uuid(),
|
||||
name: z.string().min(1, 'Name ist erforderlich').max(128),
|
||||
description: z.string().max(512).optional(),
|
||||
defaultAmount: z.number().min(0).optional(),
|
||||
isActive: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export type CreateClubFeeTypeInput = z.infer<typeof CreateClubFeeTypeSchema>;
|
||||
|
||||
// =====================================================
|
||||
// Club Fee Billings (Beitragsabrechnungen)
|
||||
// =====================================================
|
||||
|
||||
export const CreateClubFeeBillingSchema = z.object({
|
||||
clubId: z.string().uuid(),
|
||||
feeTypeId: z.string().uuid(),
|
||||
year: z.number().int().min(2000).max(2100),
|
||||
amount: z.number().min(0),
|
||||
dueDate: z.string().optional(),
|
||||
paidDate: z.string().optional(),
|
||||
paymentMethod: paymentMethodSchema.optional(),
|
||||
status: billingStatusSchema.default('offen'),
|
||||
notes: z.string().max(1024).optional(),
|
||||
});
|
||||
|
||||
export type CreateClubFeeBillingInput = z.infer<typeof CreateClubFeeBillingSchema>;
|
||||
|
||||
// =====================================================
|
||||
// Club Notes (Notizen / Aufgaben)
|
||||
// =====================================================
|
||||
|
||||
export const CreateClubNoteSchema = z.object({
|
||||
clubId: z.string().uuid(),
|
||||
title: z.string().min(1, 'Titel ist erforderlich').max(256),
|
||||
content: z.string().optional(),
|
||||
noteType: noteTypeSchema.default('notiz'),
|
||||
dueDate: z.string().optional(),
|
||||
isCompleted: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export type CreateClubNoteInput = z.infer<typeof CreateClubNoteSchema>;
|
||||
|
||||
// =====================================================
|
||||
// Association History (Verbandshistorie)
|
||||
// =====================================================
|
||||
|
||||
export const CreateAssociationHistorySchema = z.object({
|
||||
clubId: z.string().uuid(),
|
||||
year: z.number().int().min(1800).max(2100),
|
||||
memberCount: z.number().int().min(0).optional(),
|
||||
notes: z.string().max(2048).optional(),
|
||||
});
|
||||
|
||||
export type CreateAssociationHistoryInput = z.infer<typeof CreateAssociationHistorySchema>;
|
||||
Reference in New Issue
Block a user