127 lines
5.7 KiB
Markdown
127 lines
5.7 KiB
Markdown
# 09 Anleitung für Entwickler
|
||
|
||
## Einrichten
|
||
|
||
```bash
|
||
npm install
|
||
cd server && npm install && cp .env.example .env # Werte anpassen
|
||
npm start # Backend :5007
|
||
cd .. && npm run dev # Frontend :3101, /api wird geproxied
|
||
```
|
||
|
||
Lokal braucht es eine erreichbare MariaDB mit der Datenbank `icon_CRM`; das
|
||
Schema legt das Backend beim Start selbst an.
|
||
|
||
```bash
|
||
npx tsc --noEmit # Typprüfung, laeuft nicht automatisch im Build
|
||
npm run build # dist/
|
||
```
|
||
|
||
## Aufbau
|
||
|
||
```
|
||
src/
|
||
App.tsx Layout, gemeinsame Daten, Ansichtswechsel
|
||
main.tsx Provider-Kette
|
||
types.ts Gemeinsame Typen und Konstanten
|
||
index.css Tailwind-Theme, Designs, Dunkelmodus
|
||
lib/api.ts Fetch-Wrapper + typisierte Endpunkte
|
||
lib/auth.tsx JWT im localStorage
|
||
lib/theme.tsx Design und hell/dunkel
|
||
lib/navigation.tsx Karte oeffnen, Einstellungen-Reiter ansteuern
|
||
lib/options.tsx Auswahllisten aus den Stammdaten
|
||
lib/mentions.tsx @-Token: Parser, Renderer, Vorschlagsliste
|
||
lib/richText.ts Altbestand in Editor-HTML, Bereinigung
|
||
lib/imageCompress.ts Bilder vor dem Upload verkleinern
|
||
lib/whisper*.ts(x), dictation.tsx, audio.ts Diktat
|
||
components/… nach Bereichen
|
||
server/
|
||
src/server.js Express-App, Routenmontage
|
||
src/db.js Pool, Schema-Init, Migrationen, Admin-Seed
|
||
src/schema.sql Idempotentes Schema
|
||
src/history.js Historie schreiben, Erwaehnungen indizieren
|
||
src/ai.js OpenAI-Aufruf an einer Stelle
|
||
src/cvText.js Text aus PDF/DOCX
|
||
src/migrate.js Einmalige Uebernahme aus Supabase
|
||
src/linkedinPhoto.js Profilfoto aus der Open-Graph-Vorschau
|
||
src/middleware/auth.js requireAuth, requireAdmin
|
||
src/routes/ ein Router je Bereich
|
||
deploy/ systemd-Unit, Apache-Vhost, deploy.ps1
|
||
legacy/ der alte Supabase-Stand, nur zum Nachschlagen
|
||
```
|
||
|
||
## Ein neues Feld hinzufügen
|
||
|
||
Am Beispiel eines Kontaktfelds – die Reihenfolge ist wichtig, sonst fehlt das
|
||
Feld an einer Stelle und fällt erst im Betrieb auf:
|
||
|
||
1. **Schema:** Spalte in `server/src/schema.sql` ergänzen **und** eine Migration
|
||
in `server/src/db.js` (`ALTER TABLE … ADD COLUMN IF NOT EXISTS`) – der
|
||
Bestand auf dem Server wird nicht neu angelegt.
|
||
2. **Route:** Feld in `server/src/routes/contacts.js` in die Liste der erlaubten
|
||
Felder aufnehmen (Lesen **und** Schreiben) und in der Aufbereitung der Zeile
|
||
berücksichtigen.
|
||
3. **Historie:** Klarnamen in `server/src/history.js` eintragen, sonst steht in
|
||
der Historie der Spaltenname.
|
||
4. **Typ:** `src/types.ts`.
|
||
5. **Formular:** `src/components/contacts/ContactModal.tsx`.
|
||
6. **Anzeige:** Liste, Kacheln, ggf. `contactUi.tsx`.
|
||
7. **Erweiterte Suche:** wenn das Feld für Fragen taugt, in den Bestand in
|
||
`server/src/routes/search.js` aufnehmen – und im Systemprompt erklären, was
|
||
`null` bedeutet.
|
||
|
||
Schritt 7 ist der, der am leichtesten vergessen wird. Die Sätze standen ein
|
||
halbes Jahr in der Datenbank und fehlten trotzdem in jeder Antwort der Suche,
|
||
weil sie nicht im mitgelieferten Bestand standen.
|
||
|
||
## Eine neue Ansicht hinzufügen
|
||
|
||
1. `View` in `src/types.ts` erweitern.
|
||
2. Eintrag in `src/components/Sidebar.tsx` (optional mit `children` für ein
|
||
Untermenü und `badge` für die Zahl daneben).
|
||
3. Block `{view === '…' && <… />}` in `src/App.tsx`.
|
||
4. Zählwert im Dashboard-Endpunkt ergänzen, falls ein Badge gewünscht ist.
|
||
|
||
Der Name der Ansicht ist zugleich ihr Schlüssel – wird er umbenannt (wie
|
||
„Projekte" → „Laufende Projekte"), müssen alle vier Stellen mitgehen, dazu die
|
||
Zuordnung in `navigation.open()` und der Filter in der Historie.
|
||
|
||
## Bekannte Fallen
|
||
|
||
**`gpt-5.6-luna` und `temperature`.** Jeder eigene Wert wird mit HTTP 400
|
||
abgewiesen. `ai.js` schickt keinen.
|
||
|
||
**Instabile Arrays in der Netzwerkansicht.** Ein `= []` als Vorgabewert erzeugt
|
||
bei jedem Rendern ein neues Array; `useMemo`/`useEffect` laufen dann endlos und
|
||
alle Knoten liegen auf einem Punkt. Deshalb `NO_EXTERNALS` auf Modulebene und
|
||
`useMemo` auf der Aufruferseite.
|
||
|
||
**Designs aus der Datenbank.** Wird eine Palette entfernt, tragen Benutzer sie
|
||
weiterhin am Konto. Drei Sicherungen greifen: das Backend liefert unbekannte
|
||
Werte als `null` aus, der Theme-Kontext prüft den Serverwert, und die Kopfzeile
|
||
fällt auf `icon` zurück. Ohne das reicht ein gelöschter Palettenname für eine
|
||
weiße Seite nach dem Anmelden.
|
||
|
||
**Altes HTML im Editor.** Der Bestand bringt Inline-Farben, geschützte
|
||
Leerzeichen und verschachtelte Listen mit. `toEditorHtml()` räumt zurückhaltend
|
||
auf, `index.css` hebt Inline-Farben im Editor aus. Wer beides umgeht, bekommt im
|
||
Dunkelmodus schwarze Schrift auf weißen Kästen und Zeilen, die nicht umbrechen.
|
||
|
||
**`notes` vs. `details` bei Ausschreibungen.** Der veröffentlichte Text steht in
|
||
`notes`. `details` ist im gesamten Bestand leer.
|
||
|
||
**Deployment ohne Build.** `-SkipBuild` lädt das vorhandene `dist/` hoch. Nach
|
||
einer Änderung an `src/` oder `index.html` also erst bauen.
|
||
|
||
**Kein automatischer Typcheck im Build.** `vite build` prüft die Typen nicht.
|
||
`npx tsc --noEmit` gehört vor jedes Deployment.
|
||
|
||
## Stil
|
||
|
||
Die Kommentare in diesem Projekt erklären **warum**, nicht was. Eine Zeile, die
|
||
nur wiederholt, was der Code sagt, ist eine Zeile zu viel; eine Zeile, die eine
|
||
Entscheidung begründet, erspart der nächsten Person eine Stunde.
|
||
|
||
Beschriftungen und Meldungen sind auf Deutsch, ohne Fachjargon, und sagen im
|
||
Zweifel, was zu tun ist – nicht nur, dass etwas fehlgeschlagen ist.
|