From 5a1f412ce3746c8732df9ee57d68c661ce532961 Mon Sep 17 00:00:00 2001 From: Benjamin Sutter Date: Sun, 7 Jun 2026 00:04:17 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20anonymes=20Suchprofil=20=E2=80=94=20Tog?= =?UTF-8?q?gle=20im=20Speichern-Schritt,=20supply-side=20Masking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Toggle 'Anonymes Suchprofil' im AISearchSavePreview (nicht in Suchmaske) - Box wechselt zu lila wenn aktiv, Text erklärt was anonymisiert wird - isAnonymous wird via buildNeedInput in gespeichertes Need geschrieben - MatchListCard + NeedSelectionPanel: zeigen 'Anonyme Anfrage' (lila) statt Firmenname Co-Authored-By: Claude Sonnet 4.6 --- src/components/demand/AISearchSavePreview.tsx | 42 ++++++++++++++++++- .../demand/NeedExtendedRequirements.tsx | 1 + src/components/match-center/MatchListCard.tsx | 7 +++- .../match-center/NeedSelectionPanel.tsx | 11 +++-- src/pages/demand/AISearch.tsx | 5 ++- 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/components/demand/AISearchSavePreview.tsx b/src/components/demand/AISearchSavePreview.tsx index f923770..c8b64b4 100644 --- a/src/components/demand/AISearchSavePreview.tsx +++ b/src/components/demand/AISearchSavePreview.tsx @@ -1,4 +1,4 @@ -import { Alert, Box, Button, CircularProgress } from '@mui/material' +import { Alert, Box, Button, CircularProgress, FormControlLabel, Switch, Typography } from '@mui/material' import { Save } from 'lucide-react' import { NeedCardPreview } from './NeedCardPreview' import type { ParseNeedResult, ParsedNeedCriteria, WeightingKey } from '../../domain/needBuilder' @@ -10,7 +10,9 @@ interface Props { needTitle: string overallConfidence: number isSaving: boolean + isAnonymous: boolean onNeedTitleChange: (t: string) => void + onAnonymousChange: (v: boolean) => void onBack: () => void onSave: () => void } @@ -22,7 +24,9 @@ export function AISearchSavePreview({ needTitle, overallConfidence, isSaving, + isAnonymous, onNeedTitleChange, + onAnonymousChange, onBack, onSave, }: Props) { @@ -39,7 +43,41 @@ export function AISearchSavePreview({ needTitle={needTitle} onNeedTitleChange={onNeedTitleChange} /> - + + {/* Anonymity option */} + + onAnonymousChange(e.target.checked)} + sx={{ + '& .MuiSwitch-switchBase.Mui-checked': { color: '#7c3aed' }, + '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { bgcolor: '#7c3aed' }, + }} + /> + } + label={ + + + Anonymes Suchprofil + + + Firmenname wird nicht an Vermieter übermittelt — Verwalter sehen nur Branche und Flächenbedarf + + + } + /> + + + diff --git a/src/components/demand/NeedExtendedRequirements.tsx b/src/components/demand/NeedExtendedRequirements.tsx index bdcbec4..b49aea2 100644 --- a/src/components/demand/NeedExtendedRequirements.tsx +++ b/src/components/demand/NeedExtendedRequirements.tsx @@ -112,6 +112,7 @@ export function NeedExtendedRequirements({ criteria: c, onChange: set }: Props) )} + ) diff --git a/src/components/match-center/MatchListCard.tsx b/src/components/match-center/MatchListCard.tsx index e425a2a..6401c4f 100644 --- a/src/components/match-center/MatchListCard.tsx +++ b/src/components/match-center/MatchListCard.tsx @@ -70,9 +70,12 @@ export function MatchListCard({ match, property, need, onSelect, onApprove }: Pr {need && ( )} - - {need.companyName} - + + + {need.isAnonymous ? 'Anonyme Anfrage' : need.companyName} + + {need.isAnonymous && ( + + )} + {pendingCount > 0 && ( diff --git a/src/pages/demand/AISearch.tsx b/src/pages/demand/AISearch.tsx index 83da95b..04a54d0 100644 --- a/src/pages/demand/AISearch.tsx +++ b/src/pages/demand/AISearch.tsx @@ -38,6 +38,7 @@ export default function AISearch() { const [weights, setWeights] = useState>(defaultWeights) const [weightingKey, setWeightingKey] = useState(0) const [needTitle, setNeedTitle] = useState('') + const [isAnonymous, setIsAnonymous] = useState(false) const [error, setError] = useState(null) const isManualTextRef = useRef(false) @@ -165,7 +166,7 @@ export default function AISearch() { setStep(NeedBuilderStep.SAVING) const entries = Object.entries(parseResult.confidenceByField) const conf = entries.length > 0 ? entries.reduce((s, [, v]) => s + v, 0) / entries.length : 0 - const input = buildNeedInput(editedCriteria, weights, needTitle, conf, 'ACTIVE') + const input = buildNeedInput({ ...editedCriteria, isAnonymous: isAnonymous || undefined }, weights, needTitle, conf, 'ACTIVE') createNeedMutation.mutate(input, { onSuccess: (created) => { queryClient.invalidateQueries({ queryKey: ['matches'] }) @@ -253,7 +254,9 @@ export default function AISearch() { needTitle={needTitle} overallConfidence={overallConfidence} isSaving={step === NeedBuilderStep.SAVING} + isAnonymous={isAnonymous} onNeedTitleChange={setNeedTitle} + onAnonymousChange={setIsAnonymous} onBack={() => setStep(NeedBuilderStep.IDLE)} onSave={handleSaveProfile} />