gpt-6-luna kostet rund 50 % weniger als gpt-5.6-luna. Das Modell in icon_settings (Einstellungen -> KI) hat weiter Vorrang vor der Vorgabe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.7 KiB
09 Anleitung für Entwickler
Einrichten
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.
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:
- Schema: Spalte in
server/src/schema.sqlergänzen und eine Migration inserver/src/db.js(ALTER TABLE … ADD COLUMN IF NOT EXISTS) – der Bestand auf dem Server wird nicht neu angelegt. - Route: Feld in
server/src/routes/contacts.jsin die Liste der erlaubten Felder aufnehmen (Lesen und Schreiben) und in der Aufbereitung der Zeile berücksichtigen. - Historie: Klarnamen in
server/src/history.jseintragen, sonst steht in der Historie der Spaltenname. - Typ:
src/types.ts. - Formular:
src/components/contacts/ContactModal.tsx. - Anzeige: Liste, Kacheln, ggf.
contactUi.tsx. - Erweiterte Suche: wenn das Feld für Fragen taugt, in den Bestand in
server/src/routes/search.jsaufnehmen – und im Systemprompt erklären, wasnullbedeutet.
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
Viewinsrc/types.tserweitern.- Eintrag in
src/components/Sidebar.tsx(optional mitchildrenfür ein Untermenü undbadgefür die Zahl daneben). - Block
{view === '…' && <… />}insrc/App.tsx. - 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
Luna-Modelle 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.