fix(reports): Buchungsliste im Dialog nicht mehr rechts abschneiden

Der Dialog war 1152 Punkte breit, die Tabelle brauchte mehr - "Billable
Zeit" und "Non-billable Zeit" lagen ausserhalb und waren nur durch
seitliches Scrollen zu erreichen, was niemand sieht.

- Neue Dialogbreite 3xl: bis 1600 Punkte, hoechstens 95 % des Fensters.
- Die Tabelle liegt jetzt auf festen Spaltenbreiten (table-fixed): Datum,
  Mitarbeiter und die beiden Zeitspalten bekommen ihre Breite, Kunde und
  Projekt einen Anteil, den Rest nimmt der Task.
- Lange Kunden-, Projekt- und Tasknamen samt Notiz brechen um statt
  abgeschnitten zu werden; die Zahlen brechen nie.

Geprueft bei 1920, 1440 und 1280 Punkten: Tabelle passt ohne Querscrollen,
die letzte Spalte liegt im Dialog. Auf dem Handy scrollt die Tabelle wie
vorher innerhalb des Dialogs, die Seite selbst nicht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-21 12:30:08 +02:00
co-authored by Claude Opus 5
parent 7e7188d476
commit 994f39daff
3 changed files with 31 additions and 22 deletions
+20 -19
View File
@@ -70,16 +70,16 @@ export const EntryList: React.FC<{
return (
<div className="scrollbar-slim overflow-x-auto">
<table className="w-full min-w-[820px] text-sm">
<table className="w-full min-w-[720px] table-fixed text-sm">
<thead>
<tr className="border-b border-ink-200 text-[11px] text-ink-400">
{head('entry_date', 'Datum', 'left', 'pl-4')}
{showUser && head('user_name', 'Mitarbeiter')}
{head('customer_name', 'Kunde')}
{head('project_display', 'Projekt')}
{head('task_name', 'Task')}
{head('billable', 'Billable Zeit', 'right')}
{head('non_billable', 'Non-billable Zeit', 'right', 'pr-4')}
{head('entry_date', 'Datum', 'left', 'w-[110px] pl-4')}
{showUser && head('user_name', 'Mitarbeiter', 'left', 'w-[150px]')}
{head('customer_name', 'Kunde', 'left', 'w-[18%]')}
{head('project_display', 'Projekt', 'left', 'w-[24%]')}
{head('task_name', 'Task', 'left')}
{head('billable', 'Billable Zeit', 'right', 'w-[104px]')}
{head('non_billable', 'Non-billable Zeit', 'right', 'w-[124px] pr-4')}
</tr>
</thead>
<tbody>
@@ -110,30 +110,31 @@ export const EntryList: React.FC<{
</td>
{showUser && (
<td className="px-3 py-2.5 align-top">
<span className="flex items-center gap-2">
<span className="flex items-start gap-2">
<Avatar id={e.user_id} name={e.user_name} avatar={e.user_avatar} size={22} />
<span className="truncate font-semibold text-ink-700">{e.user_name}</span>
<span className="break-words font-semibold text-ink-700">{e.user_name}</span>
</span>
</td>
)}
<td className="px-3 py-2.5 align-top">
<span className="flex min-w-0 items-center gap-2">
<span className="h-2.5 w-2.5 shrink-0 rounded-full" style={{ background: colorFor(e.customer_id, e.customer_color) }} />
<span className="truncate font-semibold text-ink-700">{e.customer_name}</span>
<span className="flex min-w-0 items-start gap-2">
<span className="mt-1.5 h-2.5 w-2.5 shrink-0 self-start rounded-full" style={{ background: colorFor(e.customer_id, e.customer_color) }} />
<span className="break-words font-semibold text-ink-700">{e.customer_name}</span>
</span>
</td>
<td className="px-3 py-2.5 align-top">
<span className="block truncate font-bold text-ink-900">{e.project_display}</span>
<span className="tabular block truncate text-[11px] text-ink-400">{e.project_code}</span>
<span className="block break-words font-bold text-ink-900">{e.project_display}</span>
<span className="tabular block text-[11px] text-ink-400">{e.project_code}</span>
</td>
<td className="px-3 py-2.5 align-top">
<span className="block truncate text-ink-700">{e.task_name ?? '–'}</span>
{e.note && <span className="block truncate text-[11px] text-ink-400">{e.note}</span>}
{/* Tasknamen und Notizen sind teils sehr lang - hier wird umgebrochen statt abgeschnitten. */}
<span className="block break-words text-ink-700">{e.task_name ?? '–'}</span>
{e.note && <span className="block break-words text-[11px] text-ink-400">{e.note}</span>}
</td>
<td className={`tabular px-3 py-2.5 text-right align-top font-extrabold ${bill ? 'text-ink-900' : 'text-ink-300'}`}>
<td className={`tabular whitespace-nowrap px-3 py-2.5 text-right align-top font-extrabold ${bill ? 'text-ink-900' : 'text-ink-300'}`}>
{bill ? formatHours(bill) : '–'}
</td>
<td className={`tabular py-2.5 pl-3 pr-4 text-right align-top font-extrabold ${non ? 'text-ink-700' : 'text-ink-300'}`}>
<td className={`tabular whitespace-nowrap py-2.5 pl-3 pr-4 text-right align-top font-extrabold ${non ? 'text-ink-700' : 'text-ink-300'}`}>
{non ? formatHours(non) : '–'}
</td>
</tr>
+1 -1
View File
@@ -280,7 +280,7 @@ const UserEntriesDialog: React.FC<{
);
return (
<Modal
size="2xl"
size="3xl"
onClose={onClose}
title={row.name}
subtitle={
+10 -2
View File
@@ -20,10 +20,18 @@ interface ModalProps {
onClose: () => void;
children: React.ReactNode;
footer?: React.ReactNode;
size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
}
const SIZES = { sm: 'sm:max-w-sm', md: 'sm:max-w-lg', lg: 'sm:max-w-2xl', xl: 'sm:max-w-4xl', '2xl': 'sm:max-w-6xl' };
const SIZES = {
sm: 'sm:max-w-sm',
md: 'sm:max-w-lg',
lg: 'sm:max-w-2xl',
xl: 'sm:max-w-4xl',
'2xl': 'sm:max-w-6xl',
// Fuer breite Tabellen: nimmt fast das ganze Fenster, hoert aber bei 1600 px auf.
'3xl': 'sm:max-w-[min(100rem,95vw)]',
};
/** Offene Dialoge in Oeffnungsreihenfolge - Escape schliesst nur den obersten. */
const openModals: symbol[] = [];