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:
Sascha Wolf
2026-08-18 12:21:33 +02:00
co-authored by Claude Opus 5
parent 05cc261900
commit 23087a12fb
11 changed files with 135 additions and 50 deletions
+24 -8
View File
@@ -27,6 +27,8 @@ interface AcquisitionFormProps {
editing: Acquisition | null;
/** Inhalte der Dropdowns, gepflegt unter Einstellungen -> Akquise. */
options: AcquisitionOptions;
/** Schon benutzte Anbieter - das Feld bleibt Freitext. */
providerSuggestions: string[];
onSaved: () => void;
onCancelEdit: () => void;
}
@@ -80,6 +82,7 @@ function toFormState(entry: Acquisition | null): FormState {
export const AcquisitionForm: React.FC<AcquisitionFormProps> = ({
editing,
options,
providerSuggestions,
onSaved,
onCancelEdit,
}) => {
@@ -252,14 +255,27 @@ export const AcquisitionForm: React.FC<AcquisitionFormProps> = ({
onChange={(value) => set('sender', value)}
onOpenSettings={openSettings}
/>
<OptionSelect
id="acq-provider"
label={ACQUISITION_FIELD_LABELS.provider}
value={form.provider}
options={options.provider}
onChange={(value) => set('provider', value)}
onOpenSettings={openSettings}
/>
{/* Anbieter bleibt Freitext: neue Vermittler tauchen zu oft auf, als dass
das Eintragen an einer gepflegten Liste haengen sollte. Die
Vorschlagsliste erspart das Ausschreiben bekannter Namen. */}
<div>
<label className={labelClass} htmlFor="acq-provider">
Anbieter
</label>
<input
id="acq-provider"
list="acq-provider-vorschlaege"
value={form.provider}
onChange={(event) => set('provider', event.target.value)}
placeholder="z. B. Hays"
className={fieldClass}
/>
<datalist id="acq-provider-vorschlaege">
{providerSuggestions.map((option) => (
<option key={option} value={option} />
))}
</datalist>
</div>
<OptionSelect
id="acq-channel"
label={ACQUISITION_FIELD_LABELS.channel}
@@ -175,6 +175,7 @@ export const Acquisitions: React.FC<AcquisitionsProps> = ({ view = 'eingang', re
<AcquisitionForm
editing={editing}
options={data?.options ?? EMPTY_ACQUISITION_OPTIONS}
providerSuggestions={data?.provider_suggestions ?? []}
onSaved={() => load(true)}
onCancelEdit={() => setEditing(null)}
/>
@@ -15,10 +15,13 @@ import {
} from '../../types';
/*
* Inhalte der Akquise-Dropdowns pflegen: "Von", "Anbieter", "Kanal", "Berater".
* Inhalte der Akquise-Dropdowns pflegen: "Von", "Kanal", "Berater".
*
* Vier gleich aufgebaute Listen statt vier Sonderfaelle - die Felder verhalten
* sich gleich, also sieht ihre Pflege auch gleich aus.
* "Anbieter" steht bewusst nicht dabei - das Feld ist Freitext mit Vorschlaegen
* aus den bisherigen Eintraegen.
*
* Gleich aufgebaute Listen statt Sonderfaelle - die Felder verhalten sich
* gleich, also sieht ihre Pflege auch gleich aus.
*
* Umbenennen zieht die vorhandenen Akquise-Eintraege mit, Loeschen nicht: eine
* Liste aufzuraeumen darf keine Daten aendern. Beide Antworten sagen, wie viele
@@ -133,7 +136,6 @@ export const AcquisitionSettings: React.FC = () => {
const [isLoading, setIsLoading] = useState(true);
const [drafts, setDrafts] = useState<Record<AcquisitionOptionField, string>>({
sender: '',
provider: '',
channel: '',
consultant: '',
});
@@ -219,10 +221,11 @@ export const AcquisitionSettings: React.FC = () => {
<section className="rounded-2xl border-l-4 border-l-brand-500 border border-ink-200 bg-surface p-5">
<h3 className="text-base text-ink-900">Auswahllisten der Akquise</h3>
<p className="mt-1 text-xs text-ink-400">
Diese vier Listen füllen die Dropdowns im Formular unter <em>Akquise</em>. Feste Auswahl
statt Freitext, damit aus „Hays", „hays" und „Hays AG" nicht drei Anbieter werden.
Pflegen darf jeder angemeldete Benutzer - wer beim Eintragen auf einen neuen Anbieter
stößt, soll weiterarbeiten können.
Diese drei Listen füllen die Dropdowns <em>Von</em>, <em>Kanal</em> und <em>Berater</em> im
Formular unter <em>Akquise</em>. Pflegen darf jeder angemeldete Benutzer - wer beim
Eintragen einen fehlenden Wert braucht, soll weiterarbeiten können. Das Feld
<em> Anbieter</em> steht bewusst nicht hier: dort wird frei getippt, mit Vorschlägen aus
den bisherigen Einträgen.
</p>
<p className="mt-2 text-xs text-ink-400">
<strong>Umbenennen</strong> ändert die vorhandenen Akquise-Einträge mit.
+7 -5
View File
@@ -253,16 +253,18 @@ export type OpportunityViewMode = 'grid' | 'list' | 'pipeline';
/**
* 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.
* gepflegt.
*
* "Anbieter" ist ausdruecklich nicht dabei: dort wird frei getippt, mit
* Vorschlaegen aus den bisherigen Eintraegen (`AcquisitionList.provider_suggestions`).
*/
export const ACQUISITION_OPTION_FIELDS = ['sender', 'provider', 'channel', 'consultant'] as const;
export const ACQUISITION_OPTION_FIELDS = ['sender', 'channel', 'consultant'] as const;
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',
};
@@ -270,7 +272,6 @@ export const ACQUISITION_FIELD_LABELS: Record<AcquisitionOptionField, string> =
/** 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',
};
@@ -284,7 +285,6 @@ export type AcquisitionOptions = Record<AcquisitionOptionField, AcquisitionOptio
export const EMPTY_ACQUISITION_OPTIONS: AcquisitionOptions = {
sender: [],
provider: [],
channel: [],
consultant: [],
};
@@ -337,6 +337,8 @@ export interface AcquisitionList {
counts: { eingang: number; archiv: number };
/** Inhalte der Dropdowns; kommen mit, damit die Eingabe sie sofort hat. */
options: AcquisitionOptions;
/** Schon benutzte Anbieter, der zuletzt verwendete zuerst - "Anbieter" ist Freitext. */
provider_suggestions: string[];
entries: Acquisition[];
}