feat(vertrieb): Lead-CRM mit Dossiers, Kontakten & Austausch-Log
Neues Lead-Modul im Vertriebsbereich: qualitative Dossiers (Stammdaten, Pain & Bedarf, Soft Factors, Strategie), Kontaktpersonen, Austausch-Log mit Follow-up-Daten und Lead→Deal-Promotion. Routen /leads (Liste, nach Status gruppiert) und /lead/:id (Dossier), Teaser im Bereich Vertrieb. Migration 0008_lead_crm (lead, lead_contact, lead_touchpoint). Datenmodell-Tabellen liegen in der gemeinsamen schema.ts (Folge-Commit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
8933169ba9
commit
d72aa9fbf3
9 files changed
+4291
No files matched your search
@@ -0,0 +1,54 @@
|
||||
// Anzeige-Metadaten für Leads — zentral, von /leads und /lead/:id genutzt.
|
||||
|
||||
export type LeadStatus = 'cold' | 'warm' | 'hot' | 'won' | 'lost';
|
||||
|
||||
export const LEAD_STATUS_META: Record<
|
||||
LeadStatus,
|
||||
{ label: string; badge: string; dot: string }
|
||||
> = {
|
||||
cold: {
|
||||
label: 'Cold',
|
||||
badge: 'bg-black/5 text-[var(--color-ink-muted)]',
|
||||
dot: 'bg-[var(--color-border-strong)]',
|
||||
},
|
||||
warm: {
|
||||
label: 'Warm',
|
||||
badge: 'bg-[var(--color-ampel-amber-bg)] text-[var(--color-ampel-amber-fg)]',
|
||||
dot: 'bg-[var(--color-ampel-amber-fg)]',
|
||||
},
|
||||
hot: {
|
||||
label: 'Hot',
|
||||
badge: 'bg-[var(--color-ampel-red-bg)] text-[var(--color-ampel-red-fg)]',
|
||||
dot: 'bg-[var(--color-ampel-red-fg)]',
|
||||
},
|
||||
won: {
|
||||
label: 'Won',
|
||||
badge: 'bg-[var(--color-ampel-green-bg)] text-[var(--color-ampel-green-fg)]',
|
||||
dot: 'bg-[var(--color-ampel-green-fg)]',
|
||||
},
|
||||
lost: {
|
||||
label: 'Lost',
|
||||
badge: 'bg-black/5 text-[var(--color-ink-faint)]',
|
||||
dot: 'bg-[var(--color-ink-faint)]',
|
||||
},
|
||||
};
|
||||
|
||||
// Reihenfolge für Gruppierung/Sortierung in der Liste.
|
||||
export const LEAD_STATUS_ORDER: LeadStatus[] = ['hot', 'warm', 'cold', 'won', 'lost'];
|
||||
|
||||
export const CONTACT_FUNKTION_LABEL: Record<string, string> = {
|
||||
entscheider: 'Entscheider',
|
||||
influencer: 'Influencer',
|
||||
gatekeeper: 'Gatekeeper',
|
||||
end_user: 'End-User',
|
||||
champion: 'Champion',
|
||||
};
|
||||
|
||||
export const TOUCHPOINT_KANAL_LABEL: Record<string, string> = {
|
||||
call: 'Call',
|
||||
mail: 'Mail',
|
||||
linkedin: 'LinkedIn',
|
||||
termin: 'Termin',
|
||||
event: 'Event',
|
||||
sonstiges: 'Sonstiges',
|
||||
};
|
||||
@@ -12,6 +12,8 @@ const Reflexion = lazy(() =>
|
||||
);
|
||||
const Bereich = lazy(() => import('../routes/bereich').then((m) => ({ default: m.BereichRoute })));
|
||||
const Projekt = lazy(() => import('../routes/projekt').then((m) => ({ default: m.ProjektRoute })));
|
||||
const Leads = lazy(() => import('../routes/leads').then((m) => ({ default: m.LeadsRoute })));
|
||||
const Lead = lazy(() => import('../routes/lead').then((m) => ({ default: m.LeadRoute })));
|
||||
const Archiv = lazy(() => import('../routes/archiv').then((m) => ({ default: m.ArchivRoute })));
|
||||
const NotFound = lazy(() =>
|
||||
import('../routes/not-found').then((m) => ({ default: m.NotFoundRoute })),
|
||||
@@ -40,6 +42,8 @@ const router = createBrowserRouter([
|
||||
{ path: 'bereich', element: withSuspense(<Bereich />) },
|
||||
{ path: 'bereich/:slug', element: withSuspense(<Bereich />) },
|
||||
{ path: 'projekt/:id', element: withSuspense(<Projekt />) },
|
||||
{ path: 'leads', element: withSuspense(<Leads />) },
|
||||
{ path: 'lead/:id', element: withSuspense(<Lead />) },
|
||||
{ path: 'archiv', element: withSuspense(<Archiv />) },
|
||||
{ path: '*', element: withSuspense(<NotFound />) },
|
||||
],
|
||||
|
||||
@@ -11,6 +11,8 @@ export const voice = {
|
||||
blockerResolved: 'Blocker erledigt.',
|
||||
projectCreated: 'Projekt angelegt.',
|
||||
projectArchived: 'Projekt archiviert.',
|
||||
leadCreated: 'Lead angelegt.',
|
||||
leadPromoted: 'Lead ist jetzt ein Deal.',
|
||||
saved: 'Gespeichert.',
|
||||
strong: 'Stark.',
|
||||
},
|
||||
@@ -19,6 +21,7 @@ export const voice = {
|
||||
reflect: 'Reflektieren',
|
||||
newBlocker: 'Neuer Blocker',
|
||||
newProject: 'Neues Projekt',
|
||||
newLead: 'Neuer Lead',
|
||||
},
|
||||
hint: {
|
||||
notCheckedInToday: 'Du hast heute noch nicht gepingt.',
|
||||
|
||||
Reference in new issue
Block a user