e70ae79d-5898-4c55-8ceb-d2c701ce08b4

This commit is contained in:
Z User committed 2026-10-26 14:18:39 +00:00
1 parent d226dabba3
commit 5855f699f3
53 files changed
+1911 -44

No files matched your search

+21 -10
View File
@@ -9,7 +9,9 @@ import { Button } from '@/components/ui/button'
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'
import { Skeleton } from '@/components/ui/skeleton'
import { PageHeader } from '../primitives'
import { CreateDealDialog } from '../create-deal-dialog'
import { formatEuro, formatDate } from '@/lib/format'
import { useAos } from '@/store/aos'
import { cn } from '@/lib/utils'
interface CrmData {
@@ -32,7 +34,9 @@ const STAGE_COLORS: Record<string, string> = {
}
export function CrmView() {
const { data, loading } = useFetch<CrmData>('/api/aos/crm')
const { data, loading, refetch } = useFetch<CrmData>('/api/aos/crm')
const setSelectedCompany = useAos((s) => s.setSelectedCompany)
const [createOpen, setCreateOpen] = useState(false)
if (loading || !data) {
return (
@@ -56,9 +60,9 @@ export function CrmView() {
title="CRM"
description="Firmen, Ansprechpartner und Deal-Pipeline. Konfigurierbare Stages, DSGVO-Konsens-Protokoll pro Kontakt."
actions={
<Button size="sm">
<Button size="sm" onClick={() => setCreateOpen(true)}>
<Plus className="size-4" />
Firma
Deal
</Button>
}
/>
@@ -94,13 +98,20 @@ export function CrmView() {
</TabsContent>
<TabsContent value="companies" className="mt-4">
<CompaniesTable companies={data.companies} />
<CompaniesTable companies={data.companies} onSelectCompany={setSelectedCompany} />
</TabsContent>
<TabsContent value="persons" className="mt-4">
<PersonsTable persons={data.persons} />
<PersonsTable persons={data.persons} onSelectCompany={setSelectedCompany} />
</TabsContent>
</Tabs>
<CreateDealDialog
open={createOpen}
onOpenChange={setCreateOpen}
companies={data.companies.map((c) => ({ id: c.id, name: c.name }))}
onCreated={refetch}
/>
</div>
)
}
@@ -158,7 +169,7 @@ function PipelineBoard({ pipeline }: { pipeline: CrmData['pipeline'] }) {
)
}
function CompaniesTable({ companies }: { companies: CrmData['companies'] }) {
function CompaniesTable({ companies, onSelectCompany }: { companies: CrmData['companies']; onSelectCompany: (id: string) => void }) {
const [q, setQ] = useState('')
const filtered = companies.filter(
(c) => c.name.toLowerCase().includes(q.toLowerCase()) || (c.city ?? '').toLowerCase().includes(q.toLowerCase())
@@ -190,14 +201,14 @@ function CompaniesTable({ companies }: { companies: CrmData['companies'] }) {
</thead>
<tbody className="divide-y divide-border">
{filtered.map((c) => (
<tr key={c.id} className="transition hover:bg-muted/40">
<tr key={c.id} className="cursor-pointer transition hover:bg-muted/40" onClick={() => onSelectCompany(c.id)}>
<td className="px-4 py-2.5">
<div className="flex items-center gap-2">
<Building2 className="size-3.5 text-muted-foreground" />
<div className="flex flex-col">
<span className="font-medium">{c.name}</span>
{c.website && (
<a href={`https://${c.website}`} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground">
<a href={`https://${c.website}`} target="_blank" rel="noopener noreferrer" onClick={(e) => e.stopPropagation()} className="flex items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground">
<Globe className="size-3" />{c.website}
</a>
)}
@@ -238,7 +249,7 @@ function CompaniesTable({ companies }: { companies: CrmData['companies'] }) {
)
}
function PersonsTable({ persons }: { persons: CrmData['persons'] }) {
function PersonsTable({ persons, onSelectCompany }: { persons: CrmData['persons']; onSelectCompany: (id: string) => void }) {
return (
<Card>
<CardContent className="p-0">
@@ -254,7 +265,7 @@ function PersonsTable({ persons }: { persons: CrmData['persons'] }) {
</thead>
<tbody className="divide-y divide-border">
{persons.map((p) => (
<tr key={p.id} className="transition hover:bg-muted/40">
<tr key={p.id} className="cursor-pointer transition hover:bg-muted/40" onClick={() => p.companyId && onSelectCompany(p.companyId)}>
<td className="px-4 py-2.5 font-medium">{p.firstName} {p.lastName}</td>
<td className="px-4 py-2.5 text-xs text-muted-foreground">{p.company?.name ?? '—'}</td>
<td className="px-4 py-2.5 text-xs">{p.role ?? '—'}</td>