Anmeldung gilt 7 Tage statt 12 Stunden

JWT-Laufzeit (JWT_EXPIRES_IN) standardmaessig auf 7d gesetzt, damit
Benutzer nicht mehr taeglich neu anmelden muessen. Doku (Betrieb,
Benutzeranleitung, Glossar) und .env.example angepasst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 22:10:46 +02:00
co-authored by Claude Opus 5.5
parent cbac02a96a
commit 4adf48f336
5 changed files with 439 additions and 439 deletions
+159 -159
View File
@@ -1,159 +1,159 @@
# 07 Betrieb
## Deployment
Gebaut wird **lokal**, auf den Server gehen nur `dist/` plus die Backend-Quellen.
```powershell
$env:ICON_CRM_SSH_PASSWORD = '…'
powershell -File deploy/deploy.ps1
powershell -File deploy/deploy.ps1 -SkipBuild # ohne neuen Build
powershell -File deploy/deploy.ps1 -Deps # bei neuen Backend-Paketen
```
Das Skript (`deploy/deploy.ps1`) macht in dieser Reihenfolge:
```mermaid
flowchart LR
B["npm run build"] --> U1["pscp dist/"]
U1 --> U2["pscp server/src + package.json"]
U2 --> R["systemctl restart icon-crm-backend"]
R --> C["curl /api/health"]
```
Schlägt der Build fehl, wird **nichts** hochgeladen – sonst stünde auf dem
Server ein halber Stand.
> **Falle aus der Praxis:** Wer `index.html` von Hand ändert und dann mit
> `-SkipBuild` hochlädt, spielt das alte `dist/` aus. Nach jeder Änderung an
> `src/` oder `index.html` gilt: erst bauen, dann hochladen.
Alte Build-Dateien werden beim Hochladen **nicht** entfernt – `dist/assets/`
wächst mit jeder Auslieferung. Das ist Absicht: ein Browser mit offenem Tab holt
sonst ein Bruchstück, das es nicht mehr gibt. Gelegentlich aufräumen schadet
trotzdem nicht.
## Dienste auf dem Server
| Was | Befehl |
| --- | --- |
| Zustand | `systemctl status icon-crm-backend` |
| Neustart | `systemctl restart icon-crm-backend` |
| Log | `journalctl -u icon-crm-backend -n 100 --no-pager` |
| Gesundheit | `curl -s http://127.0.0.1:5007/api/health` → `{"ok":true,"db":"up"}` |
| Apache | `systemctl reload apache2`, Logs unter `/var/log/apache2/icon_crm_*.log` |
Die systemd-Unit begrenzt den Speicher auf 1 GB (`MemoryMax=1024M`). Das ist
kein Sparzwang, sondern ein Sicherheitsnetz: ein Leck im Backend soll nicht
Apache und MariaDB mitreißen.
## Umgebungsvariablen
`server/.env`, chmod 600, gehört **nicht** ins Repository. Vorlage:
`server/.env.example`.
| Variable | Bedeutung |
| --- | --- |
| `PORT`, `HOST` | `5007` auf `127.0.0.1` – von außen nur über Apache |
| `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME` | MariaDB, Datenbank `icon_CRM` |
| `JWT_SECRET`, `JWT_EXPIRES_IN` | Sitzungen, Vorgabe 12 h |
| `ADMIN_USERNAME`, `ADMIN_NAME`, `ADMIN_EMAIL`, `ADMIN_PASSWORD` | erster Administrator, **nur solange `icon_users` leer ist** |
| `CORS_ORIGINS` | erlaubte Herkunft für den lokalen Dev-Server |
| `UPLOAD_DIR` | Ablage der Lebensläufe |
| `OPENAI_API_KEY`, `OPENAI_MODEL` | KI; fehlt der Schlüssel, laufen die übrigen Teile weiter |
| `PERINCO_API_URL`, `PERINCO_API_TOKEN` | ICON liest perinco |
| `PERINCO_INBOUND_TOKEN` | perinco liest ICON |
Ohne `ADMIN_PASSWORD` erzeugt das Backend eines und schreibt es **einmalig** ins
Log. Danach ist das Passwort nur noch über die Nutzerverwaltung bzw. *Mein
Konto* änderbar.
## Erstaufsetzung (einmalig, bereits erledigt)
```bash
apt-get install -y apache2 mariadb-server certbot python3-certbot-apache nodejs
a2enmod proxy proxy_http rewrite headers expires
mariadb -e "CREATE DATABASE icon_CRM CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mariadb -e "CREATE USER 'iconcrm'@'localhost' IDENTIFIED BY '…';
GRANT ALL PRIVILEGES ON icon_CRM.* TO 'iconcrm'@'localhost';"
cd /var/www/icon_CRM/server && npm install --omit=dev
cp deploy/icon-crm-backend.service /etc/systemd/system/
systemctl daemon-reload && systemctl enable --now icon-crm-backend
cp deploy/icrm.icon-experts.de.conf /etc/apache2/sites-available/
a2ensite icrm.icon-experts.de && systemctl reload apache2
certbot --apache -d icrm.icon-experts.de
```
Das Schema legt das Backend beim Start selbst an (`schema.sql` +
Migrationen in `db.js`). Die Firewall (`ufw`) lässt 22, 80 und 443 durch; das
Zertifikat erneuert certbot selbst.
## Sicherung
Der Bestand steht vollständig in MariaDB; hochgeladene Lebensläufe liegen unter
`/var/www/icon_CRM/uploads`. Beides gehört in die Sicherung:
```bash
mariadb-dump --single-transaction icon_CRM | gzip > icon_CRM-$(date +%F).sql.gz
tar czf uploads-$(date +%F).tar.gz -C /var/www/icon_CRM uploads
```
## Der alte Name crm.icon-experts.de
Das erste ICON CRM lief unter **crm.icon-experts.de** auf `212.227.135.41`
(`/var/www/ICON`, Supabase als Datenbank). Seit dem 14.09.2026 zeigt der Name
auf diesen Server und wird vom Vhost `icrm.icon-experts.de` als `ServerAlias`
mitbedient – dasselbe Zertifikat deckt beide Namen ab. Verschickte Links und
Lesezeichen laufen damit weiter, es gibt keinen zweiten Stand zu pflegen.
Der alte Aufbau ist zurückgebaut: Vhosts (`icon`, `icon-le-ssl`) abgeschaltet,
`/var/www/ICON` gelöscht, das dortige Zertifikat für `crm.icon-experts.de`
entfernt (sonst hätte die Erneuerung ab sofort täglich Fehler gemeldet).
Gesichert ist er als `icon-crm-alt-2026-09-14.zip` – Quellcode, Build, `.env`
und beide Vhost-Dateien, ohne `node_modules`. Die Datei liegt **auf beiden
Servern** unter `/root/backups/`, Prüfsumme
`252f7fa3226c2df7c4020a0234c3095c`.
> Die Supabase-Instanz `icondb.perinco.de` läuft weiter. Sie war die Datenbank
> des alten CRM und ist die Quelle der Migration (`server/src/migrate.js`) –
> abzuschalten wäre eine eigene Entscheidung, nicht Teil dieses Rückbaus.
## Störungssuche
| Symptom | Erster Griff |
| --- | --- |
| Weiße Seite nach dem Rollout | `index.html` darf nicht aus dem Cache kommen – der Vhost setzt dafür `no-cache`; im Zweifel Strg+F5 |
| 502 auf `/api` | Backend aus: `systemctl status icon-crm-backend`, dann `journalctl -u …` |
| `{"db":"down"}` | MariaDB aus oder Zugangsdaten falsch |
| Anmeldung schlägt fehl | `icon_users` prüfen: existiert der Benutzer, ist `is_active = 1`? |
| Zertifikatswarnung | `certbot certificates`; deckt das Zertifikat **alle** Namen ab, auch `www`? |
| Website zeigt alte Ausschreibungen | `curl -s https://icon-experts.de/api/public/tenders | head` – kommt das durch den Proxy? |
| perinco-Kontakte fehlen | `GET /api/perinco/status`; Token beidseitig gesetzt? |
| Diktat lädt nicht | siehe [04 Diktat](04-diktat.md) – das ist eine Sache des Browsers, nicht des Servers |
### Wenn Daten zu fehlen scheinen
Erst messen, dann handeln. Die drei Griffe, die in der Praxis geholfen haben:
```bash
mysql icon_CRM -e "SELECT COUNT(*) FROM icon_contacts" # steht es in der Datenbank?
curl -s .../api/contacts -H "Authorization: Bearer …" | wc -c # liefert die API es aus?
tail -n 50 /var/log/apache2/icon_crm_access.log # kam es beim Browser an?
```
Stimmen alle drei, liegt es an der Ansicht – meist an einem aktiven Filter.
### DNS-Umstellungen
Nach einem Wechsel des A-Records antworten fremde Resolver unterschiedlich
lange mit der alten Adresse. Zum Prüfen die **autoritativen** Nameserver und
mindestens einen öffentlichen Resolver befragen, nicht nur den eigenen:
```bash
dig +short icon-experts.de @1.1.1.1
dig +short icon-experts.de @8.8.8.8
```
Ein lokaler Resolver (PiHole, Router) kann den alten Wert deutlich länger
halten – das sieht dann aus wie ein Serverfehler und ist keiner.
# 07 Betrieb
## Deployment
Gebaut wird **lokal**, auf den Server gehen nur `dist/` plus die Backend-Quellen.
```powershell
$env:ICON_CRM_SSH_PASSWORD = '…'
powershell -File deploy/deploy.ps1
powershell -File deploy/deploy.ps1 -SkipBuild # ohne neuen Build
powershell -File deploy/deploy.ps1 -Deps # bei neuen Backend-Paketen
```
Das Skript (`deploy/deploy.ps1`) macht in dieser Reihenfolge:
```mermaid
flowchart LR
B["npm run build"] --> U1["pscp dist/"]
U1 --> U2["pscp server/src + package.json"]
U2 --> R["systemctl restart icon-crm-backend"]
R --> C["curl /api/health"]
```
Schlägt der Build fehl, wird **nichts** hochgeladen – sonst stünde auf dem
Server ein halber Stand.
> **Falle aus der Praxis:** Wer `index.html` von Hand ändert und dann mit
> `-SkipBuild` hochlädt, spielt das alte `dist/` aus. Nach jeder Änderung an
> `src/` oder `index.html` gilt: erst bauen, dann hochladen.
Alte Build-Dateien werden beim Hochladen **nicht** entfernt – `dist/assets/`
wächst mit jeder Auslieferung. Das ist Absicht: ein Browser mit offenem Tab holt
sonst ein Bruchstück, das es nicht mehr gibt. Gelegentlich aufräumen schadet
trotzdem nicht.
## Dienste auf dem Server
| Was | Befehl |
| --- | --- |
| Zustand | `systemctl status icon-crm-backend` |
| Neustart | `systemctl restart icon-crm-backend` |
| Log | `journalctl -u icon-crm-backend -n 100 --no-pager` |
| Gesundheit | `curl -s http://127.0.0.1:5007/api/health` → `{"ok":true,"db":"up"}` |
| Apache | `systemctl reload apache2`, Logs unter `/var/log/apache2/icon_crm_*.log` |
Die systemd-Unit begrenzt den Speicher auf 1 GB (`MemoryMax=1024M`). Das ist
kein Sparzwang, sondern ein Sicherheitsnetz: ein Leck im Backend soll nicht
Apache und MariaDB mitreißen.
## Umgebungsvariablen
`server/.env`, chmod 600, gehört **nicht** ins Repository. Vorlage:
`server/.env.example`.
| Variable | Bedeutung |
| --- | --- |
| `PORT`, `HOST` | `5007` auf `127.0.0.1` – von außen nur über Apache |
| `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME` | MariaDB, Datenbank `icon_CRM` |
| `JWT_SECRET`, `JWT_EXPIRES_IN` | Sitzungen, Vorgabe 7 Tage (`7d`) |
| `ADMIN_USERNAME`, `ADMIN_NAME`, `ADMIN_EMAIL`, `ADMIN_PASSWORD` | erster Administrator, **nur solange `icon_users` leer ist** |
| `CORS_ORIGINS` | erlaubte Herkunft für den lokalen Dev-Server |
| `UPLOAD_DIR` | Ablage der Lebensläufe |
| `OPENAI_API_KEY`, `OPENAI_MODEL` | KI; fehlt der Schlüssel, laufen die übrigen Teile weiter |
| `PERINCO_API_URL`, `PERINCO_API_TOKEN` | ICON liest perinco |
| `PERINCO_INBOUND_TOKEN` | perinco liest ICON |
Ohne `ADMIN_PASSWORD` erzeugt das Backend eines und schreibt es **einmalig** ins
Log. Danach ist das Passwort nur noch über die Nutzerverwaltung bzw. *Mein
Konto* änderbar.
## Erstaufsetzung (einmalig, bereits erledigt)
```bash
apt-get install -y apache2 mariadb-server certbot python3-certbot-apache nodejs
a2enmod proxy proxy_http rewrite headers expires
mariadb -e "CREATE DATABASE icon_CRM CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mariadb -e "CREATE USER 'iconcrm'@'localhost' IDENTIFIED BY '…';
GRANT ALL PRIVILEGES ON icon_CRM.* TO 'iconcrm'@'localhost';"
cd /var/www/icon_CRM/server && npm install --omit=dev
cp deploy/icon-crm-backend.service /etc/systemd/system/
systemctl daemon-reload && systemctl enable --now icon-crm-backend
cp deploy/icrm.icon-experts.de.conf /etc/apache2/sites-available/
a2ensite icrm.icon-experts.de && systemctl reload apache2
certbot --apache -d icrm.icon-experts.de
```
Das Schema legt das Backend beim Start selbst an (`schema.sql` +
Migrationen in `db.js`). Die Firewall (`ufw`) lässt 22, 80 und 443 durch; das
Zertifikat erneuert certbot selbst.
## Sicherung
Der Bestand steht vollständig in MariaDB; hochgeladene Lebensläufe liegen unter
`/var/www/icon_CRM/uploads`. Beides gehört in die Sicherung:
```bash
mariadb-dump --single-transaction icon_CRM | gzip > icon_CRM-$(date +%F).sql.gz
tar czf uploads-$(date +%F).tar.gz -C /var/www/icon_CRM uploads
```
## Der alte Name crm.icon-experts.de
Das erste ICON CRM lief unter **crm.icon-experts.de** auf `212.227.135.41`
(`/var/www/ICON`, Supabase als Datenbank). Seit dem 14.09.2026 zeigt der Name
auf diesen Server und wird vom Vhost `icrm.icon-experts.de` als `ServerAlias`
mitbedient – dasselbe Zertifikat deckt beide Namen ab. Verschickte Links und
Lesezeichen laufen damit weiter, es gibt keinen zweiten Stand zu pflegen.
Der alte Aufbau ist zurückgebaut: Vhosts (`icon`, `icon-le-ssl`) abgeschaltet,
`/var/www/ICON` gelöscht, das dortige Zertifikat für `crm.icon-experts.de`
entfernt (sonst hätte die Erneuerung ab sofort täglich Fehler gemeldet).
Gesichert ist er als `icon-crm-alt-2026-09-14.zip` – Quellcode, Build, `.env`
und beide Vhost-Dateien, ohne `node_modules`. Die Datei liegt **auf beiden
Servern** unter `/root/backups/`, Prüfsumme
`252f7fa3226c2df7c4020a0234c3095c`.
> Die Supabase-Instanz `icondb.perinco.de` läuft weiter. Sie war die Datenbank
> des alten CRM und ist die Quelle der Migration (`server/src/migrate.js`) –
> abzuschalten wäre eine eigene Entscheidung, nicht Teil dieses Rückbaus.
## Störungssuche
| Symptom | Erster Griff |
| --- | --- |
| Weiße Seite nach dem Rollout | `index.html` darf nicht aus dem Cache kommen – der Vhost setzt dafür `no-cache`; im Zweifel Strg+F5 |
| 502 auf `/api` | Backend aus: `systemctl status icon-crm-backend`, dann `journalctl -u …` |
| `{"db":"down"}` | MariaDB aus oder Zugangsdaten falsch |
| Anmeldung schlägt fehl | `icon_users` prüfen: existiert der Benutzer, ist `is_active = 1`? |
| Zertifikatswarnung | `certbot certificates`; deckt das Zertifikat **alle** Namen ab, auch `www`? |
| Website zeigt alte Ausschreibungen | `curl -s https://icon-experts.de/api/public/tenders | head` – kommt das durch den Proxy? |
| perinco-Kontakte fehlen | `GET /api/perinco/status`; Token beidseitig gesetzt? |
| Diktat lädt nicht | siehe [04 Diktat](04-diktat.md) – das ist eine Sache des Browsers, nicht des Servers |
### Wenn Daten zu fehlen scheinen
Erst messen, dann handeln. Die drei Griffe, die in der Praxis geholfen haben:
```bash
mysql icon_CRM -e "SELECT COUNT(*) FROM icon_contacts" # steht es in der Datenbank?
curl -s .../api/contacts -H "Authorization: Bearer …" | wc -c # liefert die API es aus?
tail -n 50 /var/log/apache2/icon_crm_access.log # kam es beim Browser an?
```
Stimmen alle drei, liegt es an der Ansicht – meist an einem aktiven Filter.
### DNS-Umstellungen
Nach einem Wechsel des A-Records antworten fremde Resolver unterschiedlich
lange mit der alten Adresse. Zum Prüfen die **autoritativen** Nameserver und
mindestens einen öffentlichen Resolver befragen, nicht nur den eigenen:
```bash
dig +short icon-experts.de @1.1.1.1
dig +short icon-experts.de @8.8.8.8
```
Ein lokaler Resolver (PiHole, Router) kann den alten Wert deutlich länger
halten – das sieht dann aus wie ein Serverfehler und ist keiner.
+224 -224
View File
@@ -1,224 +1,224 @@
# 08 Anleitung für Benutzer
Diese Anleitung setzt nichts voraus. Adresse: <https://icrm.icon-experts.de>.
## Anmelden
Benutzername oder E-Mail und Passwort. Die Anmeldung gilt 12 Stunden; danach
fragt das CRM erneut. Passwort ändern: *Einstellungen → Mein Konto*.
Rechts oben stehen zwei Umschalter: die **Farbwelt** (ICON, Half-Life, Pixel
Art) und **hell/dunkel**. Beides hängt am Konto und gilt auf jedem Gerät. Ein
Doppelklick auf den Hell/Dunkel-Knopf stellt „wie das System" wieder her.
## Suchen
Oben in der Kopfleiste, oder mit `Strg+K`. Rechts im Feld zwei Symbole:
* **Lupe** – die normale Suche. Läuft beim Tippen mit, durchsucht Kontakte,
Unternehmen, Ausschreibungen, Projekte, Leads und die Historie. Ein Klick auf
einen Treffer öffnet dessen Karte.
* **Funke** – die erweiterte Suche. Hier steht eine ganze Frage:
*„Wer arbeitet alles bei der KfW?"*, *„Wer hat den höchsten Satz?"*. Antwort
auf Enter, nicht beim Tippen. Die genannten Datensätze stehen darunter als
anklickbare Schaltflächen.
## Dashboard
Zahlen zum Bestand und die letzten Tätigkeiten. Von hier führt jeder Eintrag in
die zugehörige Karte.
## Leads
Die Pipeline als Kanban über acht Spalten, von **Backlog** bis **Absage**.
Kacheln lassen sich mit der Maus in eine andere Spalte ziehen; der Wechsel landet
in der Historie.
Auf jeder Kachel:
| Zeichen | Bedeutung |
| --- | --- |
| Aktenschrank | archivieren – landet unter *Leads → Archiviert* und lässt sich von dort zurückholen |
| roter Mülleimer | löschen, mit Rückfrage |
| Balken mit Prozentzahl | Stand der ToDo-Liste |
Die Karte eines Leads hat drei Reiter:
1. **Stammdaten** – Titel, Unternehmen, Ansprechpartner, Kontaktstand,
Zuständiger, Notizen und die Erinnerung.
2. **ToDos** – Checkliste mit Fälligkeitsdatum. Der Fortschritt steht als
Prozentwert auf der Kachel.
3. **Historie** – wer wann was geändert hat.
### Erinnerungen
Zwei Arten, beide als Pop-up, das sich unabhängig von der geöffneten Ansicht
meldet:
* **gesetzt** – „in 2 Tagen anrufen". Eingegeben werden **Tage**, nicht ein
Datum; gerechnet wird auf dem Server. Quittieren heißt *Erledigt* oder *in n
Tagen erneut*.
* **automatisch** – wird ein Lead 30 Tage nicht angefasst, fragt das CRM, ob er
archiviert werden soll. Bleibt er stehen, läuft die Frist von vorn.
## Kontakte
Drei Ansichten, oben rechts umschaltbar: **Kacheln**, **Liste**, **Netzwerk**.
* In der **Liste** sortiert ein Klick auf eine Spaltenüberschrift; ein zweiter
Klick dreht die Richtung um. Leere Felder stehen immer am Ende.
* Der Schalter **perinco** blendet zusätzlich die Kontakte des perinco CRM ein –
grün umrandet, nur zum Lesen.
* Filter (Art, Status, Unternehmen …) sind sichtbar ausgezeichnet. Steht die
Liste leer da, sagt der Text welche Filter aktiv sind; daneben liegt *Filter
zurücksetzen*.
Die Karte eines Kontakts hat Stammdaten, Werdegang, Gespräche und Historie.
Im Reiter **Werdegang & Interviews** lässt sich jede Station bearbeiten:
*Station hinzufügen*, der Stift ändert eine bestehende, der Mülleimer entfernt
sie. Das Unternehmen ist ein Textfeld mit Vorschlagsliste – ein Name, den es
noch nicht gibt, legt das Unternehmen an (wie im CV-Import).
Der Haken **aktuelle Station** hängt mit dem Arbeitgeber im Reiter Stammdaten
zusammen: es kann immer nur eine Station laufen. Wird eine dazu erklärt, bekommt
die bisherige ein Enddatum, und der Arbeitgeber des Kontakts wechselt mit. Wird
die laufende Station gelöscht, hat der Kontakt danach keinen Arbeitgeber mehr –
die Rückfrage sagt das.
| Feld | Hinweis |
| --- | --- |
| Art | Experte, Ansprechpartner, Lead – die Liste wird in den Stammdaten gepflegt |
| Status | Interessiert, Interview, Gebucht, Inaktiv |
| Stundensatz vor Ort / remote | in **Euro je Stunde**; leer heißt „nicht hinterlegt", nicht „null Euro" |
| Foto | hochladen oder mit *Von LinkedIn* aus dem öffentlichen Profil holen |
| CV | Link oder die hochgeladene Datei aus dem CV-Import |
### CV-Import
*Kontakte → CV-Import.* Lebensläufe hineinziehen (PDF, DOCX, Text; bis 20 auf
einmal). Danach drei Ordner:
1. **Eingang** – je Datei entscheiden: *Ja, auslesen* oder *Nein*.
2. **Screening** – der Vorschlag der KI steht im Formular. Alles ist änderbar;
was hier korrigiert wird, gewinnt.
3. **Auswahl** – bestätigt, Kontakt angelegt, Lebenslauf hängt daran.
Trägt der Lebenslauf ein **Bewerbungsfoto**, steht es oben im Formular, mit
gesetztem Haken. Es wird auf 400 px verkleinert (höchstens 512 kB) und beim
Übernehmen als Foto des Kontakts gespeichert. Hat der Automat statt des
Portraits das Firmenlogo erwischt, Haken entfernen – ein anderes Bild lässt sich
jederzeit in der Kontaktkarte setzen.
Es wird **nichts automatisch angelegt**. Frühere Stationen werden als beendete
Anstellungen übernommen – der Kontakt erscheint dadurch beim alten Arbeitgeber
sofort unter „Ehemalige".
## Unternehmen
Ebenfalls Kacheln, Liste und Netzwerk, dazu der Reiter **Group** (Konzern).
Ein Unternehmen mit gesetztem Haken *Kunde* erscheint zusätzlich in der
Kundenansicht; der Zustand *aktiv* oder *alumni* unterscheidet laufende von
früheren Kunden.
Fünf Sterne bewerten das Unternehmen, `Freelancer` sagt, ob dort überhaupt
freiberuflich gearbeitet wird.
### Netzwerkansicht und Stammbaum
Rechtecke sind Unternehmen, Kreise sind Kontakte. Ziehen verschiebt, das Mausrad
zoomt. Ein Klick auf ein Unternehmen mit Kontakten öffnet dessen **Stammbaum**:
| Ast | Bedeutung |
| --- | --- |
| golden | Berater (Art „Experte") |
| hell | Ansprechpartner und übrige |
| grau, gestrichelt | Ehemalige – darunter steht, wo die Person heute ist |
In den Kontakten lassen sich zusätzlich Verbindungen anlegen: *Verbindung
erstellen*, dann zwei Kontakte anklicken; Esc bricht ab.
## Ausschreibungen
Eine Tabelle; ein Klick klappt die Zeile auf. Links die zugewiesenen Experten
und der Kundenkontakt, rechts der **Ausschreibungstext**.
> **Der Schalter „Online" entscheidet, ob die Ausschreibung auf der ICON Website
> in der Projektbörse steht.** Er sitzt deshalb direkt in der Liste.
Der Text wird automatisch gespeichert, 800 ms nach dem letzten Tastendruck – es
gibt keinen Speichern-Knopf. Über dem Feld liegen die Knöpfe der Schreibhilfe
(*Text erzeugen*, *Verbessern*, *Kürzen*, *Stichpunkte*, *Anforderungsprofil*)
und der Diktat-Knopf. *Zurück* holt den Stand vor dem letzten KI-Eingriff.
Zwei Untermenüs:
* **Live!** – dieselben Ausschreibungen als Kanban über die Phasen, mit Ziehen
bearbeitbar.
* **Archiv** – archivierte Ausschreibungen; von dort zurückholbar.
## Laufende Projekte
Titel, Kunde, Experte, Zeitraum, Status, Tagessatz und Notizen. Die Notizen
sind ein WYSIWYG-Feld – im übernommenen Bestand steht dort ein Kopfblock aus dem
alten CRM und darunter der Ausschreibungstext.
### Marge
Jede Projektkachel weist die Marge aus, und in der Projektkarte steht die
Rechnung aufgeschlüsselt:
| Zeile | Woher |
| --- | --- |
| Tagessatz Kunde | Feld **Tagessatz** am Projekt |
| Stundensatz Berater | Kontaktkarte des Experten |
| Kosten (8 h) | Stundensatz × 8 |
| Marge | Tagessatz − Kosten, dazu der Prozentsatz vom Tagessatz |
**Ein Tag sind 8 Stunden.** Hat der Berater einen Remote- *und* einen
Vor-Ort-Satz, wird mit dem **Remote-Satz** gerechnet; fehlt der, mit dem
Vor-Ort-Satz. Welcher benutzt wurde, steht daneben.
Die Farbe ist eine schnelle Ampel: grün ab 25 %, gelb darüber bis 0, rot bei
negativer Marge.
Fehlt der Tagessatz oder hat der Berater keinen Stundensatz, steht statt einer
Zahl der Grund dort – eine leere Zeile ließe offen, ob die Marge null oder
unbekannt ist. Der Tagessatz wird am Projekt gepflegt, die Stundensätze beim
Kontakt (*Kontakt → Stundensatz vor Ort / remote*).
## Historie
Alles, was im CRM getan wurde, mit Zeitpunkt, Person und – bei Änderungen – den
betroffenen Feldern samt Vorher/Nachher. Filterbar nach Art des Datensatzes.
**@-Erwähnungen:** In jedem Notizfeld öffnet `@` eine Vorschlagsliste über
Kontakte, Unternehmen und Benutzer. Im Lesezustand ist jede Erwähnung anklickbar
und führt auf das Profil; im Bearbeiten-Zustand steht dort die rohe Form
`@[Name](contact:12)` – anders ließe sie sich nicht ändern oder löschen.
## Diktat
*Einstellungen → Diktat*, einmal je Gerät: **Sprachmodell laden** (rund 300 MB,
bleibt danach im Browser). Anschließend steht in jedem längeren Textfeld ein
Mikrofon-Knopf.
Aufnahme starten, sprechen, Knopf erneut drücken – der erkannte Text wird
**angehängt**, nie ersetzt. Das Kreuz daneben verwirft die Aufnahme.
Die Erkennung läuft auf dem eigenen Rechner. Es wird nichts hochgeladen und
nichts gespeichert. Details: [04 Diktat](04-diktat.md).
## Einstellungen
| Reiter | Wer | Inhalt |
| --- | --- | --- |
| Nutzerverwaltung | Administratoren | Benutzer anlegen, Rolle, aktiv/inaktiv |
| Stammdaten | Administratoren | Auswahllisten: Branchen, Arten der Kontakte |
| KI | Administratoren | OpenAI-Schlüssel, Modell (Auswahlliste der für den Schlüssel verfügbaren Modelle), Verbindungstest |
| Mein Konto | alle | Foto, Farbe, Passwort, Design |
| Diktat | alle | Spracherkennung einrichten |
Beim Pflegen der Stammdaten gelten zwei Regeln: **Umbenennen zieht den Bestand
mit**, und **Entfernen geht nur, solange ein Wert nirgends benutzt wird** –
hinter jedem Eintrag steht, wie oft er vorkommt.
# 08 Anleitung für Benutzer
Diese Anleitung setzt nichts voraus. Adresse: <https://icrm.icon-experts.de>.
## Anmelden
Benutzername oder E-Mail und Passwort. Die Anmeldung gilt 7 Tage; danach
fragt das CRM erneut. Passwort ändern: *Einstellungen → Mein Konto*.
Rechts oben stehen zwei Umschalter: die **Farbwelt** (ICON, Half-Life, Pixel
Art) und **hell/dunkel**. Beides hängt am Konto und gilt auf jedem Gerät. Ein
Doppelklick auf den Hell/Dunkel-Knopf stellt „wie das System" wieder her.
## Suchen
Oben in der Kopfleiste, oder mit `Strg+K`. Rechts im Feld zwei Symbole:
* **Lupe** – die normale Suche. Läuft beim Tippen mit, durchsucht Kontakte,
Unternehmen, Ausschreibungen, Projekte, Leads und die Historie. Ein Klick auf
einen Treffer öffnet dessen Karte.
* **Funke** – die erweiterte Suche. Hier steht eine ganze Frage:
*„Wer arbeitet alles bei der KfW?"*, *„Wer hat den höchsten Satz?"*. Antwort
auf Enter, nicht beim Tippen. Die genannten Datensätze stehen darunter als
anklickbare Schaltflächen.
## Dashboard
Zahlen zum Bestand und die letzten Tätigkeiten. Von hier führt jeder Eintrag in
die zugehörige Karte.
## Leads
Die Pipeline als Kanban über acht Spalten, von **Backlog** bis **Absage**.
Kacheln lassen sich mit der Maus in eine andere Spalte ziehen; der Wechsel landet
in der Historie.
Auf jeder Kachel:
| Zeichen | Bedeutung |
| --- | --- |
| Aktenschrank | archivieren – landet unter *Leads → Archiviert* und lässt sich von dort zurückholen |
| roter Mülleimer | löschen, mit Rückfrage |
| Balken mit Prozentzahl | Stand der ToDo-Liste |
Die Karte eines Leads hat drei Reiter:
1. **Stammdaten** – Titel, Unternehmen, Ansprechpartner, Kontaktstand,
Zuständiger, Notizen und die Erinnerung.
2. **ToDos** – Checkliste mit Fälligkeitsdatum. Der Fortschritt steht als
Prozentwert auf der Kachel.
3. **Historie** – wer wann was geändert hat.
### Erinnerungen
Zwei Arten, beide als Pop-up, das sich unabhängig von der geöffneten Ansicht
meldet:
* **gesetzt** – „in 2 Tagen anrufen". Eingegeben werden **Tage**, nicht ein
Datum; gerechnet wird auf dem Server. Quittieren heißt *Erledigt* oder *in n
Tagen erneut*.
* **automatisch** – wird ein Lead 30 Tage nicht angefasst, fragt das CRM, ob er
archiviert werden soll. Bleibt er stehen, läuft die Frist von vorn.
## Kontakte
Drei Ansichten, oben rechts umschaltbar: **Kacheln**, **Liste**, **Netzwerk**.
* In der **Liste** sortiert ein Klick auf eine Spaltenüberschrift; ein zweiter
Klick dreht die Richtung um. Leere Felder stehen immer am Ende.
* Der Schalter **perinco** blendet zusätzlich die Kontakte des perinco CRM ein –
grün umrandet, nur zum Lesen.
* Filter (Art, Status, Unternehmen …) sind sichtbar ausgezeichnet. Steht die
Liste leer da, sagt der Text welche Filter aktiv sind; daneben liegt *Filter
zurücksetzen*.
Die Karte eines Kontakts hat Stammdaten, Werdegang, Gespräche und Historie.
Im Reiter **Werdegang & Interviews** lässt sich jede Station bearbeiten:
*Station hinzufügen*, der Stift ändert eine bestehende, der Mülleimer entfernt
sie. Das Unternehmen ist ein Textfeld mit Vorschlagsliste – ein Name, den es
noch nicht gibt, legt das Unternehmen an (wie im CV-Import).
Der Haken **aktuelle Station** hängt mit dem Arbeitgeber im Reiter Stammdaten
zusammen: es kann immer nur eine Station laufen. Wird eine dazu erklärt, bekommt
die bisherige ein Enddatum, und der Arbeitgeber des Kontakts wechselt mit. Wird
die laufende Station gelöscht, hat der Kontakt danach keinen Arbeitgeber mehr –
die Rückfrage sagt das.
| Feld | Hinweis |
| --- | --- |
| Art | Experte, Ansprechpartner, Lead – die Liste wird in den Stammdaten gepflegt |
| Status | Interessiert, Interview, Gebucht, Inaktiv |
| Stundensatz vor Ort / remote | in **Euro je Stunde**; leer heißt „nicht hinterlegt", nicht „null Euro" |
| Foto | hochladen oder mit *Von LinkedIn* aus dem öffentlichen Profil holen |
| CV | Link oder die hochgeladene Datei aus dem CV-Import |
### CV-Import
*Kontakte → CV-Import.* Lebensläufe hineinziehen (PDF, DOCX, Text; bis 20 auf
einmal). Danach drei Ordner:
1. **Eingang** – je Datei entscheiden: *Ja, auslesen* oder *Nein*.
2. **Screening** – der Vorschlag der KI steht im Formular. Alles ist änderbar;
was hier korrigiert wird, gewinnt.
3. **Auswahl** – bestätigt, Kontakt angelegt, Lebenslauf hängt daran.
Trägt der Lebenslauf ein **Bewerbungsfoto**, steht es oben im Formular, mit
gesetztem Haken. Es wird auf 400 px verkleinert (höchstens 512 kB) und beim
Übernehmen als Foto des Kontakts gespeichert. Hat der Automat statt des
Portraits das Firmenlogo erwischt, Haken entfernen – ein anderes Bild lässt sich
jederzeit in der Kontaktkarte setzen.
Es wird **nichts automatisch angelegt**. Frühere Stationen werden als beendete
Anstellungen übernommen – der Kontakt erscheint dadurch beim alten Arbeitgeber
sofort unter „Ehemalige".
## Unternehmen
Ebenfalls Kacheln, Liste und Netzwerk, dazu der Reiter **Group** (Konzern).
Ein Unternehmen mit gesetztem Haken *Kunde* erscheint zusätzlich in der
Kundenansicht; der Zustand *aktiv* oder *alumni* unterscheidet laufende von
früheren Kunden.
Fünf Sterne bewerten das Unternehmen, `Freelancer` sagt, ob dort überhaupt
freiberuflich gearbeitet wird.
### Netzwerkansicht und Stammbaum
Rechtecke sind Unternehmen, Kreise sind Kontakte. Ziehen verschiebt, das Mausrad
zoomt. Ein Klick auf ein Unternehmen mit Kontakten öffnet dessen **Stammbaum**:
| Ast | Bedeutung |
| --- | --- |
| golden | Berater (Art „Experte") |
| hell | Ansprechpartner und übrige |
| grau, gestrichelt | Ehemalige – darunter steht, wo die Person heute ist |
In den Kontakten lassen sich zusätzlich Verbindungen anlegen: *Verbindung
erstellen*, dann zwei Kontakte anklicken; Esc bricht ab.
## Ausschreibungen
Eine Tabelle; ein Klick klappt die Zeile auf. Links die zugewiesenen Experten
und der Kundenkontakt, rechts der **Ausschreibungstext**.
> **Der Schalter „Online" entscheidet, ob die Ausschreibung auf der ICON Website
> in der Projektbörse steht.** Er sitzt deshalb direkt in der Liste.
Der Text wird automatisch gespeichert, 800 ms nach dem letzten Tastendruck – es
gibt keinen Speichern-Knopf. Über dem Feld liegen die Knöpfe der Schreibhilfe
(*Text erzeugen*, *Verbessern*, *Kürzen*, *Stichpunkte*, *Anforderungsprofil*)
und der Diktat-Knopf. *Zurück* holt den Stand vor dem letzten KI-Eingriff.
Zwei Untermenüs:
* **Live!** – dieselben Ausschreibungen als Kanban über die Phasen, mit Ziehen
bearbeitbar.
* **Archiv** – archivierte Ausschreibungen; von dort zurückholbar.
## Laufende Projekte
Titel, Kunde, Experte, Zeitraum, Status, Tagessatz und Notizen. Die Notizen
sind ein WYSIWYG-Feld – im übernommenen Bestand steht dort ein Kopfblock aus dem
alten CRM und darunter der Ausschreibungstext.
### Marge
Jede Projektkachel weist die Marge aus, und in der Projektkarte steht die
Rechnung aufgeschlüsselt:
| Zeile | Woher |
| --- | --- |
| Tagessatz Kunde | Feld **Tagessatz** am Projekt |
| Stundensatz Berater | Kontaktkarte des Experten |
| Kosten (8 h) | Stundensatz × 8 |
| Marge | Tagessatz − Kosten, dazu der Prozentsatz vom Tagessatz |
**Ein Tag sind 8 Stunden.** Hat der Berater einen Remote- *und* einen
Vor-Ort-Satz, wird mit dem **Remote-Satz** gerechnet; fehlt der, mit dem
Vor-Ort-Satz. Welcher benutzt wurde, steht daneben.
Die Farbe ist eine schnelle Ampel: grün ab 25 %, gelb darüber bis 0, rot bei
negativer Marge.
Fehlt der Tagessatz oder hat der Berater keinen Stundensatz, steht statt einer
Zahl der Grund dort – eine leere Zeile ließe offen, ob die Marge null oder
unbekannt ist. Der Tagessatz wird am Projekt gepflegt, die Stundensätze beim
Kontakt (*Kontakt → Stundensatz vor Ort / remote*).
## Historie
Alles, was im CRM getan wurde, mit Zeitpunkt, Person und – bei Änderungen – den
betroffenen Feldern samt Vorher/Nachher. Filterbar nach Art des Datensatzes.
**@-Erwähnungen:** In jedem Notizfeld öffnet `@` eine Vorschlagsliste über
Kontakte, Unternehmen und Benutzer. Im Lesezustand ist jede Erwähnung anklickbar
und führt auf das Profil; im Bearbeiten-Zustand steht dort die rohe Form
`@[Name](contact:12)` – anders ließe sie sich nicht ändern oder löschen.
## Diktat
*Einstellungen → Diktat*, einmal je Gerät: **Sprachmodell laden** (rund 300 MB,
bleibt danach im Browser). Anschließend steht in jedem längeren Textfeld ein
Mikrofon-Knopf.
Aufnahme starten, sprechen, Knopf erneut drücken – der erkannte Text wird
**angehängt**, nie ersetzt. Das Kreuz daneben verwirft die Aufnahme.
Die Erkennung läuft auf dem eigenen Rechner. Es wird nichts hochgeladen und
nichts gespeichert. Details: [04 Diktat](04-diktat.md).
## Einstellungen
| Reiter | Wer | Inhalt |
| --- | --- | --- |
| Nutzerverwaltung | Administratoren | Benutzer anlegen, Rolle, aktiv/inaktiv |
| Stammdaten | Administratoren | Auswahllisten: Branchen, Arten der Kontakte |
| KI | Administratoren | OpenAI-Schlüssel, Modell (Auswahlliste der für den Schlüssel verfügbaren Modelle), Verbindungstest |
| Mein Konto | alle | Foto, Farbe, Passwort, Design |
| Diktat | alle | Spracherkennung einrichten |
Beim Pflegen der Stammdaten gelten zwei Regeln: **Umbenennen zieht den Bestand
mit**, und **Entfernen geht nur, solange ein Wert nirgends benutzt wird** –
hinter jedem Eintrag steht, wie oft er vorkommt.
+1 -1
View File
@@ -60,7 +60,7 @@ angemeldeten Benutzer vorbelegt.
**Diktat / Whisper** – Spracherkennung, die vollständig im Browser läuft. Siehe
[04 Diktat](04-diktat.md).
**JWT** – das Token der Anmeldung, 12 Stunden gültig, im `localStorage`. Wird
**JWT** – das Token der Anmeldung, 7 Tage gültig, im `localStorage`. Wird
bei jeder Anfrage zusätzlich gegen die Datenbank geprüft.
**MentionField** – das Textfeld mit zwei Zuständen (lesen / bearbeiten) für