feat(opportunities): Phasen des Vermittlungsprozesses, Kanban zuerst, Zahlen im Menue
Drei Dinge: - Die Phasen sind jetzt die des Vermittlungsprozesses: Neu, Beworben, Erstgespraech Berater, Kundenvorstellung, Interview Kunde, Zusage, Beauftragt, Absage. Offen ist alles bis einschliesslich Zusage - dort ist der Einsatz bestaetigt, die Beauftragung steht aber noch aus. migrateOpportunityStages() stellt bestehende Datenbanken um und setzt alle vorhandenen Opportunities auf "Neu": in welcher der neuen Phasen ein Projekt steckt, sagt der alte Trichter nicht (so mit dem Nutzer entschieden). Der Datenbankwert heisst 'Erstgespraech Berater' ohne Umlaut - Projektkonvention fuer Datenbankwerte. Angezeigt wird er ueber STAGE_LABELS; dazu gibt es STAGE_HINTS mit der Erlaeuterung je Phase als Titelhinweis an Auswahl, Badge und Spaltenkopf. - Kanban ist die erste Ansicht der Opportunities. Die Pipeline ist die Arbeitsansicht, Kachel und Liste sind zum Nachschauen. - Hinter "Archiv", beiden "Papierkorb"-Punkten und "Unpassend" steht die Anzahl, wie schon bei "Aus Mails". Die Zahlen kommen aus einem eigenen Endpunkt (/api/badges), weil sie alle ins Menue gehoeren und zusammen kommen sollen; Inhalte laedt weiter jede Ansicht selbst. Der Scanner liegt in einer eigenen Datenbank und kann fehlen - sein Zaehler ist dann null und die Leiste zeigt dort einfach keine Zahl. Der Aufruf haengt bewusst hinter den Daten und nicht im Promise.all: faellt er aus, bleibt das Menue ohne Zahlen statt die Ansicht mit einem Fehler. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1ac0e2d2c7
commit
42639c65fa
+27
-2
@@ -18,7 +18,15 @@ import { ScannerPortals } from './components/scanner/ScannerPortals';
|
||||
import { useToast } from './components/Toast';
|
||||
import { NavigationProvider } from './lib/navigation';
|
||||
import { useAuth } from './lib/auth';
|
||||
import { connectionsApi, eventsApi, leadsApi, opportunitiesApi, usersApi } from './lib/api';
|
||||
import {
|
||||
badgesApi,
|
||||
connectionsApi,
|
||||
eventsApi,
|
||||
leadsApi,
|
||||
opportunitiesApi,
|
||||
usersApi,
|
||||
type SidebarBadges,
|
||||
} from './lib/api';
|
||||
import {
|
||||
OPEN_STAGES,
|
||||
formatEuro,
|
||||
@@ -71,6 +79,7 @@ const App: React.FC = () => {
|
||||
const [users, setUsers] = useState<AppUser[]>([]);
|
||||
const [events, setEvents] = useState<CalendarEvent[]>([]);
|
||||
const [opportunities, setOpportunities] = useState<Opportunity[]>([]);
|
||||
const [badges, setBadges] = useState<SidebarBadges | null>(null);
|
||||
const [isDataLoading, setIsDataLoading] = useState(false);
|
||||
|
||||
/**
|
||||
@@ -104,6 +113,16 @@ const App: React.FC = () => {
|
||||
setEvents(eventRows);
|
||||
setUsers(userRows);
|
||||
setOpportunities(opportunityRows);
|
||||
|
||||
/*
|
||||
* Die Zahlen der Seitenleiste haengen bewusst hinter den Daten und nicht
|
||||
* im Promise.all: sie sind Beiwerk. Faellt der Aufruf aus, bleibt das
|
||||
* Menue ohne Zahlen statt die ganze Ansicht mit einem Fehler.
|
||||
*/
|
||||
badgesApi
|
||||
.get()
|
||||
.then(setBadges)
|
||||
.catch(() => setBadges(null));
|
||||
} catch (err) {
|
||||
addToast(err instanceof Error ? err.message : 'Daten konnten nicht geladen werden.', 'error');
|
||||
} finally {
|
||||
@@ -180,7 +199,13 @@ const App: React.FC = () => {
|
||||
onViewChange={goTo}
|
||||
isOpen={isSidebarOpen}
|
||||
onToggle={() => setIsSidebarOpen((open) => !open)}
|
||||
mailPoolCount={mailPoolLeads.length}
|
||||
counts={{
|
||||
'Aus Mails': mailPoolLeads.length,
|
||||
Archiv: badges?.acquisition_archive,
|
||||
'Opportunity-Papierkorb': badges?.opportunity_trash,
|
||||
Papierkorb: badges?.mail_trash,
|
||||
Unpassend: badges?.scanner_rejected,
|
||||
}}
|
||||
/>
|
||||
|
||||
<main className="flex min-w-0 flex-1 flex-col overflow-hidden">
|
||||
|
||||
Reference in New Issue
Block a user