4cf4ccc5957785b1fca78eedf60972b4a3a42e14
Rows expanded unboundedly because `fixedHeader: true` + a fixed `height` forced a scroll container that filled itself with stretched rows on short result sets. Pagination text rendered white-on-white because the shared button base (app-shell.css) redefines `--app-color` as `--app-primary-inverse` in the button scope, so `color: var(--app-color)` in an override resolves to white. Sort-column headers looked misaligned because Grid.js uses floats to place label and sort icon, which ignore the cell's vertical-align. - Sticky header is now opt-in; default scroll behavior is page-level. - Row heights come from padding + line-height only (no fixed `height`). - Pagination buttons theme via `--app-background-color`/`--app-color` redefinition (the Pico convention) using `--app-form-element-color`, which is not shadowed in the button scope. `margin-inline-start: auto` on `.gridjs-pages` guarantees right-alignment even without a summary. - Sort headers use `inline-block` + `vertical-align: middle` instead of vendor floats so label and icon share one cross-axis center. - Loading UX replaced with a real `<table>` skeleton that mirrors the final column count + outer layout (card + footer with summary and 5 pager placeholders). Grid.js mount point is hidden via `.app-grid-shell-loading > :not(.app-grid-skeleton)` — vendor wraps its own `.gridjs-container` one level deeper, so a direct-child selector on the mount element is required. - Re-fetch state shows a thin top progress bar + faded tbody (GitHub pattern) so existing data stays as context. - `prefers-reduced-motion` disables all skeleton/progress animations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CoreCore (MintyPHP)
CoreCore ist eine mandantenfaehige Admin-Anwendung auf Basis von MintyPHP. Der Fokus liegt auf klarer Rollen-/Rechtestruktur, Tenant-Scope und einer konsistenten Admin-UI.
Funktionsumfang
- Mandantenverwaltung (inkl. Branding/Farbe/Favicon)
- Abteilungen pro Mandant
- Benutzerverwaltung mit Rollen, Sichtbarkeit und Stammdaten
- Rollen- und Berechtigungsverwaltung
- Adressbuch (tenant-scoped, read-only Profilansicht)
- Globale Suche (Admin + Address Book)
- Mail-Versand (SMTP/PHPMailer) inkl. Mail-Log
- Onboarding-PDF fuer Zugangsdaten (Single inline, Bulk als ZIP)
- Passwort-Reset und Remember-Login-Tokens
- Geplante Aufgaben (Scheduler mit Ausfuehrungshistorie)
Tech Stack
- PHP 8.5 (Runtime im Docker-Container)
- MintyPHP Core
- MariaDB 11
- Memcached
- Nginx
- Grid.js (Tabellen)
- PHPMailer
- Dompdf (PDF Rendering)
- endroid/qr-code (QR-Code Rendering)
Schnellstart (Docker)
- Umgebungsdatei anlegen
cp .env.example .env
- Config-Template fuer den App-Bootstrap anlegen
cp config/config.php.example config/config.php
- Container starten
docker compose up --build -d
- App oeffnen
- App: http://localhost:8080
- phpMyAdmin: http://localhost:8081
- Container stoppen
docker compose down
Alternativ ueber den Dev-Orchestrator:
cp config/config.php.example config/config.php
bin/dev init
bin/dev up
bin/dev down
bin/dev console list
bin/dev qa
Produktion (Docker)
Es gibt eine getrennte Produktions-Compose:
- Dev:
/docker-compose.yml - Prod:
/docker-compose.prod.yml
Grundschritte:
.envauf Produktionswerte setzen (siehe/.env.prod.example)- Domain und TLS in
/docker/nginx/prod.confanpassen - Zertifikate unter
/docker/nginx/certs/bereitstellen - Starten:
docker compose -f docker-compose.prod.yml up --build -d
Details stehen in:
/docs/howto-docker-lokal.md/docs/howto-docker-produktiv.md/docs/explanation-docker-betrieb.md(Übersicht)
Seed-Daten
Die Initialisierung liegt in /db/init/init.sql.
Standard-Demo-User:
- E-Mail:
demo@user.com - Passwort:
Demo123 - Tenant:
MusterMandant - Abteilung:
Musterabteilung - Rollen:
Admin,User
Initiale DB-Settings (Seed):
app_title->CoreCoreapp_locale->deapp_theme->lightapp_theme_user->1app_registration->1(aktiv)app_primary_color->#105433api_token_default_ttl_days->90api_token_max_ttl_days->365api_cors_allowed_origins->http://localhost:8080,http://127.0.0.1:8080session_idle_timeout_minutes->30session_absolute_timeout_hours->8user_inactivity_deactivate_days->180user_inactivity_delete_days->365default_tenant_id->MusterMandantdefault_department_id-> AbteilungscodeMUSTER(Musterabteilung)default_role_id-> RolleUSER(User)frontend_telemetry_enabled->1frontend_telemetry_sample_rate->0.2frontend_telemetry_allowed_events->ajax_error,warn_once
Projektstruktur
/configKonfiguration (Routen, Assets, Themes, Module + ENV-Bootstrap)/db/initDatenbankschema + Seeds/libBackend-Code (Repository,Service,Http,Support)/pagesAction-Layer + Views pro Route/templatesLayouts + wiederverwendbare Partials/weboeffentliche Assets (CSS/JS/Vendor)/i18nUebersetzungen/testsPHPUnit-Tests/docsProjekt-Dokumentation
Architektur-Kurzregeln
- Keine DB-Zugriffe in
.phtml-Views. - SQL bleibt in Repositories.
- Geschaeftslogik bleibt in Services.
pages/.../*.phpfungiert als Action-/Controller-Schicht.- Service-Aufloesung in Actions/Helpern ueber
app(Foo::class). new ...Factory()nur im Composition Root (web/index.php+core/App/registerContainer.php) oder in*Factory.php.pages/**/data().phpsind GET-only (gridRequireGetRequest()) und normalisierenlimit/offset/order/dirueber Grid-Helper.- Permissions + Tenant-Scope werden in Actions/Services erzwungen.
- Wiederkehrende UI-Bloecke als Partials zentralisieren.
UI-Konventionen
Edit-Seiten verwenden einheitliche Tab-Struktur:
- erster Tab:
Master data - letzter Tab:
Visibility - optional letzter Tab:
Danger zone
User-Organisation:
- Departments werden tenantweise angezeigt (ein Multi-Select je ausgewaehltem Tenant).
- Nach Tenant-Aenderungen im selben Formular werden Department-Optionen nach Speichern/Reload aktualisiert (kein Live-Update).
Zentrale Partials:
/templates/partials/app-details-titlebar.phtml/templates/partials/app-visibility-status-field.phtml/templates/partials/app-danger-zone-delete-field.phtml/templates/partials/app-details-aside-audit.phtml/templates/partials/app-details-aside-ids.phtml
Onboarding-PDF (User)
- Permission:
users.access_pdf - Single:
admin/users/access-pdf/{uuid}(inline im neuen Tab) - Bulk:
admin/users/access-pdf-bulk(POST, ZIP mit einer PDF pro User) - Hard limit: maximal 100 User pro Bulk-Request
- Benoetigte Extensions:
ext-zip,ext-dom,ext-mbstring - Bestehende Installationen: Schema-/Permission-Updates als idempotentes SQL-Update ausfuehren
Microsoft SSO (Tenant, Entra ID)
- Zentraler Einstieg:
login(optional mit Tenant-Hintlogin?tenant={tenant-slug}) - OIDC-Endpunkte:
auth/microsoft/startundauth/microsoft/callback - Tenant-Config liegt in
tenant_auth_microsoft - Externe User-Linkung liegt in
user_external_identities - Optionaler Profil-Sync pro Tenant:
sync_profile_on_loginaktiviert Sync bei jedem Microsoft-Loginsync_profile_fields:first_name,last_name,phone,mobile,avatar- Default-Felder bei aktivem Sync ohne Auswahl:
first_name,last_name phone/mobile/avatarnutzen Microsoft Graph (User.Read)
- Shared App Credentials werden in Settings gepflegt (optional Tenant-Overrides)
- Permission fuer Tenant-SSO-Konfiguration:
tenants.sso_manage - Bestandsinstallationen: Schema-/Permission-Updates als idempotentes SQL-Update ausfuehren
- Wichtig:
APP_CRYPTO_KEYin.envsetzen, sonst koennen SSO-Secrets nicht gespeichert/verwendet werden
Asset- und Frontend-System
- Core-CSS-Entrypoint:
/web/css/core.css - CSS-Layer/Vendor-Basis:
/web/css/app-layers.css - Vendor-Overrides:
/web/css/vendor-overrides - Asset-Gruppen:
/config/assets.php - Template-Styles:
renderTemplateStyles('default'|'login') - Seiten-Styles per Action:
Buffer::set('style_groups', ...) - CSS-Contract-Check:
bin/css-contract-check.sh - JS-Bootstrap:
/web/js/app-boot.js(frueh, ohne Module)/web/js/app-init.js(Standardseiten)/web/js/app-login-init.js(Loginseiten)
Qualitaetschecks
Alle Befehle aus dem Projekt-Root ausfuehren.
PHPUnit
docker compose exec php vendor/bin/phpunit --bootstrap vendor/autoload.php
Gezielt:
docker compose exec php vendor/bin/phpunit --bootstrap vendor/autoload.php tests/I18n/TranslationKeysTest.php
docker compose exec php vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Repository/RepoQueryTest.php
PHPStan
docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress
Coding Style (PHP CS Fixer)
CI-Check (ohne Dateien zu aendern):
docker compose exec php vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --diff --verbose
Lokal automatisch formatieren:
docker compose exec php composer cs:fix
Frontend-JS Smoke-Check (ohne Node)
- Browser-DevTools öffnen (Console + Preserve log)
- Kernflows klicken: Users, Address-Book, Audit-Listen, Filter setzen/zurücksetzen
- Keine JavaScript-Errors/Unhandled-Rejections in der Console
Dokumentation
/docs/index.mdDokumentationsstart mit chronologischem Lernpfad und Referenzbereichen/docs/tutorial-02-systemueberblick.mdEinstieg in Produktbild und Kernprinzipien/docs/tutorial-03-setup-und-erster-login.mdlokales Setup und erster Smoke-Test/docs/reference-domain-glossar.mdzentrale Begriffe fuer Tenant/Role/Scope/docs/tutorial-04-architektur-request-flow.mdRequest-Flow und Schichtmodell/docs/tutorial-07-security-tenant-scope.mdSecurity-Grundlagen vor Feature-Arbeit/docs/tutorial-08-api-erweitern.mdverbindlicher API-Aenderungsablauf inkl. OpenAPI
Troubleshooting
- Verhalten wirkt stale nach groesseren Refactors:
docker compose restart php nginx
- Uebersetzungs-Keys fehlen:
docker compose exec php vendor/bin/phpunit --bootstrap vendor/autoload.php tests/I18n/TranslationKeysTest.php
- Favicon/Logo aus Storage fehlt:
- Symlink und Tenant-Kontext pruefen (
web/favicon/tenants -> storage/tenants).
- Symlink und Tenant-Kontext pruefen (
Lizenz
MIT (siehe /LICENSE.md)
Description
Languages
PHP
70.8%
HTML
11.8%
JavaScript
10.4%
CSS
6.6%
Shell
0.3%
Other
0.1%