27b1057cd4
Rapport ist jetzt dual: lokal (wie bisher) ODER Cloud auf eigenem Supabase-Server. Beide Modi haben dieselben Funktionen, Cloud zusätzlich Multi-User + Live-Sync. Storage-Architektur - src/storage/adapter.js: einheitliche Promise-API, LocalStorage- und SupabaseAdapter - src/storage/migrations.js: applyMigrations als reine Funktion, für beide Backends - Konfig-driven: VITE_SUPABASE_URL im Production-Build → automatisch Cloud-Modus Postgres-Schema (supabase/migrations/0001–0010) - 29 Tabellen, multi-tenant via studio_id + Row-Level-Security - Audit-Spalten (created_by/updated_by/at) + Trigger - Seed-Trigger pro neuem Studio (Rollen, Templates, Absenz-Typen) - Realtime-Publication für Live-Sync - RPCs: ensure_profile, create_studio_with_admin (mit Personen-Sharing), list_studios, load_persons_for_studio, attach_user_to_studio Cloud-Features (App) - BackendChoice.jsx als Erst-Screen «Lokal oder Cloud» - CloudSetup.jsx: 3-Schritt-Wizard für Erst-Einrichtung - Login.jsx: Modus-Switcher + Server-URL + Studio-Dropdown + Passwort-Vergessen - ResetPassword.jsx: empfängt Mail-Link-Klick via PASSWORD_RECOVERY-Event - Realtime: Änderungen zwischen Browsern ohne Reload sichtbar - Settings → System: Cloud-Verbindung, Studio-Switcher, weiteres Studio anlegen - Settings → Team: Mitarbeiter via Email einladen (Admin-Aktion) - Personen-Sharing: bei neuem Studio Personen aus anderen Studios übernehmen - Reload-Resume: studio_id in sessionStorage, kein erneuter Login nötig Web-Deploy - deploy/docker-compose.yml + nginx.conf: dist/ via nginx-Container, Port 8080 - .env.production.example: Build-time Cloud-URL - DEPLOY.md: Anleitung für LAN-only und extern via Nginx Proxy Manager Doku - README.md: Cloud-Variante prominent erklärt - ARCHITECTURE.md: Storage-Adapter, Migrations, neue Views in Risiko-Tabelle - DEPLOY.md: Schritt-für-Schritt für Mac Mini + NPM Version-Bump auf 0.8.0 in package.json, src-tauri/tauri.conf.json, Cargo.toml. Changelog-Entry im App.jsx-Modal (Karim sieht ihn beim ersten Start). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
117 lines
5.1 KiB
PL/PgSQL
117 lines
5.1 KiB
PL/PgSQL
-- ============================================================================
|
|
-- RAPPORT — Default-Stammdaten pro neuem Studio
|
|
-- ============================================================================
|
|
-- Wenn ein neues Studio angelegt wird (INSERT INTO studios), bekommt es
|
|
-- automatisch:
|
|
-- - eine studio_settings-Zeile (alle Defaults aus CREATE TABLE)
|
|
-- - 4 studio_roles (PL, TS, BL, AS)
|
|
-- - 3 dashboard_templates (admin, projektleiter, mitarbeiter)
|
|
-- - 3 app_roles (r-admin, r-projektleiter, r-mitarbeiter)
|
|
-- - 7 absence_types (Krankheit, Unfall, …)
|
|
-- - 2 letter_templates (Offerte, Zahlungserinnerung)
|
|
--
|
|
-- Quelle der Werte: src/constants.js (`defaultData`) + DEFAULT_ABSENZ_TYPES.
|
|
-- Wenn dort etwas geändert wird, hier nachziehen — und umgekehrt.
|
|
--
|
|
-- SECURITY DEFINER: Funktion läuft mit Postgres-Owner-Rechten, umgeht damit
|
|
-- die RLS-Policies (der gerade anlegende User ist noch nicht studio_member,
|
|
-- könnte sonst nichts einfügen).
|
|
-- ============================================================================
|
|
|
|
create or replace function seed_studio_defaults(s_id uuid)
|
|
returns void
|
|
language plpgsql
|
|
security definer
|
|
as $$
|
|
begin
|
|
-- 1. studio_settings (1 Zeile, alle Defaults aus CREATE TABLE)
|
|
insert into studio_settings (studio_id) values (s_id);
|
|
|
|
-- 2. studio_roles (Rate-Profile)
|
|
insert into studio_roles (studio_id, id, label, rate, sort) values
|
|
(s_id, 'PL', 'Projektleiter/in', 140, 1),
|
|
(s_id, 'TS', 'Technischer Support', 120, 2),
|
|
(s_id, 'BL', 'Bauleiter/in', 135, 3),
|
|
(s_id, 'AS', 'Administrativer Support', 120, 4);
|
|
|
|
-- 3. dashboard_templates (vor app_roles wegen FK)
|
|
insert into dashboard_templates (studio_id, id, name, is_public, layout) values
|
|
(s_id, 'tpl-admin', 'Administrator', true, $j$[
|
|
{"id":"dw-a1","cols":4,"minH":0,"widgets":["kpi-projekte","kpi-stunden","kpi-ausstehend","kpi-umsatz"]},
|
|
{"id":"dw-a2","cols":1,"minH":0,"widgets":["warnungen"]},
|
|
{"id":"dw-a3","cols":2,"minH":0,"widgets":["aktive-projekte","unverrechnete-stunden"]},
|
|
{"id":"dw-a4","cols":2,"minH":0,"widgets":["umsatz-sparkline","offene-offerten"]},
|
|
{"id":"dw-a5","cols":1,"minH":0,"widgets":["letzte-zeiteintraege"]}
|
|
]$j$::jsonb),
|
|
(s_id, 'tpl-projektleiter', 'Projektleiter', true, $j$[
|
|
{"id":"dw-p1","cols":2,"minH":0,"widgets":["kpi-projekte","kpi-stunden"]},
|
|
{"id":"dw-p2","cols":1,"minH":0,"widgets":["warnungen"]},
|
|
{"id":"dw-p3","cols":3,"minH":0,"widgets":["meine-projekte","team-auslastung","offene-offerten"]},
|
|
{"id":"dw-p4","cols":1,"minH":0,"widgets":["letzte-zeiteintraege"]}
|
|
]$j$::jsonb),
|
|
(s_id, 'tpl-mitarbeiter', 'Mitarbeiter', true, $j$[
|
|
{"id":"dw-m1","cols":3,"minH":0,"widgets":["kpi-stunden","ueberstunden","meine-ferien"]},
|
|
{"id":"dw-m2","cols":2,"minH":0,"widgets":["meine-projekte","stunden-woche"]},
|
|
{"id":"dw-m3","cols":1,"minH":0,"widgets":["meine-zeiteintraege"]}
|
|
]$j$::jsonb);
|
|
|
|
-- 4. app_roles (permissions=NULL bedeutet "alle erlaubt")
|
|
insert into app_roles (studio_id, id, name, permissions, dashboard_template_id) values
|
|
(s_id, 'r-admin', 'Administrator',
|
|
null,
|
|
'tpl-admin'),
|
|
(s_id, 'r-projektleiter', 'Projektleiter',
|
|
array['dashboard','projects','time','quotes','personen','mitarbeiter','settings'],
|
|
'tpl-projektleiter'),
|
|
(s_id, 'r-mitarbeiter', 'Mitarbeiter',
|
|
array['dashboard','projects','time','personen','settings'],
|
|
'tpl-mitarbeiter');
|
|
|
|
-- 5. absence_types (aus DEFAULT_ABSENZ_TYPES in constants.js)
|
|
insert into absence_types (studio_id, id, label, color) values
|
|
(s_id, 'krankheit', 'Krankheit', '#8a1a1a'),
|
|
(s_id, 'unfall', 'Unfall', '#b5621e'),
|
|
(s_id, 'intern', 'Intern', '#1a4e8a'),
|
|
(s_id, 'informatik', 'Informatik', '#555'),
|
|
(s_id, 'rechnungswesen', 'Rechnungswesen', '#7a6a00'),
|
|
(s_id, 'weiterbildung', 'Weiterbildung', '#2d6a4f'),
|
|
(s_id, 'militaer', 'Militär / Zivildienst', '#3d3d38');
|
|
|
|
-- 6. letter_templates
|
|
insert into letter_templates (studio_id, id, name, body) values
|
|
(s_id, 'offer', 'Offerte',
|
|
$b$Sehr geehrte/r {{client}}
|
|
|
|
Gerne unterbreiten wir Ihnen die Offerte für das Projekt «{{project}}».
|
|
|
|
[Leistungsumfang]
|
|
|
|
Honorar: CHF [Betrag]
|
|
|
|
Wir freuen uns auf die Zusammenarbeit.
|
|
|
|
Freundliche Grüsse$b$),
|
|
(s_id, 'reminder', 'Zahlungserinnerung',
|
|
$b$Sehr geehrte/r {{client}}
|
|
|
|
Bei einer Überprüfung unserer Buchhaltung stellen wir fest, dass die Rechnung [Nr.] vom [Datum] über CHF [Betrag] noch nicht beglichen ist.
|
|
|
|
Wir bitten Sie höflich, den offenen Betrag innert 10 Tagen zu überweisen.
|
|
|
|
Freundliche Grüsse$b$);
|
|
end$$;
|
|
|
|
-- ─── Trigger: bei jedem Studio-Insert die Defaults reinkippen ──────────────
|
|
create or replace function trg_studios_seed_defaults()
|
|
returns trigger
|
|
language plpgsql
|
|
as $$
|
|
begin
|
|
perform seed_studio_defaults(new.id);
|
|
return new;
|
|
end$$;
|
|
|
|
create trigger studios_seed_defaults
|
|
after insert on studios
|
|
for each row execute function trg_studios_seed_defaults();
|