Consolidates the previously separate omsorgapp and omsorgCore repos (each had their own nested .git with GitHub history) plus the old root-level website/mitarbeiter-app into a single monorepo, matching the structure already documented in the root CLAUDE.md. Also moves the PHP employee app aside as omsorgWeb/mitarbeiter-app-legacy/ to serve as a template for a ground-up rewrite. Fixes .gitignore in the same pass: the config-secrets/uploads/data patterns were unanchored (relative to repo root, not depth-agnostic), so they silently stopped matching once the app moved under omsorgWeb/. Patterns are now **/-prefixed and cover both mitarbeiter-app and mitarbeiter-app-legacy, keeping DB/SMTP credentials and uploaded employee documents out of version control. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
8.4 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is the OMSORG website plus OMSORG Connect, an internal employee web app ("Mitarbeiter-App") for a German nursing care company. There is no build system — everything is plain PHP, HTML, CSS, and vanilla JS deployed directly to a web server (htdocs).
This project is one part of the OMSORG monorepo — see the root CLAUDE.md (../CLAUDE.md) for the overall platform picture and ../REQUIREMENTS.md for functional/non-functional requirements with FR-IDs. This app is the reference implementation for OMSORG Connect; its MySQL database is expected to eventually move behind the shared omsorgCore backend (currently empty, planned as C#/.NET + PostgreSQL) rather than staying a standalone data store.
Deployment
Upload the full directory contents to the server's htdocs folder and overwrite existing files. After deploying, clear the browser cache or test in an incognito window.
There are no build steps, package managers, or test runners.
Architecture
Public website (/)
Static HTML with a contact form:
index.html— main landing pagesend-form.php— handles the public contact form, sends email via PHPmail()send-status-template.php— reusable status page template for form resultsdatenschutz.html,impressum.html— legal pages
Employee app (/mitarbeiter-app/)
A session-based PHP app with no framework, backed by a MySQL database (PDO).
Entry point
index.php— login page. Redirects topages/dashboard.phpif already logged in. Includes rate limiting: 5 failed attempts trigger a 10-minute lockout.setup.php— one-time setup script (run once after first deploy to bootstrap the DB)
Library (lib/)
auth.php— included at the top of every protected page. Providesrequire_login(),require_admin(),is_logged_in(),is_admin(),current_user(),current_name(),current_username(),csrf_token(),csrf_field(),verify_csrf(),e(). Users are stored in theusersDB table. Login is case-insensitive on username.db.php— providesdb(): PDO(singleton). Reads credentials fromconfig.php, runs pending migrations on first connection, and setsPDO::ERRMODE_EXCEPTION.config.php— returns array with MySQL DSN/credentials, SMTP settings, and mail recipient addresses. Import with$config = require __DIR__ . '/config.php';.layout.php— shared sidebar layout. Calllayout_start($title, $current_page)andlayout_end()to wrap page content. Renders the sidebar nav, user avatar, and injects the service worker.mail.php— providessmtp_send($cfg, $to, $subject, $body, $attach = []). Raw SMTP implementation (no PHPMailer), supports SSL (port 465) and STARTTLS (port 587), and optional file attachments.
Pages (pages/)
Each page is a standalone PHP file using layout_start/layout_end:
dashboard.php— main landing page after login; shows news and quick linksstundennachweis.php— upload monthly timesheet (PDF/image)urlaubsantrag.php— vacation request formabwesenheitsantrag.php— absence request form (sick leave, etc.)benefitsantrag.php— employee benefits requestfortbildungsantrag.php— training request with optional file uploadeinsatzbewertung.php— rate a deployment/assignmenteinsatzanweisung.php— view/download personal assignment instructions (PDF)dokumentenarchiv.php— personal document archive (upload/view own documents)downloads.php— company-wide file downloads (admin-managed)dienstplan.php— view personal shift schedulewerben.php— refer a new employeesettings.php— profile settings: change password, upload avataradmin.php— admin-only: tabs for Anträge, Nutzer, Dienstplan, Downloads, Fortbildungsmaterial, Stundennachweis, Bewertungen, News, Einsatzanweisung, Login-Versuche (failed login attempts: lists recent attempts, shows currently locked IPs, and clears thelogin_attemptstable)admin-dienstplan.php— admin shift planner view- Serve pages (
*-serve.php,dokument-serve.php,download-serve.php, etc.) — stream protected files fromuploads/,downloads/,fortbildung-materials/with auth check
Actions (actions/)
POST-only endpoints, each does one thing and redirects back:
submit-urlaubsantrag.php,submit-abwesenheitsantrag.php,submit-benefitsantrag.php,submit-fortbildungsantrag.php,submit-stundennachweis.php,submit-einsatzbewertung.php,submit-werben.php— insert into the matchingrequests_*DB table, send notification email viasmtp_send()admin-action.php— admin status updates (accept/reject requests), user management (add/delete/reset password), news CRUDsave-dienstplan.php— admin saves shift entriesdownloads-action.php,downloads-reorder.php— admin manages downloadable filesfortbildung-material-action.php,fortbildung-material-reorder.php— admin manages training materialseinsatzanweisung-action.php— admin uploads/assigns personal instruction PDFsupload-dokument.php,delete-dokument.php— user document archive managementupload-avatar.php— user uploads profile picture (stored inassets/avatars/)save-profile.php— user changes own passwordlogout.php— destroys session, redirects to../index.php
Frontend
app.css— all styles (no framework)manifest.webmanifest+service-worker.js— PWA support ("Add to Home Screen")
Database schema (MySQL)
Managed via migrations in migrations/. Key tables:
users— id, username, name, role, password_hash, active, created_at, email, telefon, avatarrequests_urlaubsantrag— vacation requests (von, bis, vertretung, nachricht, status, admin_note)requests_abwesenheitsantrag— absence requests (von, bis, grund, vertretung, nachricht, status, admin_note)requests_benefitsantrag— benefits requestsrequests_fortbildungsantrag— training requests (with optional file upload)requests_stundennachweis— timesheet uploads (monat, filename)requests_werben— employee referralsdienstplan— shift schedule (user_id, date, schicht; UNIQUE on user_id+date)downloads— company download files (title, filename, sort_order)fortbildung_materials— training material files (title, filename, sort_order)dokumente— user personal document archive (user_id, kategorie, filename)einsatzbewertungen— deployment ratingseinsatzanweisung— one PDF per user (UNIQUE on user_id)news— company news posts (title, text, date)schema_migrations— tracks applied migrations
Migration system
db.php runs _run_migrations() on every connection. Migrations are PHP files in migrations/ returning ['description' => ..., 'up' => [...SQL...]]. They are applied in filename order and tracked in schema_migrations. Already-existing DBs without the migrations table are stamped as fully applied on first run.
File storage
Uploaded files are stored outside webroot or protected by .htaccess:
uploads/— user form attachments (stundennachweis, fortbildung, einsatzanweisung). Filename pattern:{date}_{username}_{type}_{randomhex}.{ext}downloads/— admin-managed company downloads (hashed filenames)fortbildung-materials/— admin-managed training materials (hashed filenames)assets/avatars/— user profile pictures ({username}_{randomhex}.{ext})
Allowed upload types: pdf, doc, docx, jpg, jpeg, png. Max size: 12 MB.
Security conventions
lib/is blocked from direct HTTP access vialib/.htaccessuploads/,downloads/,fortbildung-materials/,data/,migrations/all have.htaccessdenying direct access; files are served only through*-serve.phppages with auth checks- All output uses
e()(alias forhtmlspecialchars) to prevent XSS - Every POST form includes a CSRF token (
csrf_field()in form,verify_csrf()in action) - Login rate-limiting: 5 failures → 10-minute session lockout
- Uploads are renamed to random hex filenames before storage
Email (configured in config.php)
- SMTP via
smtp_send()inlib/mail.php; credentials inconfig.php mail_info→info@omsorg-pflegedienste.de(Geschäftsführung)mail_sabrina→s.berggoetz@omsorg-pflegedienste.de(Disposition)mail_from→no-reply@omsorg-pflegedienste.de