fix(akquise): Anbieter wieder als Freitext mit Vorschlaegen
Das Dropdown fiel dem Feld zur Last: neue Vermittler tauchen zu oft auf, als dass das Eintragen an einer gepflegten Liste haengen sollte. Anbieter ist wieder ein Textfeld, jetzt mit Vorschlagsliste aus den bisherigen Eintraegen - der zuletzt benutzte zuerst. In den Einstellungen bleiben die drei Listen "Von", "Kanal" und "Berater". dropProviderOptions() nimmt 'provider' aus dem ENUM der Optionstabelle und loescht die dort gepflegten Werte; die Anbieter der Akquise-Eintraege bleiben unberuehrt, sie stehen als Text in crm_acquisitions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
05cc261900
commit
23087a12fb
@@ -25,7 +25,7 @@ erDiagram
|
||||
crm_opportunities ||--o{ crm_opportunity_contacts : hat
|
||||
crm_users ||--o{ crm_acquisitions : created_by
|
||||
crm_acquisitions |o--o| crm_opportunities : "wird zu"
|
||||
crm_acquisition_options ||--o{ crm_acquisitions : "fuellt Von, Anbieter, Kanal, Berater"
|
||||
crm_acquisition_options ||--o{ crm_acquisitions : "fuellt Von, Kanal, Berater"
|
||||
|
||||
crm_users {
|
||||
int id PK
|
||||
@@ -91,7 +91,7 @@ erDiagram
|
||||
}
|
||||
crm_acquisition_options {
|
||||
int id PK
|
||||
enum field "sender | provider | channel | consultant"
|
||||
enum field "sender | channel | consultant"
|
||||
varchar value "Inhalt des Dropdowns"
|
||||
}
|
||||
crm_events {
|
||||
@@ -126,14 +126,16 @@ Sechs Eigenheiten, die man kennen muss:
|
||||
- **`avatar` ist eine Data-URL in der Datenbank.** Der Browser verkleinert jedes
|
||||
Bild vor dem Upload auf 256 px (`src/lib/imageCompress.ts`), aus 2,9 MB werden
|
||||
wenige Kilobyte.
|
||||
- **Die Dropdowns der Akquise stehen in einer Tabelle, nicht in vier.**
|
||||
- **Die Dropdowns der Akquise stehen in einer Tabelle, nicht in drei.**
|
||||
`crm_acquisition_options` unterscheidet die Listen über die Spalte `field`. Die
|
||||
vier Felder verhalten sich gleich, also ist jede weitere Liste ein Wert im
|
||||
ENUM statt einer neuen Tabelle samt Route und Oberfläche. Der Unique-Schlüssel
|
||||
auf `(field, value)` greift über die Kollation `utf8mb4_unicode_ci` auch bei
|
||||
abweichender Groß-/Kleinschreibung - „hays" ist neben „Hays" kein neuer Wert.
|
||||
Felder verhalten sich gleich, also ist jede weitere Liste ein Wert im ENUM
|
||||
statt einer neuen Tabelle samt Route und Oberfläche. Der Unique-Schlüssel auf
|
||||
`(field, value)` greift über die Kollation `utf8mb4_unicode_ci` auch bei
|
||||
abweichender Groß-/Kleinschreibung - „mail" ist neben „Mail" kein neuer Wert.
|
||||
Die Akquise-Einträge halten den **Text**, keinen Fremdschlüssel: wird ein Wert
|
||||
aus der Liste entfernt, bleiben die Einträge lesbar und änderbar.
|
||||
`crm_acquisitions.provider` (Anbieter) gehört nicht dazu - das Feld ist
|
||||
Freitext, seine Vorschläge kommen aus den vorhandenen Einträgen.
|
||||
- **`crm_acquisitions` hat keine Zustandsspalte.** Eingang, Archiv und Löschung
|
||||
ergeben sich aus `found_at` (14 bzw. 90 Tage) und `converted_at`. Eine Spalte,
|
||||
die ein Job umsetzen müsste, kann von der Wirklichkeit abweichen - eine
|
||||
|
||||
@@ -89,16 +89,19 @@ weg.
|
||||
| --- | --- |
|
||||
| **Datum** | wann gefunden. Vorbelegt mit heute; entscheidet später über Archiv und Löschung |
|
||||
| **Von** | Auswahl: wer es geschickt hat |
|
||||
| **Anbieter** | Auswahl: Vermittler oder Portal, z. B. Hays |
|
||||
| **Anbieter** | Freitext: Vermittler oder Portal, z. B. Hays. Vorschläge aus den bisherigen Einträgen |
|
||||
| **Kanal** | Auswahl: auf welchem Weg es kam |
|
||||
| **Projekttitel** | das einzige Pflichtfeld |
|
||||
| **Berater** | Auswahl: für wen das Projekt passt |
|
||||
| **Link** | Adresse der Ausschreibung. Ist einer hinterlegt, wird der Titel in der Liste anklickbar und öffnet ihn in einem neuen Tab |
|
||||
|
||||
*Von*, *Anbieter*, *Kanal* und *Berater* sind **Auswahllisten**. Ihre Inhalte
|
||||
stehen unter *Einstellungen → Akquise* und lassen sich dort von jedem Benutzer
|
||||
pflegen - Freitext hätte aus „Hays", „hays" und „Hays AG" drei Anbieter gemacht.
|
||||
Ist eine Liste noch leer, steht unter dem Feld ein Verweis dorthin.
|
||||
*Von*, *Kanal* und *Berater* sind **Auswahllisten**. Ihre Inhalte stehen unter
|
||||
*Einstellungen → Akquise* und lassen sich dort von jedem Benutzer pflegen. Ist
|
||||
eine Liste noch leer, steht unter dem Feld ein Verweis dorthin.
|
||||
|
||||
**Anbieter** ist bewusst keine Liste, sondern ein Freitextfeld: neue Vermittler
|
||||
tauchen zu oft auf, als dass das Eintragen daran hängen sollte. Das Feld schlägt
|
||||
vor, was schon einmal benutzt wurde - der zuletzt verwendete Anbieter zuerst.
|
||||
|
||||
Beim Link genügt `hays.de/projekt/1`, das `https://` wird ergänzt.
|
||||
|
||||
@@ -564,13 +567,14 @@ Datenbank.
|
||||
| --- | --- | --- |
|
||||
| **Nutzerverwaltung** | Admin | Benutzer anlegen, bearbeiten, deaktivieren, löschen |
|
||||
| **Mein Konto** | alle | Foto, Terminfarbe, Passwort ändern |
|
||||
| **Akquise** | alle | Inhalte der Auswahllisten *Von*, *Anbieter*, *Kanal*, *Berater* |
|
||||
| **Akquise** | alle | Inhalte der Auswahllisten *Von*, *Kanal*, *Berater* |
|
||||
| **Postfach** | Admin | Verbindung zu Microsoft 365, Abgleich, Kontaktübernahme |
|
||||
| **Scanner** | Admin | Suchintervall, Schwellenwerte, KI-Schlüssel, Ton, E-Mail |
|
||||
| **Whisper** | alle | Spracherkennung für das Diktat einrichten |
|
||||
|
||||
Im Reiter **Akquise** stehen die vier Auswahllisten des Akquise-Formulars, jede
|
||||
Im Reiter **Akquise** stehen die drei Auswahllisten des Akquise-Formulars, jede
|
||||
mit einem Feld zum Hinzufügen und je Eintrag einem Stift und einem Papierkorb.
|
||||
*Anbieter* fehlt hier: das Feld ist Freitext mit Vorschlägen.
|
||||
Zwei Dinge sind dabei unterschiedlich:
|
||||
|
||||
* **Umbenennen** ändert die vorhandenen Akquise-Einträge mit. Aus „Hays" wird in
|
||||
|
||||
@@ -28,7 +28,7 @@ Beim Start: `initDatabase()` (CRM-Schema, Admin-Seed) → `initScannerDatabase()
|
||||
| Export | Zweck |
|
||||
| --- | --- |
|
||||
| `pool` | mysql2-Pool, `connectionLimit: 8`, `dateStrings: true` |
|
||||
| `initDatabase()` | führt `schema.sql` Statement für Statement aus, dann `migrateLeadKind()`, `migrateMailPool()`, `ensureAdminUser()` |
|
||||
| `initDatabase()` | führt `schema.sql` Statement für Statement aus, dann `migrateLeadKind()`, `migrateMailPool()`, `seedAcquisitionOptions()`, `dropProviderOptions()`, `ensureAdminUser()` |
|
||||
|
||||
`dateStrings: true` ist eine bewusste Entscheidung: Termine kommen als
|
||||
`YYYY-MM-DD HH:MM:SS` **ohne** Zeitzonenumrechnung an. Die Terminansicht rechnet mit
|
||||
@@ -52,6 +52,10 @@ Mal etwas:
|
||||
Einträgen als Freitext steht. Auch hier ein Schalter in `crm_settings` - ohne
|
||||
ihn wäre jeder Neustart ein Wiederherstellen von Werten, die jemand bewusst
|
||||
entfernt hat.
|
||||
* `dropProviderOptions()` nimmt *Anbieter* wieder aus den Auswahllisten heraus -
|
||||
das Feld ist Freitext mit Vorschlägen. Erkannt am Spaltentyp: steht `provider`
|
||||
noch im ENUM, ist die Umstellung offen. Die Zeilen müssen vor dem Verengen weg,
|
||||
sonst weist MariaDB den `ALTER` ab.
|
||||
|
||||
`ensureAdminUser()` legt nur an, wenn `crm_users` **leer** ist. Ohne
|
||||
`ADMIN_PASSWORD` erzeugt es ein Zufallspasswort und schreibt es einmalig ins Log -
|
||||
@@ -264,7 +268,7 @@ Warum genau diese Konstanten festgelegt sind, steht in
|
||||
| `components/acquisition/Acquisitions.tsx` | Akquise und Archiv (`view`); Suche, Sortierung, Übernahme, Löschen |
|
||||
| `components/acquisition/AcquisitionForm.tsx` | Eingabe über der Liste; behält Datum, Anbieter, Kanal und Berater für den nächsten Eintrag |
|
||||
| `components/acquisition/AcquisitionTable.tsx` | die Liste; `safeLink()` lässt nur http(s) zum Link werden |
|
||||
| `components/acquisition/acquisitionUi.tsx` | `fieldClass` (abgesetzte Felder) und `OptionSelect` - zeigt einen Wert, der nicht mehr in der Liste steht, als solchen |
|
||||
| `components/acquisition/acquisitionUi.tsx` | `fieldClass` (abgesetzte Felder) und `OptionSelect` - zeigt einen Wert, der nicht mehr in der Liste steht, als solchen. *Anbieter* nutzt es nicht: Freitext mit `<datalist>` |
|
||||
| `components/settings/AcquisitionSettings.tsx` | Reiter *Einstellungen → Akquise*: die vier Auswahllisten pflegen |
|
||||
| `components/leads/leadUi.tsx` | `inputClass`, `labelClass`, `KindBadge`, `Avatar`, Farben der Kontaktart |
|
||||
| `components/leads/mailPool.ts` | `findMailPoolMatch()`, `diffFields()`, `isConflict()`, `MAIL_POOL_FIELDS` - Dublettenabgleich des Vorbereichs |
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ Quelltext.
|
||||
| **Match** / Treffer | Die Bewertung **eines** Projekts für **einen** Agenten: Prozentwert, Pro-Liste, Contra-Liste, Status. Tabelle `matches`. Ein Projekt erzeugt so viele Treffer, wie es aktive Agenten gibt. |
|
||||
| **Opportunity** | Geschäft in Arbeit: Phase, Tagessatz und Laufzeit oder Projektpreis, Wahrscheinlichkeit, Ansprechpartner. Tabelle `crm_opportunities`. |
|
||||
| **Akquise** | Eingang für gefundene Projekte, bevor daraus eine Opportunity wird. Tabelle `crm_acquisitions`. Nach 14 Tagen im **Archiv**, nach 90 Tagen gelöscht. |
|
||||
| **Auswahllisten** (Akquise) | Inhalte der Dropdowns *Von*, *Anbieter*, *Kanal*, *Berater*. Tabelle `crm_acquisition_options`, gepflegt unter *Einstellungen → Akquise*. |
|
||||
| **Auswahllisten** (Akquise) | Inhalte der Dropdowns *Von*, *Kanal*, *Berater*. Tabelle `crm_acquisition_options`, gepflegt unter *Einstellungen → Akquise*. *Anbieter* ist Freitext mit Vorschlägen. |
|
||||
| **Art** (eines Kontakts) | Recruiter, Partner oder Endkunde. Spalte `crm_leads.kind`; hieß bis 18.08.2026 „Status" mit den Werten Neu .. Verloren. |
|
||||
| **Papierkorb** | Im CRM gelöschte Mails. Umkehrbar, bis er geleert wird. Im Postfach crm@perinco.de bleiben die Mails in jedem Fall; der Abgleich holt gelöschte nicht wieder herein. |
|
||||
| **Phase** / Stage | Fortschritt einer Opportunity: Neu, Qualifizierung, Angebot, Verhandlung, Gewonnen, Verloren. |
|
||||
|
||||
Reference in New Issue
Block a user