Spec: Projektarten, Zuordnungs-Ablauf und Bereich Services

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-30 18:25:27 +00:00
1 parent b45108c1de
commit 7a4af8e3c8
1 file changed
+143
@@ -0,0 +1,143 @@
# Projektarten, Zuordnungs-Ablauf und Bereich „Services“ – Design
Stand: 2026-09-30 · Status: vom Nutzer im Chat freigegeben (Abschnitte 1–3), Spec zur Durchsicht
Gesamtvorhaben in drei Teilprojekten, **nacheinander**: jedes wird gebaut, geht live und wird vom Nutzer
live getestet, **bevor** das nächste beginnt.
1. **Projektarten + Services** (diese Spec)
2. Preview 2.0 (statisch / Build / Dev-Server bei Bedarf, Build ohne Portal-Geheimnisse) – eigene Spec
3. Funktion „Website-Analyse“ (Einwilligung, eigene Auswertung, Heatmap, Rechtstexte) – eigene Spec
Recherchen dazu: `/root/reports/analyse-funktion-2026-09-30/` (projekttypen-preview.md, technik-analyse-heatmap.md,
recht-tracking-deutschland.md).
## Ziel
Jedes Repo hat eine **Art** (Website, Web-App, …). Egal ob es in Gitea oder im WOMS angelegt wird, wird
die anlegende Person durch einen Ablauf geführt, bis Art, Kunde und Ticket feststehen. Es entstehen
**keine leeren Tickets** und **keine verwaisten Kunden-Repos**. Im WOMS zeigt ein Bereich „Services“ unsere
Leistungsarten, was je Art möglich ist und welche Projekte welche Art haben.
## Entscheidungen (vom Nutzer)
| # | Entscheidung |
|---|---|
| P1 | Arten: **Website, Web-App, App, Migration/Umzug, Automation/Skript, Sonstiges**. Landingpage und Shop laufen als Website; Wartung ist ein Abo, keine Art; Template bleibt eigene Markierung. |
| P2 | Bestehende Repos bekommen **keine automatische Art**; sie stehen auf „Art fehlt“, bis jemand sie von Hand setzt. |
| P3 | Gitea bleibt erlaubt. Wer ein Repo in Gitea anlegt, bekommt eine **Mail** (Muster Push-Zeitmail) mit Link in den Zuordnungs-Ablauf. |
| P4 | Der Ablauf gilt **auch** für den Mail-Link, **mit WOMS-Login** (inkl. 2FA). |
| P5 | Ablauf: Art → Für einen Kunden? → Kunde → Ticket (vorhandenes oder neu) → Abschließen. Ein neues Ticket entsteht **erst beim Abschließen**. |
| P6 | Bereich „Services“ = Variante **B**: Katalog + Projektlisten je Art. Funktionen werden **nur am Projekt** geschaltet. |
| P7 | Jetzt nur das Gerüst und als Funktion „Website-Analyse“ (Status *In Entwicklung*). Weitere Funktionen fragt Claude am Ende ab. |
## 1. Datenmodell und Erkennung
**`websiteProjects` – neue Attribute** (Schema-Skript `scripts/projektarten-setup.mjs`, idempotent, Muster
`portal-setup.mjs`):
- `kind` (string 20, optional): `website` | `webapp` | `app` | `migration` | `automation` | `other`; leer = Art fehlt
- `forCustomer` (boolean, optional; leer = noch nicht beantwortet)
- `kindSetBy` (string 64), `kindSetAt` (ISO-String)
- `createdByGitea` (string 64): Gitea-Login der anlegenden Person (für die Mail)
- `assignMailSentAt`, `assignReminderSentAt` (ISO-Strings)
`projectType` (preview/website/template/repo) bleibt unverändert und regelt weiter nur das Hosting.
**Katalog** `server/services/projectKinds.js` (einzige Quelle der Wahrheit):
```js
KINDS = [
{ key: 'website', label: 'Website', preview: 'yes', features: ['analyse'], description: '…' },
{ key: 'webapp', label: 'Web-App', preview: 'possible', features: [], … },
{ key: 'app', label: 'App', preview: 'no', … },
{ key: 'migration', label: 'Migration/Umzug', preview: 'no', ticketType: 'Migration', … },
{ key: 'automation', label: 'Automation/Skript', preview: 'no', … },
{ key: 'other', label: 'Sonstiges', preview: 'no', … },
]
FEATURES = { analyse: { label: 'Website-Analyse', status: 'in_development' } }
```
Ausgeliefert über `GET /api/admin/services` (Admin-JWT wie die anderen WOMS-Aufrufe). WOMS hat keine eigene Kopie.
**Erkennung neuer Repos**
1. Org-Webhook WEBklar (Hook 112) bekommt zusätzlich das Ereignis `repository` (created). Der Handler in
`server/routes/webhook/gitea.js` legt das Projekt-Dokument an (`upsertWebsiteProjectByRepo`), setzt
`createdByGitea = sender.login` und stößt die Mail an. `deleted` wird ignoriert (Verhalten wie heute).
2. Job alle 10 Minuten in `server/index.js`: `importNewRepos()` (dieselbe Funktion wie „Suche neue Repos“)
findet Repos in persönlichen Konten. `createdByGitea` = Repo-Owner (bei Org-Repos ohne Hook-Ereignis leer
⇒ keine Mail, erscheint nur in „Art fehlt“).
3. **Nur neu angelegte** Projekt-Dokumente lösen eine Mail aus (Dokument in diesem Lauf erzeugt **und**
Repo `created_at` jünger als 24 h). Beim ersten Lauf nach dem Livegang entsteht so keine Mail-Welle.
4. Empfänger = Mitarbeiter, dessen Kürzel/Anzeigename zum Gitea-Login passt (`findEmployeeForPusher` aus
`pushTimeMail.js` wiederverwenden). Kein Treffer ⇒ keine Mail, Log-Zeile.
5. Mail (neue Staff-Vorlage **T16 „Neues Repo zuordnen“**, Stil wie T15): Repo-Name, Gitea-Link, wer, wann,
Knopf „Jetzt zuordnen“ ⇒ `https://ticket.webklar.com/projects/zuordnen/<projektId>`.
6. Erinnerung: 1× nach 24 h, falls `kind` noch leer (derselbe 10-Minuten-Job). Danach nichts mehr.
## 2. Zuordnungs-Ablauf (WOMS)
Route **`/projects/zuordnen/:id`** (ProtectedRoute, also WOMS-Login + 2FA). Kopf: Repo-Name, Gitea-Link,
angelegt von/am. Schritte mit „Zurück“/„Weiter“:
1. **Was ist das?** sechs Knöpfe aus dem Katalog.
2. **Für einen Kunden?** Ja / Nein (internes Projekt ⇒ weiter zu 5).
3. **Welcher Kunde?** Suche über Kunden; „Neuer Kunde“ öffnet das vorhandene Kunden-Formular und kehrt mit
dem neuen Kunden zurück.
4. **Welches Ticket?** offene Tickets (Status ≠ Closed/Cancelled) dieses Kunden, oder „Neues Ticket anlegen“
mit vorausgefüllten, änderbaren Feldern: Thema „<Art> – <Repo-Name>“, Ticket-Art `Migration` bei Art
Migration sonst `Webpage`, Angefragt von = Ansprechpartner des Kunden, Start heute, Details = Repo-Link.
5. **Zusammenfassung + „Abschließen“.** Bei Art Website mit Kunde: Preview-Schalter an, Subdomain-Vorschlag
aus dem Repo-Namen (änderbar, Prüfung wie heute).
**Abschließen** = ein Aufruf `POST /api/admin/website-projects/:id/zuordnen`
`{ kind, forCustomer, customerId?, ticketId? | newTicket?, preview?: { subdomain } }`. Der Server:
1. prüft Eingaben (Art aus Katalog, Kunde existiert, Ticket gehört zum Kunden und ist offen);
2. legt ggf. das Ticket an (neue Funktion `createTicketForProject` in `ticketAdmin.js`, Felder wie das
WOMS-Formular);
3. ruft `assignProject(projectId, { customerId, ticketId })` (Umzug in die Org, Pushes nachtragen);
4. setzt `kind`, `forCustomer`, `kindSetBy` (Mitarbeiter aus dem JWT), `kindSetAt`;
5. bei Website + Preview: `updateWebsiteProject(id, { subdomain, projectType: 'preview' })`.
Scheitert Schritt 3 nach einem neu angelegten Ticket, wird das Ticket wieder gelöscht (kein leeres Ticket).
Antwort `{ ticketId }` ⇒ WOMS springt ins Ticket.
Weitere Einstiege:
- WOMS „Neues Repo anlegen“ (Projekte): nach Name/Vorlage direkt weiter in diesen Ablauf.
- Ticket ⇒ „Projekt zuweisen“: Kunde/Ticket bekannt; fehlt die Art, fragt ein kleiner Dialog nur Schritt 1.
- Bereits zugeordnet (`kind` gesetzt) ⇒ Seite zeigt „Bereits erledigt von <Name> am <Datum>“ mit Link zum
Ticket; Art bleibt dort änderbar.
## 3. Bereich „Services“ (WOMS)
Menüpunkt **„Services“** neben „Projekte“, Route `/services`. Daten: `GET /api/admin/services` liefert
Katalog + Projekte gruppiert nach `kind` (Repo, Kunde, Ticket, Preview-URL, Funktionszustände).
- Eine Karte je Art: Name, Beschreibung, Anzahl, **mögliche Funktionen** mit Status (Website-Analyse:
*In Entwicklung*), **Projekte dieser Art** als Liste (Klick ⇒ Ticket bzw. Projekt).
- Karte **„Art fehlt“**: alle Repos ohne `kind`, je mit Knopf „Zuordnen“ ⇒ Ablauf aus Abschnitt 2.
- Kein Umschalten hier. Umschalten der Funktionen kommt mit Teilprojekt 3 **am Projekt** (Ticket ⇒ Projekte).
- Kundenportal: Tab „Funktionen“ zeigt dem Kunden nur Funktionen, die laut Katalog zur Art seines Projekts
passen (Anbindung an die Funktionen-Spec, sobald diese live ist).
## Fehlerfälle
- Gitea-Login ohne passenden Mitarbeiter ⇒ keine Mail, Projekt steht in „Art fehlt“.
- SMTP-Fehler ⇒ `assignMailSentAt` bleibt leer, nächster Job-Lauf versucht es erneut (höchstens 3 Versuche).
- Zwei Personen schließen gleichzeitig ab ⇒ zweiter Aufruf bekommt 409 „bereits zugeordnet“.
- Appwrite nicht erreichbar ⇒ Job überspringt den Lauf, keine Teil-Zustände.
## Tests
- Unit (Portal, `node --test`): Katalog, Eingabeprüfung, Mail-Auswahl „nur neue Repos“, Erinnerung,
Rückbau des Tickets bei Fehler, 409 bei Doppel-Abschluss.
- WOMS (vitest): Schritt-Logik des Ablaufs, Vorbelegung des neuen Tickets.
- Playwright gegen Fixture-Daten: Ablauf komplett (intern, Kunde + vorhandenes Ticket, Kunde + neues Ticket),
Services-Seite.
- Live-Test mit dem Nutzer: Repo in Gitea anlegen ⇒ Mail ⇒ Ablauf ⇒ Ticket/Preview prüfen; danach aufräumen.
## Livegang (je Schritt mit OK des Nutzers)
L1 Schema-Skript · L2 Portal pushen (main) · L3 Hook 112 um `repository` erweitern · L4 WOMS pushen (test) ·
L5 Live-Test mit dem Nutzer · danach erst Teilprojekt 2.
## Nicht Teil dieser Spec
Preview-Modi/Dev-Server (Teilprojekt 2), Analyse und deren Rechtstexte (Teilprojekt 3), weitere Funktionen
im Katalog (werden am Ende beim Nutzer abgefragt), Umbenennen/Abschaffen von `projectType`.