chore(format): Prettier auf B4-Zieldateien anwenden
Vorbereitung der Modal-Umstellung: TimelineSection/leads/bereich (Alt-Bestand nie formatiert) + kleiner B3-Nachzug in projekt/lead. Reine Formatierung, damit die folgenden Modal-Commits nur echte Aenderungen zeigen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
8b0df9cc4e
commit
30006e3b32
5 files changed
+67
-51
No files matched your search
@@ -80,10 +80,7 @@ export function TimelineSection() {
|
||||
)}
|
||||
|
||||
{manualOpen && (
|
||||
<ManualDialog
|
||||
onClose={() => setManualOpen(false)}
|
||||
todos={todayQuery.data?.todos ?? []}
|
||||
/>
|
||||
<ManualDialog onClose={() => setManualOpen(false)} todos={todayQuery.data?.todos ?? []} />
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
@@ -258,7 +255,10 @@ function TimelineRow({
|
||||
<p className="truncate">{label}</p>
|
||||
<p className="text-xs text-[var(--color-ink-muted)]">
|
||||
{CATEGORY_LABEL[entry.category]}
|
||||
{entry.description && !todoTitle && entry.description !== label && ` · ${entry.description}`}
|
||||
{entry.description &&
|
||||
!todoTitle &&
|
||||
entry.description !== label &&
|
||||
` · ${entry.description}`}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -111,22 +111,22 @@ function Section({
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<ProjectLogo url={p.logoUrl} name={p.name} size={40} />
|
||||
<div className="min-w-0">
|
||||
<p className="flex items-center gap-2 font-medium">
|
||||
{p.name}
|
||||
{p.modus !== 'standard' && (
|
||||
<span
|
||||
className={clsx(
|
||||
'rounded-full px-2 py-0.5 text-[10px] font-medium',
|
||||
MODUS_META[p.modus].badge,
|
||||
)}
|
||||
>
|
||||
{MODUS_META[p.modus].label}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-[var(--color-ink-muted)]">
|
||||
{p.deadline ? `bis ${p.deadline}` : 'kein Deadline'}
|
||||
</p>
|
||||
<p className="flex items-center gap-2 font-medium">
|
||||
{p.name}
|
||||
{p.modus !== 'standard' && (
|
||||
<span
|
||||
className={clsx(
|
||||
'rounded-full px-2 py-0.5 text-[10px] font-medium',
|
||||
MODUS_META[p.modus].badge,
|
||||
)}
|
||||
>
|
||||
{MODUS_META[p.modus].label}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-[var(--color-ink-muted)]">
|
||||
{p.deadline ? `bis ${p.deadline}` : 'kein Deadline'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<PflichtMinimumIndicator state={p.pflichtMinimumState} />
|
||||
@@ -373,14 +373,15 @@ function LeadsTeaser() {
|
||||
const q = trpc.lead.list.useQuery();
|
||||
const leads = q.data ?? [];
|
||||
const offen = leads.filter((l) => l.status !== 'won' && l.status !== 'lost');
|
||||
const top = [...offen]
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, 3);
|
||||
const top = [...offen].sort((a, b) => b.score - a.score).slice(0, 3);
|
||||
return (
|
||||
<section>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold">Leads</h2>
|
||||
<Link to="/leads" className="text-sm text-[var(--color-ink-muted)] hover:text-[var(--color-ink)] hover:underline">
|
||||
<Link
|
||||
to="/leads"
|
||||
className="text-sm text-[var(--color-ink-muted)] hover:text-[var(--color-ink)] hover:underline"
|
||||
>
|
||||
Alle Leads ({leads.length}) →
|
||||
</Link>
|
||||
</div>
|
||||
@@ -409,7 +410,9 @@ function LeadsTeaser() {
|
||||
<span className={clsx('h-2 w-2 rounded-full', meta.dot)} />
|
||||
<span className="font-medium">{l.firma}</span>
|
||||
{l.subSegment && (
|
||||
<span className="text-xs text-[var(--color-ink-muted)]">{l.subSegment}</span>
|
||||
<span className="text-xs text-[var(--color-ink-muted)]">
|
||||
{l.subSegment}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="font-mono text-xs tabular-nums text-[var(--color-ink-muted)]">
|
||||
@@ -456,4 +459,3 @@ function ProjektListeFuerBereich({ slug }: { slug: 'vertrieb' | 'marketing' }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,10 +75,7 @@ export function LeadRoute() {
|
||||
if (q.isPending) return <SkeletonCard lines={8} />;
|
||||
if (q.isError || !q.data)
|
||||
return (
|
||||
<ErrorState
|
||||
message="Lead konnte nicht geladen werden."
|
||||
onRetry={() => void q.refetch()}
|
||||
/>
|
||||
<ErrorState message="Lead konnte nicht geladen werden." onRetry={() => void q.refetch()} />
|
||||
);
|
||||
const { lead, contacts, touchpoints } = q.data;
|
||||
|
||||
|
||||
@@ -7,11 +7,7 @@ import { voice } from '../lib/voice';
|
||||
import { Card } from '../components/ui/Card';
|
||||
import { Button } from '../components/ui/Button';
|
||||
import { SkeletonCard } from '../components/ui/SkeletonCard';
|
||||
import {
|
||||
LEAD_STATUS_META,
|
||||
LEAD_STATUS_ORDER,
|
||||
type LeadStatus,
|
||||
} from '../lib/lead-meta';
|
||||
import { LEAD_STATUS_META, LEAD_STATUS_ORDER, type LeadStatus } from '../lib/lead-meta';
|
||||
|
||||
export function LeadsRoute() {
|
||||
const q = trpc.lead.list.useQuery();
|
||||
@@ -47,9 +43,7 @@ export function LeadsRoute() {
|
||||
{leads.length === 0 ? (
|
||||
<Card.Root>
|
||||
<Card.Body>
|
||||
<p className="text-sm text-[var(--color-ink-muted)]">
|
||||
Noch keine Leads. Leg einen an.
|
||||
</p>
|
||||
<p className="text-sm text-[var(--color-ink-muted)]">Noch keine Leads. Leg einen an.</p>
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
) : (
|
||||
@@ -113,7 +107,10 @@ function LeadImportDialog({ onClose }: { onClose: () => void }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/30" onClick={onClose}>
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/30"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="w-full max-w-md rounded-[var(--radius-xl)] bg-[var(--color-bg)] p-6 shadow-[var(--shadow-lg)]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
@@ -167,8 +164,12 @@ function LeadImportDialog({ onClose }: { onClose: () => void }) {
|
||||
<ul className="mt-1 space-y-0.5 text-xs text-[var(--color-ink-muted)]">
|
||||
<li>{result.contacts} Kontakte angelegt</li>
|
||||
<li>{result.touchpoints} Notizen als Log-Eintrag</li>
|
||||
{result.skippedDuplicate > 0 && <li>{result.skippedDuplicate} Duplikate übersprungen</li>}
|
||||
{result.skippedEmpty > 0 && <li>{result.skippedEmpty} leere Zeilen übersprungen</li>}
|
||||
{result.skippedDuplicate > 0 && (
|
||||
<li>{result.skippedDuplicate} Duplikate übersprungen</li>
|
||||
)}
|
||||
{result.skippedEmpty > 0 && (
|
||||
<li>{result.skippedEmpty} leere Zeilen übersprungen</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
@@ -281,7 +282,10 @@ function LeadCreateDialog({ onClose }: { onClose: () => void }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/30" onClick={onClose}>
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/30"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="max-h-[90vh] w-full max-w-md overflow-y-auto rounded-[var(--radius-xl)] bg-[var(--color-bg)] p-6 shadow-[var(--shadow-lg)]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
@@ -301,7 +305,11 @@ function LeadCreateDialog({ onClose }: { onClose: () => void }) {
|
||||
</Field>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<Field label="Branche">
|
||||
<input value={branche} onChange={(e) => setBranche(e.target.value)} className={inputCls} />
|
||||
<input
|
||||
value={branche}
|
||||
onChange={(e) => setBranche(e.target.value)}
|
||||
className={inputCls}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Nische / Sub-Segment">
|
||||
<input
|
||||
@@ -312,13 +320,25 @@ function LeadCreateDialog({ onClose }: { onClose: () => void }) {
|
||||
</Field>
|
||||
</div>
|
||||
<Field label="Website">
|
||||
<input value={website} onChange={(e) => setWebsite(e.target.value)} className={inputCls} />
|
||||
<input
|
||||
value={website}
|
||||
onChange={(e) => setWebsite(e.target.value)}
|
||||
className={inputCls}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Quelle (wie gefunden)">
|
||||
<input value={quelle} onChange={(e) => setQuelle(e.target.value)} className={inputCls} />
|
||||
<input
|
||||
value={quelle}
|
||||
onChange={(e) => setQuelle(e.target.value)}
|
||||
className={inputCls}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Projekt (optional)">
|
||||
<select value={projectId} onChange={(e) => setProjectId(e.target.value)} className={inputCls}>
|
||||
<select
|
||||
value={projectId}
|
||||
onChange={(e) => setProjectId(e.target.value)}
|
||||
className={inputCls}
|
||||
>
|
||||
<option value="">— kein Projekt —</option>
|
||||
{projects.map((p) => (
|
||||
<option key={p.id} value={p.id}>
|
||||
|
||||
@@ -74,10 +74,7 @@ export function ProjektRoute() {
|
||||
if (q.isPending) return <SkeletonCard lines={6} />;
|
||||
if (q.isError || !q.data)
|
||||
return (
|
||||
<ErrorState
|
||||
message="Projekt konnte nicht geladen werden."
|
||||
onRetry={() => void q.refetch()}
|
||||
/>
|
||||
<ErrorState message="Projekt konnte nicht geladen werden." onRetry={() => void q.refetch()} />
|
||||
);
|
||||
const { project: p, area: a, milestones } = q.data;
|
||||
const isP1 = me?.id === p.verantwortlicheUserId;
|
||||
|
||||
Reference in new issue
Block a user