feat(akquise): "Von" mit dem angemeldeten Benutzer vorbelegen

In der Regel traegt jeder seine eigenen Funde ein - das Feld stand trotzdem bei
jedem Projekt auf leer. Vorbelegt ist jetzt der angemeldete Benutzer; wer fuer
jemand anderen eintraegt, waehlt ihn aus der Liste.

Der eigene Name steht dabei immer zur Auswahl (OptionSelect.alwaysOffer), auch
wenn ihn niemand in den Einstellungen gepflegt hat: sonst muesste sich jeder
erst selbst in die Liste eintragen, bevor er ein Projekt erfassen kann. Steht er
schon in der Liste, gilt deren Schreibweise - sonst stuenden "Markus" und
"Markus Meier" als zwei Namen in derselben Spalte.

Beim Bearbeiten bleibt der gespeicherte Name stehen, sonst wuerde ein Eintrag
beim Oeffnen den Finder wechseln.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sascha Wolf
2026-08-18 13:53:53 +02:00
co-authored by Claude Opus 5
parent 23087a12fb
commit 2717462c3a
5 changed files with 61 additions and 12 deletions
+34 -5
View File
@@ -3,6 +3,7 @@ import { DictateTextarea } from '../ui/DictationField';
import { OptionSelect, fieldClass } from './acquisitionUi';
import { labelClass } from '../leads/leadUi';
import { useToast } from '../Toast';
import { useAuth } from '../../lib/auth';
import { useNavigation } from '../../lib/navigation';
import { acquisitionsApi, type AcquisitionInput } from '../../lib/api';
import {
@@ -58,10 +59,16 @@ const today = () => new Date().toISOString().slice(0, 10);
const numberToInput = (value: number | null) =>
value === null ? '' : String(value).replace('.', ',');
function toFormState(entry: Acquisition | null): FormState {
/**
* `ownName` ist der Name des angemeldeten Benutzers und belegt "Von" vor: in der
* Regel traegt jeder seine eigenen Funde ein. Wer fuer jemand anderen eintraegt,
* waehlt ihn in der Liste aus. Beim Bearbeiten gilt der gespeicherte Wert - der
* Eintrag wuerde sonst beim Oeffnen den Finder wechseln.
*/
function toFormState(entry: Acquisition | null, ownName: string): FormState {
return {
found_at: entry?.found_at?.slice(0, 10) ?? today(),
sender: entry?.sender ?? '',
sender: entry?.sender ?? ownName,
provider: entry?.provider ?? '',
channel: entry?.channel ?? '',
title: entry?.title ?? '',
@@ -87,9 +94,25 @@ export const AcquisitionForm: React.FC<AcquisitionFormProps> = ({
onCancelEdit,
}) => {
const { addToast } = useToast();
const { user } = useAuth();
const { goTo } = useNavigation();
const openSettings = () => goTo('Einstellungen', { tab: 'akquise' });
const [form, setForm] = useState<FormState>(() => toFormState(editing));
/*
* Steht der eigene Name schon in der Liste, gilt deren Schreibweise - sonst
* haette man "Markus" aus der Liste und "Markus Meier" vom Konto als zwei
* Eintraege in derselben Spalte.
*/
const ownName = useMemo(() => {
const name = user?.name?.trim() ?? '';
if (!name) return '';
const listed = options.sender.find(
(option) => option.value.toLowerCase() === name.toLowerCase()
);
return listed?.value ?? name;
}, [user?.name, options.sender]);
const [form, setForm] = useState<FormState>(() => toFormState(editing, ownName));
const [showMore, setShowMore] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const titleRef = useRef<HTMLInputElement>(null);
@@ -104,7 +127,7 @@ export const AcquisitionForm: React.FC<AcquisitionFormProps> = ({
* Liste, das Formular oben.
*/
useEffect(() => {
setForm(toFormState(editing));
setForm(toFormState(editing, ownName));
if (editing) {
const hasExtras = Boolean(
editing.company ||
@@ -121,7 +144,10 @@ export const AcquisitionForm: React.FC<AcquisitionFormProps> = ({
rootRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
titleRef.current?.focus();
}
}, [editing]);
// `ownName` steht erst, wenn die Auswahllisten geladen sind - deshalb haengt
// der Effekt daran. Ein angefangenes Formular wird dabei zurueckgesetzt; das
// passiert nur direkt beim Oeffnen der Ansicht.
}, [editing, ownName]);
// Volumen mitrechnen wie in der Opportunity: Festpreis schlaegt Tagessatz.
const volume = useMemo(() => {
@@ -247,6 +273,8 @@ export const AcquisitionForm: React.FC<AcquisitionFormProps> = ({
className={fieldClass}
/>
</div>
{/* Vorbelegt mit dem angemeldeten Benutzer; fuer jemand anderen wird er
in der Liste gewechselt. */}
<OptionSelect
id="acq-sender"
label={ACQUISITION_FIELD_LABELS.sender}
@@ -254,6 +282,7 @@ export const AcquisitionForm: React.FC<AcquisitionFormProps> = ({
options={options.sender}
onChange={(value) => set('sender', value)}
onOpenSettings={openSettings}
alwaysOffer={ownName}
/>
{/* Anbieter bleibt Freitext: neue Vermittler tauchen zu oft auf, als dass
das Eintragen an einer gepflegten Liste haengen sollte. Die