fix(ui): pager alignment, one-line billing kinds, Fields explanation

- Table pagers (Modules roster, Client 360 tickets) used an unstyled .wf-pager so the
  summary text and Prev/Next butted together. Add .wf-pager flex styling: summary on the
  left, Prev/page/Next pushed to the right end.
- Module Details: 'Billing kinds' label + checkboxes now on one line (was stacked).
- Module Fields: add a plain-language explainer — what the fields are (with an SMS
  example) and WHERE they show up (each client's Modules tab + Catalog → Module data).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat/client-detail-redesign
Thomas Joise 2 days ago
parent 7f740a7e2e
commit 6939115f6c

@ -149,6 +149,10 @@
.dash-seg button { border: none; background: none; cursor: pointer; font: 600 12.5px var(--font); color: var(--text-dim); padding: 5px 14px; border-radius: 999px; } .dash-seg button { border: none; background: none; cursor: pointer; font: 600 12.5px var(--font); color: var(--text-dim); padding: 5px 14px; border-radius: 999px; }
.dash-seg button.on { background: var(--bg-raised); color: var(--text); box-shadow: var(--shadow-md); } .dash-seg button.on { background: var(--bg-raised); color: var(--text); box-shadow: var(--shadow-md); }
/* Table pager: summary text on the left, Prev / page / Next grouped at the right end. */
.wf-pager { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; font-size: 13px; color: var(--text-dim); }
.wf-pager > span:first-child { margin-right: auto; }
/* Section header shared by the queue block and the bento cards */ /* Section header shared by the queue block and the bento cards */
.dash-head { display: flex; align-items: center; gap: 10px; padding: 2px 0 10px; } .dash-head { display: flex; align-items: center; gap: 10px; padding: 2px 0 10px; }
.dash-head h3 { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-dim); margin: 0; } .dash-head h3 { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-dim); margin: 0; }

@ -386,15 +386,13 @@ function ModuleDetails(props: { module: Module; isOwner: boolean; onSaved: () =>
<Field label="Name"><input className="wf" style={{ width: 260 }} value={name} onChange={(e) => setName(e.target.value)} /></Field> <Field label="Name"><input className="wf" style={{ width: 260 }} value={name} onChange={(e) => setName(e.target.value)} /></Field>
<Field label="SAC"><input className="wf mono" style={{ width: 120 }} value={sac} onChange={(e) => setSac(e.target.value)} /></Field> <Field label="SAC"><input className="wf mono" style={{ width: 120 }} value={sac} onChange={(e) => setSac(e.target.value)} /></Field>
</div> </div>
<div style={{ marginTop: 12 }}> <div style={{ display: 'flex', gap: 14, alignItems: 'center', flexWrap: 'wrap', marginTop: 12 }}>
<div style={{ fontSize: 12, opacity: 0.7, marginBottom: 4 }}>Billing kinds</div> <span style={{ fontSize: 12, opacity: 0.7 }}>Billing kinds:</span>
<div style={{ display: 'flex', gap: 14, flexWrap: 'wrap' }}> {ALL_KINDS.map((k) => (
{ALL_KINDS.map((k) => ( <label key={k} style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}>
<label key={k} style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}> <input type="checkbox" checked={kinds.includes(k)} onChange={() => toggleKind(k)} /> {KIND_LABEL[k]}
<input type="checkbox" checked={kinds.includes(k)} onChange={() => toggleKind(k)} /> {KIND_LABEL[k]} </label>
</label> ))}
))}
</div>
</div> </div>
<div style={{ display: 'flex', gap: 20, marginTop: 12 }}> <div style={{ display: 'flex', gap: 20, marginTop: 12 }}>
<label style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}><input type="checkbox" checked={multi} onChange={(e) => setMulti(e.target.checked)} /> Multi-subscription</label> <label style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}><input type="checkbox" checked={multi} onChange={(e) => setMulti(e.target.checked)} /> Multi-subscription</label>
@ -545,6 +543,15 @@ function FieldSpecEditor(props: { module: Module; isOwner: boolean; onSaved: ()
<> <>
<h3 style={{ marginTop: 20 }}>Fields <span style={{ fontWeight: 400, fontSize: 13, opacity: 0.65 }}> the details you record for each client on this module</span></h3> <h3 style={{ marginTop: 20 }}>Fields <span style={{ fontWeight: 400, fontSize: 13, opacity: 0.65 }}> the details you record for each client on this module</span></h3>
<div className="wf-card" style={{ padding: '10px 14px', marginBottom: 10, fontSize: 13, color: 'var(--text-dim)', lineHeight: 1.5 }}>
<strong style={{ color: 'var(--text)' }}>What are these?</strong> The pieces of information you keep for every client on
the <strong>{props.module.name}</strong> module e.g. for SMS: username, SMS balance, provider.
Define a field once here and it appears everywhere for that module, no developer needed.
<br />
<strong style={{ color: 'var(--text)' }}>Where do they show up?</strong> On each clients page <em>Modules</em> tab
(fill them in per client), and side-by-side for all clients under <em>Catalog Module data</em>.
</div>
{fields.length === 0 ? ( {fields.length === 0 ? (
<EmptyState>No fields yet{props.isOwner ? ' — add the first with the button below.' : '.'}</EmptyState> <EmptyState>No fields yet{props.isOwner ? ' — add the first with the button below.' : '.'}</EmptyState>
) : ( ) : (

Loading…
Cancel
Save