fix(reports): lange Projektnamen umbrechen statt abschneiden
Die Reporttabelle lief mit table-auto: ein langer Projektname wie "HAYS - Globales Template und Implementierung: ... _Fresenius - 01.10.2025-31.07.2026" zog die Namensspalte auf und schob Stunden und Betrag aus dem sichtbaren Bereich. Das truncate am Namen half nicht, weil die Spalte keine Breite hatte, an der es haette greifen koennen. Die Tabelle steht jetzt auf table-fixed mit festen Breiten fuer die Zahlenspalten; der Name bekommt den Rest und bricht um (break-words). Die Zahlen selbst bleiben einzeilig. Dazu die Kennzahlenkarte: ihre vier Spalten mit den grossen Betraegen passten ab lg nicht mehr in die Breite, der rechte Rand wurde von overflow-x-hidden abgeschnitten. Sie bricht jetzt auf zwei Spalten um und zeigt vier erst ab xl. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
80e46910fd
commit
c3aed8a3d3
@@ -52,15 +52,15 @@ export const ReportTable: React.FC<{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="scrollbar-slim overflow-x-auto">
|
<div className="scrollbar-slim overflow-x-auto">
|
||||||
<table className="w-full min-w-[720px] text-sm">
|
<table className="w-full min-w-[900px] table-fixed text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b border-ink-200 text-[11px] text-ink-400">
|
<tr className="border-b border-ink-200 text-[11px] text-ink-400">
|
||||||
{head('name', label, 'left', 'pl-4')}
|
{head('name', label, 'left', 'pl-4')}
|
||||||
{head('minutes', 'Stunden')}
|
{head('minutes', 'Stunden', 'right', 'w-[108px]')}
|
||||||
<th className="w-[22%] px-3 py-3" />
|
<th className="w-[20%] px-3 py-3" />
|
||||||
{showUtil && head('utilization', 'Auslastung')}
|
{showUtil && head('utilization', 'Auslastung', 'right', 'w-[116px]')}
|
||||||
{head('billable_minutes', 'Billable Std.')}
|
{head('billable_minutes', 'Billable Std.', 'right', 'w-[160px]')}
|
||||||
{showAmounts && head('amount', 'Billable Betrag', 'right', 'pr-4')}
|
{showAmounts && head('amount', 'Billable Betrag', 'right', 'w-[150px] pr-4')}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -82,12 +82,12 @@ export const ReportTable: React.FC<{
|
|||||||
<span className="h-2.5 w-2.5 shrink-0 rounded-full" style={{ background: colorFor(r.customer_id ?? r.project_id ?? r.name, r.color) }} />
|
<span className="h-2.5 w-2.5 shrink-0 rounded-full" style={{ background: colorFor(r.customer_id ?? r.project_id ?? r.name, r.color) }} />
|
||||||
) : null}
|
) : null}
|
||||||
<span className="min-w-0">
|
<span className="min-w-0">
|
||||||
<span className={`block truncate font-bold ${clickable ? 'text-ink-900 underline decoration-ink-200 underline-offset-4 group-hover:decoration-brand-400' : 'text-ink-800'}`}>{r.name}</span>
|
<span className={`block break-words font-bold ${clickable ? 'text-ink-900 underline decoration-ink-200 underline-offset-4 group-hover:decoration-brand-400' : 'text-ink-800'}`}>{r.name}</span>
|
||||||
{r.customer_name && <span className="block truncate text-[11px] text-ink-400">{r.customer_name}{r.code ? ` · ${r.code}` : ''}</span>}
|
{r.customer_name && <span className="block break-words text-[11px] text-ink-400">{r.customer_name}{r.code ? ` · ${r.code}` : ''}</span>}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="tabular px-3 py-3 text-right font-extrabold text-ink-900">{formatHours(r.minutes)}</td>
|
<td className="tabular whitespace-nowrap px-3 py-3 text-right font-extrabold text-ink-900">{formatHours(r.minutes)}</td>
|
||||||
<td className="px-3 py-3">
|
<td className="px-3 py-3">
|
||||||
<SplitBar a={r.billable_minutes} b={r.non_billable_minutes} max={max} delay={i * 30} />
|
<SplitBar a={r.billable_minutes} b={r.non_billable_minutes} max={max} delay={i * 30} />
|
||||||
</td>
|
</td>
|
||||||
@@ -96,10 +96,10 @@ export const ReportTable: React.FC<{
|
|||||||
{r.utilization == null ? '–' : `${r.utilization} %`}
|
{r.utilization == null ? '–' : `${r.utilization} %`}
|
||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
<td className="tabular px-3 py-3 text-right text-ink-700">
|
<td className="tabular whitespace-nowrap px-3 py-3 text-right text-ink-700">
|
||||||
{formatHours(r.billable_minutes)} <span className="inline-block w-14 whitespace-nowrap text-ink-400">({share} %)</span>
|
{formatHours(r.billable_minutes)} <span className="inline-block w-14 whitespace-nowrap text-ink-400">({share} %)</span>
|
||||||
</td>
|
</td>
|
||||||
{showAmounts && <td className="tabular py-3 pl-3 pr-4 text-right font-extrabold text-ink-900">{formatEuro(r.amount, 2)}</td>}
|
{showAmounts && <td className="tabular whitespace-nowrap py-3 pl-3 pr-4 text-right font-extrabold text-ink-900">{formatEuro(r.amount, 2)}</td>}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -187,13 +187,13 @@ const TimeReport: React.FC = () => {
|
|||||||
<Spinner label="Lade Report…" />
|
<Spinner label="Lade Report…" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Card className={`animate-rise grid gap-6 p-5 transition-opacity sm:p-6 lg:grid-cols-[1fr_1.2fr_1.3fr_1fr] ${report.loading ? 'opacity-60' : ''}`}>
|
<Card className={`animate-rise grid gap-6 p-5 transition-opacity sm:grid-cols-2 sm:p-6 xl:grid-cols-[1fr_1.2fr_1.3fr_1fr] ${report.loading ? 'opacity-60' : ''}`}>
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<p className="text-sm font-semibold text-ink-500">Gesamtstunden</p>
|
<p className="text-sm font-semibold text-ink-500">Gesamtstunden</p>
|
||||||
<CountUp value={s!.minutes / 60} format={(v) => v.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} className="text-4xl font-extrabold text-ink-900" />
|
<CountUp value={s!.minutes / 60} format={(v) => v.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} className="text-4xl font-extrabold text-ink-900" />
|
||||||
<p className="mt-1 text-xs text-ink-400">{s!.entries} Buchungen</p>
|
<p className="mt-1 text-xs text-ink-400">{s!.entries} Buchungen</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex min-w-0 items-center gap-4">
|
||||||
<Donut percent={share} size={96} stroke={13} />
|
<Donut percent={share} size={96} stroke={13} />
|
||||||
<div className="flex-1 space-y-1.5 text-sm">
|
<div className="flex-1 space-y-1.5 text-sm">
|
||||||
<p className="flex items-center justify-between gap-3">
|
<p className="flex items-center justify-between gap-3">
|
||||||
@@ -219,7 +219,7 @@ const TimeReport: React.FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<p className="text-sm font-semibold text-ink-500">Billable Betrag</p>
|
<p className="text-sm font-semibold text-ink-500">Billable Betrag</p>
|
||||||
<CountUp value={s!.amount} format={(v) => formatEuro(v, 2)} className="text-4xl font-extrabold text-ink-900" />
|
<CountUp value={s!.amount} format={(v) => formatEuro(v, 2)} className="text-4xl font-extrabold text-ink-900" />
|
||||||
<label className="mt-2 flex cursor-pointer items-center gap-2 text-sm font-semibold text-ink-700">
|
<label className="mt-2 flex cursor-pointer items-center gap-2 text-sm font-semibold text-ink-700">
|
||||||
@@ -230,7 +230,7 @@ const TimeReport: React.FC = () => {
|
|||||||
Festpreis-Beträge werden über den virtuellen Stundensatz (Budget ÷ gebuchte Stunden) verteilt – nur für den internen Gebrauch.
|
Festpreis-Beträge werden über den virtuellen Stundensatz (Budget ÷ gebuchte Stunden) verteilt – nur für den internen Gebrauch.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<p className="text-sm font-semibold text-ink-500">Auslastung</p>
|
<p className="text-sm font-semibold text-ink-500">Auslastung</p>
|
||||||
<CountUp value={capacity ? (s!.minutes_until_today / capacity) * 100 : 0} format={(v) => `${Math.round(v)} %`} className="text-4xl font-extrabold text-ink-900" />
|
<CountUp value={capacity ? (s!.minutes_until_today / capacity) * 100 : 0} format={(v) => `${Math.round(v)} %`} className="text-4xl font-extrabold text-ink-900" />
|
||||||
<p className="mt-1 text-xs text-ink-400">Kapazität {formatHours(capacity, 0)} h bis heute</p>
|
<p className="mt-1 text-xs text-ink-400">Kapazität {formatHours(capacity, 0)} h bis heute</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user