Projektkonto: Projektliste mit Vereinbart, Berechnet, Noch offen und Zeit, Knöpfe Rechnung und Auftragsbestätigung
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
241f7cdec6
commit
cc95018083
2 files changed
+104
-7
No files matched your search
@@ -1,11 +1,81 @@
|
||||
import './projektkonto.css'
|
||||
import { Card } from '../ui'
|
||||
import { kontoZeile, ohneProjektZeile, stripeHinweis, KEINE_PROJEKTE_HINWEIS } from '../../lib/projektkontoView'
|
||||
|
||||
/**
|
||||
* Platzhalter von W0: Strang W1 ersetzt diese Datei vollständig (Dateiname, Default-Export und Props bleiben).
|
||||
* Die fertige Komponente rendert ihren Abschnitt selbst: <Card title="Projekte"> (ein <section>; den Abstand zu den
|
||||
* Nachbarn liefert `.page section + section`).
|
||||
*
|
||||
* Props (V8): konten (Projektkonten), ohneProjekt ({ berechnetCents, bezahltCents, anzahl }), auftraegeAktiv, stripeOk,
|
||||
* onRechnung(projektId) und onAuftrag(projektId); die Kennung ist die des Projekts, `null` die Zeile „Ohne Projekt“.
|
||||
* Abschnitt „Projekte“ im Kundenreiter „Aufträge und Rechnungen“: je Projekt eine Zeile mit Vereinbart, Berechnet
|
||||
* (darunter bezahlt), Noch offen und erfasster Zeit, am Ende die feste Zeile „Ohne Projekt“.
|
||||
* Props: konten (Projektkonten, darf fehlen), ohneProjekt ({ berechnetCents, bezahltCents, anzahl }), auftraegeAktiv,
|
||||
* stripeOk, onRechnung(projektId) und onAuftrag(projektId). Die Kennung ist die des Projekts, `null` die Zeile „Ohne Projekt“.
|
||||
* Die Überschrift „Projekte“ rendert die Komponente selbst; die Anzeigelogik steht in lib/projektkontoView.js.
|
||||
*/
|
||||
export default function ProjektkontoListe({ konten, ohneProjekt, auftraegeAktiv, stripeOk, onRechnung, onAuftrag }) {
|
||||
return null
|
||||
const liste = Array.isArray(konten) ? konten : []
|
||||
const zeilen = [...liste.map((k) => kontoZeile(k, { stripeOk })), ohneProjektZeile(ohneProjekt, { stripeOk })]
|
||||
const hinweis = stripeHinweis(stripeOk)
|
||||
|
||||
return (
|
||||
<Card title="Projekte">
|
||||
<div className="pk">
|
||||
{hinweis && (
|
||||
<p className="msg warn pk-hinweis" role="status">
|
||||
{hinweis}
|
||||
</p>
|
||||
)}
|
||||
{liste.length === 0 && <p className="msg muted pk-hinweis">{KEINE_PROJEKTE_HINWEIS}</p>}
|
||||
<table className="table pk-table" role="table">
|
||||
<caption className="pk-sr">Projektkonten</caption>
|
||||
<thead role="rowgroup">
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">Projekt</th>
|
||||
<th role="columnheader" scope="col">Vereinbart</th>
|
||||
<th role="columnheader" scope="col">Berechnet</th>
|
||||
<th role="columnheader" scope="col">Noch offen</th>
|
||||
<th role="columnheader" scope="col">Zeit erfasst</th>
|
||||
<th role="columnheader" scope="col"><span className="pk-sr">Aktionen</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{zeilen.map((z) => {
|
||||
const id = z.projektId || null
|
||||
const fuer = id === null ? 'ohne Projekt' : `für ${z.name}`
|
||||
return (
|
||||
<tr role="row" key={z.key}>
|
||||
<td role="cell" className="pk-name">
|
||||
<div className="cell-title">{z.name}</div>
|
||||
{z.meta && <div className="cell-sub">{z.meta}</div>}
|
||||
</td>
|
||||
<td role="cell" className="pk-vereinbart" data-label="Vereinbart">{z.vereinbart}</td>
|
||||
<td role="cell" className="pk-berechnet" data-label="Berechnet">
|
||||
<div className="num">{z.berechnet}</div>
|
||||
{z.bezahlt && <div className="cell-sub">{z.bezahlt}</div>}
|
||||
</td>
|
||||
<td role="cell" className="pk-offen" data-label="Noch offen">
|
||||
<span className="num">{z.offen}</span>
|
||||
</td>
|
||||
<td role="cell" className="pk-zeit" data-label="Zeit erfasst">
|
||||
<div className="num">{z.zeit.stunden}</div>
|
||||
{z.zeit.wert && <div className="cell-sub">{z.zeit.wert}</div>}
|
||||
{z.zeit.abo && <div className="cell-sub">{z.zeit.abo}</div>}
|
||||
</td>
|
||||
<td role="cell" className="pk-actions-cell">
|
||||
<div className="pk-actions">
|
||||
{auftraegeAktiv && (
|
||||
<button type="button" className="link" aria-label={`Auftragsbestätigung ${fuer}`} onClick={() => onAuftrag(id)}>
|
||||
Auftragsbestätigung
|
||||
</button>
|
||||
)}
|
||||
<button type="button" className="link" aria-label={`Rechnung ${fuer}`} onClick={() => onRechnung(id)}>
|
||||
Rechnung
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/* Projektliste im Kundenreiter „Aufträge und Rechnungen“. .pk ist der Container der Abfrage unten (in W1 nur hier
|
||||
container-type; der Rechnungsdialog liegt nie darin). Jede Regel trägt .pk und überstimmt so .table td aus global.css. */
|
||||
.pk { container-type: inline-size; }
|
||||
.pk .pk-hinweis { margin: 0 0 8px; }
|
||||
.pk .pk-table th, .pk .pk-table td { vertical-align: top; }
|
||||
.pk .pk-name, .pk .pk-table td .cell-sub { overflow-wrap: anywhere; }
|
||||
.pk .pk-table .num { font-variant-numeric: tabular-nums; }
|
||||
.pk .pk-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px 14px; }
|
||||
.pk .pk-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
||||
@container (max-width: 860px) {
|
||||
.pk .pk-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
||||
.pk .pk-table, .pk .pk-table tbody { display: block; }
|
||||
.pk .pk-table tr {
|
||||
display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 16px; align-items: start;
|
||||
grid-template-areas: "name name" "vereinbart berechnet" "offen zeit" "actions actions";
|
||||
padding: 12px 0; border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.pk .pk-table td, .pk .pk-table td:first-child, .pk .pk-table td:last-child { display: block; padding: 0; border: 0; min-width: 0; }
|
||||
.pk .pk-name { grid-area: name; }
|
||||
.pk .pk-vereinbart { grid-area: vereinbart; }
|
||||
.pk .pk-berechnet { grid-area: berechnet; }
|
||||
.pk .pk-offen { grid-area: offen; }
|
||||
.pk .pk-zeit { grid-area: zeit; }
|
||||
.pk .pk-actions-cell { grid-area: actions; }
|
||||
.pk .pk-actions { justify-content: flex-start; }
|
||||
.pk .pk-table td[data-label]::before { content: attr(data-label); display: block; font-size: 12px; font-weight: 500; color: var(--text-2); }
|
||||
}
|
||||
Reference in new issue
Block a user