feat(akquise): Auswahllisten statt Freitext, Felder abgesetzt
"Von", "Anbieter", "Kanal" und "Berater" sind Dropdowns. Ihre Inhalte stehen in crm_acquisition_options und werden im neuen Reiter Einstellungen -> Akquise gepflegt. Freitext hat aus "Hays", "hays" und "Hays AG" drei Anbieter gemacht; der Unique-Schluessel greift ueber die Kollation auch bei abweichender Gross-/Kleinschreibung. Eine Tabelle mit Feldkennzeichen statt vier Tabellen: die Felder verhalten sich gleich, also ist jede weitere Liste ein Wert im ENUM statt einer neuen Route und einer neuen Oberflaeche. Zwei Entscheidungen dabei: - Pflegen darf jeder Angemeldete, nicht nur ein Administrator. Eine Liste, die nur ein Admin ergaenzen kann, blockiert die Eingabe genau dann, wenn ein neuer Anbieter auftaucht. - Die Eintraege halten den Text, keinen Fremdschluessel. Umbenennen zieht sie darum ausdruecklich mit (in einer Transaktion), Loeschen nicht: eine Liste aufzuraeumen darf keine Daten aendern. Ein Wert, der nicht mehr in der Liste steht, wird im Dropdown weiter angeboten und als solcher gekennzeichnet - sonst wuerde das Bearbeiten eines alten Eintrags ihn stillschweigend ueberschreiben. Das Backend prueft beim Speichern eines Eintrags nur noch die Laenge und nicht mehr die Zugehoerigkeit zur Liste - aus demselben Grund. Dazu: die Felder der Akquise-Eingabe sind abgesetzt (getoente Flaeche, deutlicherer Rahmen, beim Hineinklicken weiss). Auf der weissen Karte gingen sie unter, und im Formular stehen bis zu fuenfzehn davon nebeneinander. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b8a78f609c
commit
05cc261900
+39
-16
@@ -21,7 +21,7 @@ export type ViewState =
|
||||
* `account` und `whisper` stehen jedem offen - jeder pflegt sein eigenes Konto
|
||||
* und richtet sein eigenes Diktat ein.
|
||||
*/
|
||||
export type SettingsTab = 'users' | 'account' | 'mail' | 'scanner' | 'whisper';
|
||||
export type SettingsTab = 'users' | 'account' | 'akquise' | 'mail' | 'scanner' | 'whisper';
|
||||
|
||||
export const ADMIN_SETTINGS_TABS: SettingsTab[] = ['users', 'mail', 'scanner'];
|
||||
|
||||
@@ -251,20 +251,43 @@ export type OpportunityViewMode = 'grid' | 'list' | 'pipeline';
|
||||
// ---------- Akquise ----------
|
||||
|
||||
/**
|
||||
* Wie das Projekt zu uns kam. Feste Auswahl, damit sich die Liste nach dem Kanal
|
||||
* gruppieren laesst - als Freitext waeren "Mail", "E-Mail" und "mail" drei Kanaele.
|
||||
* Felder der Akquise, die aus einer Auswahlliste kommen. Die Werte stehen in der
|
||||
* Datenbank (crm_acquisition_options) und werden unter Einstellungen -> Akquise
|
||||
* gepflegt - als Freitext waeren "Hays", "hays" und "Hays AG" drei Anbieter.
|
||||
*/
|
||||
export const ACQUISITION_CHANNELS = [
|
||||
'Mail',
|
||||
'Telefon',
|
||||
'Portal',
|
||||
'LinkedIn',
|
||||
'XING',
|
||||
'Empfehlung',
|
||||
'Sonstiges',
|
||||
] as const;
|
||||
export const ACQUISITION_OPTION_FIELDS = ['sender', 'provider', 'channel', 'consultant'] as const;
|
||||
|
||||
export type AcquisitionChannel = (typeof ACQUISITION_CHANNELS)[number];
|
||||
export type AcquisitionOptionField = (typeof ACQUISITION_OPTION_FIELDS)[number];
|
||||
|
||||
/** Beschriftung der Felder - dieselbe in der Liste, im Formular und in den Einstellungen. */
|
||||
export const ACQUISITION_FIELD_LABELS: Record<AcquisitionOptionField, string> = {
|
||||
sender: 'Von',
|
||||
provider: 'Anbieter',
|
||||
channel: 'Kanal',
|
||||
consultant: 'Berater',
|
||||
};
|
||||
|
||||
/** Was in der jeweiligen Liste steht - als Hilfe in den Einstellungen. */
|
||||
export const ACQUISITION_FIELD_HINTS: Record<AcquisitionOptionField, string> = {
|
||||
sender: 'Personen, von denen Projekte kommen',
|
||||
provider: 'Vermittler und Portale, z. B. Hays',
|
||||
channel: 'Auf welchem Weg das Projekt kam',
|
||||
consultant: 'Eigene Berater, für die Projekte gesucht werden',
|
||||
};
|
||||
|
||||
export interface AcquisitionOption {
|
||||
id: number;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type AcquisitionOptions = Record<AcquisitionOptionField, AcquisitionOption[]>;
|
||||
|
||||
export const EMPTY_ACQUISITION_OPTIONS: AcquisitionOptions = {
|
||||
sender: [],
|
||||
provider: [],
|
||||
channel: [],
|
||||
consultant: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Ein gefundenes Projekt, bevor daraus eine Opportunity wird.
|
||||
@@ -280,7 +303,7 @@ export interface Acquisition {
|
||||
sender: string | null;
|
||||
/** "Anbieter": Vermittler oder Portal. */
|
||||
provider: string | null;
|
||||
channel: AcquisitionChannel | null;
|
||||
channel: string | null;
|
||||
title: string;
|
||||
/** "Berater", fuer den es passt. */
|
||||
consultant: string | null;
|
||||
@@ -312,8 +335,8 @@ export interface AcquisitionList {
|
||||
archive_after_days: number;
|
||||
delete_after_days: number;
|
||||
counts: { eingang: number; archiv: number };
|
||||
/** Bisher benutzte Werte aus dem gesamten Bestand, fuer die Eingabehilfen. */
|
||||
suggestions: { providers: string[]; consultants: string[]; senders: string[] };
|
||||
/** Inhalte der Dropdowns; kommen mit, damit die Eingabe sie sofort hat. */
|
||||
options: AcquisitionOptions;
|
||||
entries: Acquisition[];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user