Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 

6.4 KiB

CLAUDE.md – spawntree Timetracker

Projekt-Überblick

Internes Timetracking-Tool (Orientierung an mite.de), zunächst Single-User, geplant als SaaS. Multi-Tenant-Architektur: jeder Account bekommt eine Subdomain und eigene Tenant-DB.

Tech Stack

  • Backend: Symfony 7.4, PHP 8.2+ (DDEV nutzt 8.4), Doctrine ORM, MariaDB 10.11
  • Frontend: Twig, SCSS (Webpack Encore), Vanilla JS (kein Framework, kein jQuery)
  • Export: PhpSpreadsheet (Excel), Dompdf (PDF), natives PHP (CSV)
  • Dev-Umgebung: DDEV, Projektname timetracking, HTTPS-Port 8459
  • Keine Symfony Forms – eigene HTML-Formulare mit fetch()-API

Verzeichnisstruktur

Der Symfony-Code liegt unter httpdocs/. Das ist das Projekt-Root für Symfony.

httpdocs/
├── src/
│   ├── Controller/          # Symfony Controller (Routes via PHP Attributes)
│   ├── Entity/
│   │   ├── Central/         # User, Account, AccountUser, Tokens (Central-DB)
│   │   └── Tenant/          # Client, Project, Service, TimeEntry (Tenant-DB)
│   ├── Repository/
│   │   ├── Central/
│   │   └── Tenant/
│   ├── Service/             # TenantContext, RegistrationService, etc.
│   ├── Doctrine/            # TenantConnectionMiddleware
│   ├── EventSubscriber/     # TenantRequestSubscriber, ArchivedUserSubscriber
│   ├── Security/            # ArchivedUserChecker, AccessDeniedHandler
│   └── Twig/                # AppExtension + Runtime
├── config/
│   ├── packages/
│   │   ├── doctrine.yaml    # Zwei Connections: central + tenant
│   │   └── security.yaml    # Firewall, Access Control, form_login
│   ├── services.yaml        # DI: Tenant-EM explizit an Controller gebunden
│   └── routes.yaml
├── templates/               # Twig-Templates (Atomic Design: atoms/components/sections)
├── assets/
│   ├── app.js               # Webpack-Entry für Timetracking
│   ├── styles/              # SCSS (main.scss als Entry)
│   └── scripts/             # JS-Module (calendar, entries, crud, team, account, report)
├── migrations/
│   ├── central/             # Doctrine-Migrations für Central-DB
│   └── tenant/              # Doctrine-Migrations für Tenant-DB
├── translations/            # messages.de.yaml
└── public/                  # Webroot (index.php, build/)

Multi-Tenant-Architektur

  • Central-DB (db): User, Account, AccountUser, Tokens
  • Tenant-DB (db_{slug}): Client, Project, Service, TimeEntry
  • TenantRequestSubscriber (Prio 20) liest Subdomain → setzt TenantContext
  • TenantConnectionMiddleware schaltet die DB-Connection auf db_{slug} um
  • Zwei Doctrine Entity Manager: central und tenant

Wichtige Befehle

# Dev-Umgebung starten
ddev start

# Datenbank Reset + Seed (innerhalb DDEV)
ddev exec bash 1-reset-and-seed.sh
ddev exec bash 2-update-tenant-db.sh

# Migrations
ddev exec php bin/console doctrine:migrations:migrate --em=central --no-interaction
ddev exec php bin/console doctrine:migrations:migrate --em=tenant --no-interaction

# Frontend Build
ddev exec npm run dev     # Development
ddev exec npm run watch   # Watch-Mode
ddev exec npm run build   # Production

# Cache leeren
ddev exec php bin/console cache:clear

# Deploy
bash httpdocs/deploy.sh

Webpack Encore Entries

Entry Datei Seite
app assets/app.js Timetracking-Woche
crud assets/scripts/crud.js Kunden/Projekte/Services
registration assets/scripts/registration.js Registrierung
team assets/scripts/team.js Team-Verwaltung
account assets/scripts/account.js Account-Einstellungen
report assets/scripts/report.js Report-Seite

Konventionen

  • Durations: Integer (Minuten) in der DB, Eingabeformate: 1:30, 8 12 (von-bis), 1,75 (Dezimal)
  • Rounding: Konfigurierbar per Account.trackingInterval (1/15/30/60 Min)
  • API-Pattern: /api/... Routen, JSON Request/Response, kein CSRF auf API-Endpunkten
  • Rollen: admin (alles), member (eigene + fremde Einträge sehen), tracker (nur eigene)
  • Translations: messages.de.yaml, JS-Strings via window.TT.i18n / window.Report.i18n. Auch Backend-Services (z.B. ReportExportService) nutzen TranslatorInterface — keine hardcoded Strings.
  • SCSS: BEM-ähnlich, Atoms → Components → Sections → Themes
  • CSS Custom Properties: Brand-Farben via :root-Variablen (--color-primary, etc.)

Rollen-System

Rolle Rechte
admin Alles: Team, alle Einträge, Account-Settings
member Eigene Einträge + alle fremden sehen (kein Team-Zugriff)
tracker Nur eigene Einträge

Superadmin = Account-Ersteller, kann Kontoinhaber übertragen und primaryColor setzen.

Services-Injection (services.yaml)

Controller die Tenant-Entities nutzen brauchen den tenant_entity_manager explizit:

  • $tenantEm: '@doctrine.orm.tenant_entity_manager' (TimeTrackingController, ReportController)
  • $em: '@doctrine.orm.tenant_entity_manager' (ClientController, ProjectController, ServiceController)

Report-Exporte

  • Excel (/reports/export/excel): PhpSpreadsheet, Autofilter, Frozen Header, Zebra-Stripes, Summenzeile
  • CSV (/reports/export/csv): Semikolon-Trennzeichen, UTF-8 BOM, deutsche Zahlenformatierung
  • PDF (/reports/export/pdf): Dompdf, A4 Querformat, professioneller Header + Footer

Alle drei nutzen die gleichen Filter-Parameter wie die Report-Seite, exportieren ohne Limit. ReportExportService bereitet Daten zentral in prepareData() auf, formatspezifische Methoden erzeugen die Ausgabe. Tracker sehen nur eigene Einträge.

TenantConnectionMiddleware

Registriert via Service-Tag in services.yaml (nicht via doctrine.yaml — DoctrineBundle 3.x unterstützt middlewares-Config-Key nicht):

App\Doctrine\TenantConnectionMiddleware:
    tags:
        - { name: doctrine.middleware, connection: tenant }