diff --git a/apps/hq-web/src/app.css b/apps/hq-web/src/app.css
index a1f0995..477e20e 100644
--- a/apps/hq-web/src/app.css
+++ b/apps/hq-web/src/app.css
@@ -516,11 +516,15 @@ table.wf tbody tr[role='button']:focus-visible td:first-child { box-shadow: inse
.cdr-node.done .nm { color: var(--text-dim); }
.cdr-node.now .nm { font-weight: 600; }
.cdr-node .dt { font-size: 10px; color: var(--text-dim); font-family: var(--mono); margin-top: 3px; }
-/* linked payment/document reference under a done step's date */
+/* Linked payment/document reference under a done step's date. It WRAPS (up to two short
+ lines) instead of being ellipsised to one 104px line: the reference exists to show the
+ payment's amount / the document's number, and a single mono line at this width cut
+ exactly that off. Wrapping grows the node downwards only, so the track keeps its 112px
+ columns and its horizontal scroll on narrow screens. */
.cdr-node .ref {
font-size: 10px; font-family: var(--mono); color: var(--accent); background: none; border: none;
- padding: 0; margin-top: 3px; cursor: pointer; max-width: 104px; overflow: hidden;
- text-overflow: ellipsis; white-space: nowrap;
+ padding: 0; margin-top: 3px; cursor: pointer; max-width: 104px;
+ white-space: normal; overflow-wrap: anywhere; line-height: 1.3; text-align: center;
}
.cdr-node .ref:hover { text-decoration: underline; }
/* outstanding panel */
diff --git a/apps/hq-web/src/pages/ClientDetail.tsx b/apps/hq-web/src/pages/ClientDetail.tsx
index 1400c96..2bf7e0a 100644
--- a/apps/hq-web/src/pages/ClientDetail.tsx
+++ b/apps/hq-web/src/pages/ClientDetail.tsx
@@ -345,6 +345,7 @@ export function ClientDetail() {
name={moduleName(cm.moduleId)}
refreshToken={milestonesVersion}
payments={ledger.data?.payments ?? []}
+ docs={ledger.data?.documents ?? []}
onTickPayment={(key, label) => { setLinkError(undefined); setPaymentPicker({ clientModuleId: cm.id, key, label }) }}
onTickCreds={(key, label) => { setCredsError(undefined); setCredsDialog({ cm, key, label }) }}
onTickDocument={(key, label) => { setDocLinkError(undefined); setDocPicker({ clientModuleId: cm.id, key, label }) }}
@@ -1427,7 +1428,10 @@ function SecretField(props: { cm: ClientModule; field: FieldDef; managerial: boo
* Un-ticking any of them toggles directly (the server clears the link).
* A done step carrying `paymentId`/`documentId` also renders a small reference under its
* date (`onOpenDocument` / `onOpenPayments`) — otherwise a linked step looks identical to a
- * bare-toggled one.
+ * bare-toggled one. That reference is resolved against the client's ledger (`payments` /
+ * `docs`) so it reads as the real date + amount / document number, wraps to a second line
+ * inside the node rather than being ellipsised, and carries the full detail in its
+ * tooltip + accessible name.
* "+ Add step" appends a project-specific milestone. Every change is one audited POST
* that returns the fresh list; `refreshToken` lets the parent force a refetch after a
* link lands from outside this component. `onChanged` additionally reloads the parent's
@@ -1437,7 +1441,10 @@ function SecretField(props: { cm: ClientModule; field: FieldDef; managerial: boo
*/
function StatusLine(props: {
clientModuleId: string; name: string; refreshToken: number
+ /** The client's ledger — used to render the linked payment / document by its real
+ * date + amount (and to fill the reference's tooltip), not a generic "linked" label. */
payments: Payment[]
+ docs: Doc[]
onTickPayment: (key: string, label: string) => void
onTickCreds: (key: string, label: string) => void
onTickDocument: (key: string, label: string) => void
@@ -1547,20 +1554,33 @@ function StatusLine(props: {
{m.label}{m.done ? (m.doneOn ?? '—') : m.key === currentKey ? 'now' : '—'}
{/* The linked payment/document itself — otherwise a done payment/quotation
- step shows only a date, with no evidence the link actually landed. */}
- {m.done && m.documentId !== null && (
-
- )}
+ step shows only a date, with no evidence the link actually landed. The
+ reference wraps inside the node (it used to be ellipsised to one 104px
+ line, which cut the amount — the one thing it exists to show), and the
+ tooltip/accessible name carries the full detail rather than static text. */}
+ {m.done && m.documentId !== null && (() => {
+ const d = props.docs.find((x) => x.id === m.documentId)
+ const detail = d === undefined
+ ? 'Linked document — open it'
+ : `${docLabel(d)} ${d.docNo ?? '(draft)'} · ${d.docDate} · ${inr(d.payablePaise)} — open it`
+ return (
+
+ )
+ })()}
{m.done && m.paymentId !== null && (() => {
const p = props.payments.find((x) => x.id === m.paymentId)
+ const detail = p === undefined
+ ? 'Linked payment — open Payments & plans'
+ : `Payment ${p.receivedOn} · ${inr(p.amountPaise)}`
+ + `${p.reference !== '' ? ` · ${p.reference}` : ''} — open Payments & plans`
return (