diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f703cdc --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,81 @@ +# Projektkonventionen + +## Architektur: Actions (Request-Command-Response) + +Jede fachliche Operation wird in eine eigene Action ausgelagert, die aus drei Klassen besteht. +Pfad: `app/Domains/{Domain}/Actions/{ActionName}/` + +### Struktur +{ActionName}Request.php → Eingabedaten (Konstruktor oder Factory-Methoden) {ActionName}Command.php → Logik, ruft execute(): {ActionName}Response auf {ActionName}Response.php → Rückgabedaten (public Properties) + + +### Regeln +- Der Controller enthält **keine** fachliche Logik – nur Absicherung, Action-Aufruf und HTTP-Response +- Commands sind nicht statisch und werden immer instanziiert +- Hat ein Request mehrere Varianten, werden **Factory-Methoden** (`forX()`) statt mehrerer Konstruktoren verwendet +- Aufrufreihenfolge im Controller: `new Request → new Command(request) → command->execute() → Response verwenden` + +--- + +## Controller + +- Alle Controller erben von `App\Scopes\CommonController` +- `CommonController` stellt folgende Repositories bereit (keine eigene Instanziierung nötig): + - `$this->eventParticipants` → `EventParticipantRepository` + - `$this->events` → `EventRepository` + - `$this->invoices` → `InvoiceRepository` + - `$this->costUnits` → `CostUnitRepository` + - `$this->users` → `UserRepository` + - `$this->tenant` → aktueller `Tenant` + +- Die Controller besitzen ausschließlich eine __invoke() - Funktion +- Für die Speichern-Actions werden separate Controller-Klassen erstellt (z. B. `StoreEventParticipantController`) +--- + +## Repositories + +- Datenbankzugriffe gehören **immer** ins Repository, nie direkt in Controller oder Actions +- Sicherheitschecks (z. B. „gehört diese Teilnahme dem eingeloggten User?") werden als eigene Repository-Methoden gekapselt +- Tenant-Filter: `app('tenant')->slug` +- Eingeloggter User: `auth()->user()` + +--- + +## Models / Ressourcen + +- Models erben von `App\Scopes\InstancedModel` (mit globalem `SiteScope`) +- `$model->toResource()->toArray($request)` liefert das aufbereitete Array über die zugehörige Resource-Klasse +- Resource-Klassen liegen in `app/Resources/{ModelName}Resource.php` + +--- + +## Tenant + +- Der aktuelle Tenant ist per `app('tenant')` verfügbar (gesetzt durch `IdentifyTenant`-Middleware) +- Tenant-Slug: `app('tenant')->slug` +- Jede tenant-spezifische DB-Abfrage filtert auf `['tenant' => app('tenant')->slug]` + +--- + +## Routing + +- API-Routen liegen in `app/Domains/{Domain}/Routes/api.php` +- Alle Routen sind in `IdentifyTenant::class`-Middleware gewrappt +- Authentifizierte Routen zusätzlich in `['auth']`-Middleware + +--- + +## Mails + +- Mails erben von `Illuminate\Mail\Mailable` +- Attachments werden über `Attachment::fromData(fn () => $content, $filename)->withMime(...)` angehängt +- Werden Daten sowohl in `content()` als auch in `attachments()` benötigt, wird eine **private Hilfsmethode mit Lazy-Caching** verwendet (einmaliges Berechnen, Ergebnis in private Property speichern) +- Blade-Templates referenzieren Mail-Attachments per `cid:`-Link: `...` + +## Actions + - Die Actions sind in `app/Domains/{Domain}/Actions/{ActionName}/`-Verzeichnissen organisiert + - Jede Action besitzt einen Request, einen Command und einen Response + - Der Request besitzt auschließlich einen Konstruktor, der die notwendigen Parameter annimmt + - Die Response-Klasse enthält ausschließlich die notwendigen Daten für die Antwort + - Die Action-Klasse enthält die Logik für die Verarbeitung der Anfrage und die Generierung der Antwort + - Die Logik wird in einer execute() - Funktion innerhalb des Commands impplementiert. Private Funktionen, für ausgelagerte Prozesse sind zulässig, wenn der Code damit lesbarer wird. diff --git a/aktuell.png b/aktuell.png new file mode 100644 index 0000000..93414e2 Binary files /dev/null and b/aktuell.png differ diff --git a/app/Domains/Dashboard/Views/Dashboard.vue b/app/Domains/Dashboard/Views/Dashboard.vue index 4adb26d..7ce9153 100644 --- a/app/Domains/Dashboard/Views/Dashboard.vue +++ b/app/Domains/Dashboard/Views/Dashboard.vue @@ -1,12 +1,478 @@ + + diff --git a/app/Domains/Dummy/Controllers/DummyController.php b/app/Domains/Dummy/Controllers/DummyController.php new file mode 100644 index 0000000..7a12820 --- /dev/null +++ b/app/Domains/Dummy/Controllers/DummyController.php @@ -0,0 +1,14 @@ +render(); + } +} diff --git a/app/Domains/Dummy/Routes/web.php b/app/Domains/Dummy/Routes/web.php new file mode 100644 index 0000000..2e3ea97 --- /dev/null +++ b/app/Domains/Dummy/Routes/web.php @@ -0,0 +1,6 @@ +name('dummy'); diff --git a/app/Domains/Dummy/Views/Dummy.vue b/app/Domains/Dummy/Views/Dummy.vue new file mode 100644 index 0000000..23a5556 --- /dev/null +++ b/app/Domains/Dummy/Views/Dummy.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/app/Domains/Rechnung/Controllers/RechnungController.php b/app/Domains/Rechnung/Controllers/RechnungController.php new file mode 100644 index 0000000..2885f9a --- /dev/null +++ b/app/Domains/Rechnung/Controllers/RechnungController.php @@ -0,0 +1,14 @@ +render(); + } +} diff --git a/app/Domains/Rechnung/Routes/web.php b/app/Domains/Rechnung/Routes/web.php new file mode 100644 index 0000000..aecab1b --- /dev/null +++ b/app/Domains/Rechnung/Routes/web.php @@ -0,0 +1,6 @@ +name('rechnung'); diff --git a/app/Domains/Rechnung/Views/Rechnung.vue b/app/Domains/Rechnung/Views/Rechnung.vue new file mode 100644 index 0000000..f8efa42 --- /dev/null +++ b/app/Domains/Rechnung/Views/Rechnung.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/app/Domains/Schulbefreiung/Controllers/SchulbefreiungController.php b/app/Domains/Schulbefreiung/Controllers/SchulbefreiungController.php new file mode 100644 index 0000000..e178c98 --- /dev/null +++ b/app/Domains/Schulbefreiung/Controllers/SchulbefreiungController.php @@ -0,0 +1,14 @@ +render(); + } +} diff --git a/app/Domains/Schulbefreiung/Routes/web.php b/app/Domains/Schulbefreiung/Routes/web.php new file mode 100644 index 0000000..1d3d2dc --- /dev/null +++ b/app/Domains/Schulbefreiung/Routes/web.php @@ -0,0 +1,6 @@ +name('schulbefreiung'); diff --git a/app/Domains/Schulbefreiung/Views/Schulbefreiung.vue b/app/Domains/Schulbefreiung/Views/Schulbefreiung.vue new file mode 100644 index 0000000..9955160 --- /dev/null +++ b/app/Domains/Schulbefreiung/Views/Schulbefreiung.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/app/Domains/Spendenquittung/Controllers/SpendenquittungController.php b/app/Domains/Spendenquittung/Controllers/SpendenquittungController.php new file mode 100644 index 0000000..fd18cf0 --- /dev/null +++ b/app/Domains/Spendenquittung/Controllers/SpendenquittungController.php @@ -0,0 +1,14 @@ +render(); + } +} diff --git a/app/Domains/Spendenquittung/Routes/web.php b/app/Domains/Spendenquittung/Routes/web.php new file mode 100644 index 0000000..84a4ba4 --- /dev/null +++ b/app/Domains/Spendenquittung/Routes/web.php @@ -0,0 +1,6 @@ +name('spendenquittung'); diff --git a/app/Domains/Spendenquittung/Views/Spendenquittung.vue b/app/Domains/Spendenquittung/Views/Spendenquittung.vue new file mode 100644 index 0000000..7f6d4bd --- /dev/null +++ b/app/Domains/Spendenquittung/Views/Spendenquittung.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/app/Domains/Teilnahmebescheinigung/Controllers/TeilnahmebescheinigungController.php b/app/Domains/Teilnahmebescheinigung/Controllers/TeilnahmebescheinigungController.php new file mode 100644 index 0000000..93c5a70 --- /dev/null +++ b/app/Domains/Teilnahmebescheinigung/Controllers/TeilnahmebescheinigungController.php @@ -0,0 +1,14 @@ +render(); + } +} diff --git a/app/Domains/Teilnahmebescheinigung/Routes/web.php b/app/Domains/Teilnahmebescheinigung/Routes/web.php new file mode 100644 index 0000000..3420d32 --- /dev/null +++ b/app/Domains/Teilnahmebescheinigung/Routes/web.php @@ -0,0 +1,6 @@ +name('teilnahmebescheinigung'); diff --git a/app/Domains/Teilnahmebescheinigung/Views/Teilnahmebescheinigung.vue b/app/Domains/Teilnahmebescheinigung/Views/Teilnahmebescheinigung.vue new file mode 100644 index 0000000..bca2820 --- /dev/null +++ b/app/Domains/Teilnahmebescheinigung/Views/Teilnahmebescheinigung.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/resources/js/app.js b/resources/js/app.js index 5902847..ec94d36 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -8,9 +8,25 @@ import 'vue3-toastify/dist/index.css' import { library } from '@fortawesome/fontawesome-svg-core' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' -import { faUser, faTrash, faCheck, faGear, faHouse } from '@fortawesome/free-solid-svg-icons' +import { + faUser, faTrash, faCheck, faGear, faHouse, + faMoneyBillWave, faFolder, faEnvelope, faArrowUpRightFromSquare, + faStar, faClock, faCircleQuestion, faSliders, + faBell, faMagnifyingGlass, + faGraduationCap, faFileInvoice, faHeart, faCertificate, + faChevronRight, faArrowRightFromBracket, faCube, + faTriangleExclamation, +} from '@fortawesome/free-solid-svg-icons' -library.add(faUser, faTrash, faCheck, faGear, faHouse) +library.add( + faUser, faTrash, faCheck, faGear, faHouse, + faMoneyBillWave, faFolder, faEnvelope, faArrowUpRightFromSquare, + faStar, faClock, faCircleQuestion, faSliders, + faBell, faMagnifyingGlass, + faGraduationCap, faFileInvoice, faHeart, faCertificate, + faChevronRight, faArrowRightFromBracket, faCube, + faTriangleExclamation, +) InertiaProgress.init() diff --git a/resources/js/layouts/AppLayout.vue b/resources/js/layouts/AppLayout.vue index dbe9a1f..1da1970 100644 --- a/resources/js/layouts/AppLayout.vue +++ b/resources/js/layouts/AppLayout.vue @@ -1,129 +1,249 @@ diff --git a/routes/web.php b/routes/web.php index dcddfb2..c64f1b3 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,3 +3,8 @@ use Illuminate\Support\Facades\Route; require_once __DIR__.'/../app/Domains/Dashboard/Routes/web.php'; +require_once __DIR__.'/../app/Domains/Schulbefreiung/Routes/web.php'; +require_once __DIR__.'/../app/Domains/Rechnung/Routes/web.php'; +require_once __DIR__.'/../app/Domains/Spendenquittung/Routes/web.php'; +require_once __DIR__.'/../app/Domains/Teilnahmebescheinigung/Routes/web.php'; +require_once __DIR__.'/../app/Domains/Dummy/Routes/web.php';