@lotics/ui 22.3.0 → 23.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -2
- package/MIGRATION.md +142 -2
- package/docs/ai_patterns.md +22 -22
- package/docs/catalog.md +119 -98
- package/docs/composition.md +134 -42
- package/docs/data_entry.md +67 -31
- package/docs/templates.md +86 -68
- package/examples/tpl_allocate.tsx +5 -5
- package/examples/tpl_attendance.tsx +2 -2
- package/examples/tpl_calendar.tsx +6 -6
- package/examples/tpl_dashboard.tsx +9 -9
- package/examples/tpl_dieline.tsx +2 -2
- package/examples/tpl_item_list.tsx +64 -37
- package/examples/tpl_lookup.tsx +5 -5
- package/examples/tpl_pick.tsx +6 -6
- package/examples/tpl_pivot.tsx +3 -3
- package/examples/tpl_record.tsx +919 -611
- package/examples/tpl_report.tsx +2 -2
- package/examples/tpl_rollup.tsx +5 -5
- package/examples/tpl_shifts.tsx +5 -5
- package/examples/tpl_statements.tsx +7 -7
- package/examples/tpl_stock.tsx +2 -2
- package/examples/tpl_task_board.tsx +42 -26
- package/examples/tpl_tower.tsx +6 -6
- package/package.json +3 -2
- package/src/agent_run.tsx +4 -4
- package/src/breakdown.tsx +1 -1
- package/src/calendar/calendar_view.tsx +1 -1
- package/src/change_review.tsx +9 -8
- package/src/chip_group.tsx +12 -2
- package/src/choice_list.tsx +2 -2
- package/src/confidence.tsx +2 -2
- package/src/data_grid.tsx +1 -1
- package/src/detail_row.tsx +50 -58
- package/src/file_dropzone.tsx +1 -1
- package/src/file_gallery_modal.tsx +3 -3
- package/src/file_rows.tsx +1 -1
- package/src/finding.tsx +4 -4
- package/src/form_field.tsx +1 -1
- package/src/format_date.ts +2 -2
- package/src/heatmap.tsx +1 -1
- package/src/inline_button.tsx +84 -0
- package/src/inline_date_picker.tsx +17 -10
- package/src/inline_edit.tsx +298 -59
- package/src/inline_member_select.tsx +8 -3
- package/src/inline_number_input.tsx +11 -4
- package/src/inline_select.tsx +26 -13
- package/src/inline_text_input.tsx +12 -4
- package/src/inline_time_picker.tsx +10 -4
- package/src/ledger.tsx +2 -2
- package/src/locale.tsx +7 -3
- package/src/matrix.tsx +1 -1
- package/src/number_input.tsx +18 -7
- package/src/pipeline.tsx +1 -1
- package/src/popover.tsx +1 -1
- package/src/press_door.tsx +1 -1
- package/src/pressable_highlight.tsx +1 -1
- package/src/progress_bar.tsx +3 -3
- package/src/record_summary.tsx +2 -2
- package/src/result_header.tsx +2 -2
- package/src/sequence.tsx +170 -0
- package/src/share_or_download.ts +2 -2
- package/src/step_progress.tsx +7 -5
- package/src/stepper.tsx +1 -1
- package/src/task.tsx +6 -6
- package/src/text_input_field.tsx +19 -1
- package/src/text_utils.ts +1 -1
- package/src/linked_record_box.tsx +0 -157
package/docs/templates.md
CHANGED
|
@@ -92,15 +92,23 @@ Laws for any screen where a record's data is edited and a primary action produce
|
|
|
92
92
|
genuine activity FEED (who changed what, when), not a schedule of payments.
|
|
93
93
|
7. **Header = identity + provenance.** Id, the key identifier, created-at. No metrics or badges
|
|
94
94
|
duplicating data a section already owns.
|
|
95
|
-
8. **
|
|
95
|
+
8. **Action placement — a verb goes where its effect lands.** Value verbs on the value
|
|
96
|
+
(`InlineButton`), row verbs on the row, an ADD below the list it extends (primary, left —
|
|
97
|
+
where the new row will appear, and primary in the `EmptyState` too: one verb, one weight),
|
|
98
|
+
a committing act in its own row at the section's end,
|
|
99
|
+
deletion in the `DangerZone`. Section headings carry identity and view controls only; an Add
|
|
100
|
+
in the heading sits above what it extends and competes with the title. A committing act rides
|
|
101
|
+
an empty-label `DetailRow` so it lands on the CONTROL column — never right-floated, which
|
|
102
|
+
aligns to nothing (only overlay footers right-align).
|
|
103
|
+
9. **Destructive placement.** Solid `danger`, bottom-left after the entity's fields, ONE
|
|
96
104
|
convention page-wide, confirmed by `Alert`. Never in a heading row, never one-tap.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
10. **Create-then-refine.** An add is one click creating a draft edited in place — drafts render
|
|
106
|
+
as empty inline editors with placeholders, never as fake data. No type-a-name-then-click
|
|
107
|
+
forms.
|
|
108
|
+
11. **Optimistic feedback.** Every edit echoes locally, saves in the background, reconciles on
|
|
101
109
|
refetch, reverts + surfaces on error. Silent success, loud failure — no persistent "saved"
|
|
102
110
|
chrome.
|
|
103
|
-
|
|
111
|
+
12. **Chrome discipline.** Kit spacing scale only; ONE divider rule (between sibling blocks,
|
|
104
112
|
never doubled with section hairlines, none trailing); no state-echo labels or filler
|
|
105
113
|
captions — explanations live in `info` popovers and empty states (→ [composition grammar
|
|
106
114
|
§Microcopy](./composition.md)). A "why" question about a control is answered by tracing
|
|
@@ -114,10 +122,10 @@ records behind each number.
|
|
|
114
122
|
### `tpl_dashboard` — the executive overview
|
|
115
123
|
|
|
116
124
|
One screen answering "how is this period going, where is it stuck, who pays the bills".
|
|
117
|
-
Bands: header + period
|
|
118
|
-
the pipeline (`StackedProgressBar`, blue = pipeline)
|
|
125
|
+
Bands: header + period, `KPIStrip`, a revenue chart (`BarChart`, emerald = money) beside
|
|
126
|
+
the pipeline (`StackedProgressBar`, blue = pipeline), a **Needs attention** `Accordion`
|
|
119
127
|
(icon + label + count `Badge`; expanding opens the records behind the count, each row
|
|
120
|
-
carrying its domain `⋯ ActionMenu`)
|
|
128
|
+
carrying its domain `⋯ ActionMenu`), top customers (`Avatar` + revenue + margin badge; the
|
|
121
129
|
name `Peek`s the dossier). Also exercises the wider chart family (`LineChart`, `PieChart`,
|
|
122
130
|
`RingGauge`, `Sparkline`, `TrendFooter`, `Funnel`) and `DateRangeFilterField` — the date
|
|
123
131
|
filter actually filters the KPI datasets. Start here for any "state of the operation"
|
|
@@ -138,8 +146,8 @@ carry meaning, one `ColorName` per state, no hand-picked hex.
|
|
|
138
146
|
|
|
139
147
|
Continuous monitoring of hundreds of live units (machines, gates, sensors). Three bands
|
|
140
148
|
answer the three ops questions in order: `KPIStrip` + `StatusGrid` = "is everything OK right
|
|
141
|
-
now?" (scan for red; the `StatusLegend` drills a state)
|
|
142
|
-
look first?" (severity, then longest unresolved)
|
|
149
|
+
now?" (scan for red; the `StatusLegend` drills a state), an exceptions rail = "where do I
|
|
150
|
+
look first?" (severity, then longest unresolved), a `Heatmap` = "when/where does it
|
|
143
151
|
cluster?" (pressing a cell explains the day). Cells and exception rows open the SAME
|
|
144
152
|
sequenced unit `Drawer` (with a `Timeline` of the unit's events). One semantic color
|
|
145
153
|
reference per state — every weight (grid cell, legend dot, drawer badge) derives from that
|
|
@@ -192,10 +200,12 @@ screens — register, per-row action, gated selection, and act-on-many in one. T
|
|
|
192
200
|
record-workspace anatomy section by section:
|
|
193
201
|
- **Details `DetailTable`** showing every inline field type — text, `InlineMemberSelect`,
|
|
194
202
|
dates incl. optional time, dot-select, money number, an `InlineSelect multi` tag field,
|
|
195
|
-
and `InlineStatic` + a System badge for read-only values.
|
|
196
|
-
where
|
|
203
|
+
and `InlineStatic` + a System badge for read-only values. It is also the worked example of
|
|
204
|
+
where a VERB goes: Phone → Call and Due → Today are both `InlineButton`s in the field's
|
|
205
|
+
`actions` (on the surface, disabled while empty) — the date editor included, since a field
|
|
206
|
+
anchors its overlay to its own box; the System badge, not being a verb, sits beside its value.
|
|
197
207
|
- **Linked records** — the customer's other records as `ListItem`s (title + description at
|
|
198
|
-
the column edge, badge
|
|
208
|
+
the column edge, badge, amount, chevron); pressing one PUSHES an **editable** workspace
|
|
199
209
|
for that record inside the drawer via the hosted `ScreenRouter` — while `canGoBack` the
|
|
200
210
|
drawer header swaps to a back button + the pushed id and the ◀ ▶ sequencer hides; back
|
|
201
211
|
pops with scroll preserved.
|
|
@@ -223,7 +233,7 @@ screens — register, per-row action, gated selection, and act-on-many in one. T
|
|
|
223
233
|
analysis surfaced (described options, one custom-answer slot; its Cancel/Back/Next/Submit
|
|
224
234
|
live in the `DialogFooter` — `ClarifyWizardScope` wraps the Dialog, `ClarifyWizardActions`
|
|
225
235
|
rides the footer) → the IMPORT stream CREATES the records and the dialog ends on the RESULT
|
|
226
|
-
LIST — one hand-rolled result row per record (outcome mark
|
|
236
|
+
LIST — one hand-rolled result row per record (outcome mark, what landed, key figures —
|
|
227
237
|
the anatomy in ai_patterns §the one law; secondary "Done" to dismiss) — the one law's
|
|
228
238
|
creation branch
|
|
229
239
|
(ai_patterns.md): no review gate on a create, the register IS the review, correction uses
|
|
@@ -249,7 +259,7 @@ progress, and a short-pick exception that flags-and-advances without stalling, e
|
|
|
249
259
|
Apply ONE source across MANY targets until the remainder is zero (cash application,
|
|
250
260
|
stock-to-orders, budget distribution). The inverse of a batch builder: batch SUMS parts up to
|
|
251
261
|
a total; allocation SPLITS a fixed total DOWN with a remainder that must hit zero. The
|
|
252
|
-
`RemainderMeter` is the spine — under (left on account)
|
|
262
|
+
`RemainderMeter` is the spine — under (left on account), exact (apply), over (blocked).
|
|
253
263
|
Oldest-first auto-allocates; each `AllocationRow` can be filled or typed.
|
|
254
264
|
|
|
255
265
|
### `tpl_record` — THE record surface
|
|
@@ -260,16 +270,16 @@ left panel as the bare circular `BackButton`). First paint is a `Skeleton` MIRRO
|
|
|
260
270
|
layout, never a spinner. It absorbed the old order-form, inline-record, intake, settings,
|
|
261
271
|
billing, and quick-capture templates. Top → bottom:
|
|
262
272
|
|
|
263
|
-
- A MINIMAL header: `RecordSummary` (title
|
|
264
|
-
carries the desk,
|
|
273
|
+
- A MINIMAL header: `RecordSummary` (title, subtitle ONLY — no stage chip, no metric; Progress
|
|
274
|
+
carries the desk, money lives in Billing), then the record's
|
|
265
275
|
ATTENTION state as a co-located `Callout` (e.g. overdue — record-scoped state reads here;
|
|
266
|
-
field-scoped state stays on its field), then a quiet `SummaryLine` (desk
|
|
267
|
-
|
|
276
|
+
field-scoped state stays on its field), then a quiet `SummaryLine` (desk, documents
|
|
277
|
+
, to collect).
|
|
268
278
|
- **GENERAL — the MAIN details, FIRST in the rail**; every later section is supplementary.
|
|
269
279
|
A `SubsectionStack` (space-only beat) of a headingless LEAD group (the key facts) plus
|
|
270
280
|
named groups (e.g. Order / Fulfilment / System — system identifiers get a HOME, not a
|
|
271
|
-
per-row badge). Every group's `DetailTable` repeats the same `labelWidth
|
|
272
|
-
|
|
281
|
+
per-row badge). Every group's `DetailTable` repeats the same `labelWidth` so the grid
|
|
282
|
+
aligns through the groups. The FIELD-ANNOTATION vocabulary is the worked
|
|
273
283
|
example here: `description` (persistent, under the value, incl. a deliberately LONG one)
|
|
274
284
|
and `error` (derived, e.g. overdue Due) — always explicit, never an ⓘ; the inline editors
|
|
275
285
|
demo their own transient save error (a throwing `onSave`). The Classification group works
|
|
@@ -281,7 +291,10 @@ billing, and quick-capture templates. Top → bottom:
|
|
|
281
291
|
field renders only while its parent value makes it real (no disabled ghost rows).
|
|
282
292
|
Radio/checkbox/search are PERSISTENT controls — the control is the best display;
|
|
283
293
|
prose-shaped values stay inline editors.
|
|
284
|
-
- **Comments
|
|
294
|
+
- **Comments — a docked PANEL, not a section.** It is about the whole record, so it has no place
|
|
295
|
+
in a top-to-bottom read and no rail entry: it pins in the page's RIGHT gutter (the column the
|
|
296
|
+
outline rail's balance already reserved), scrolling inside itself so a long thread never
|
|
297
|
+
lengthens the record, and collapses onto the pinned bar's right on narrow. `CommentList`
|
|
285
298
|
(author-only edit/delete; attachments as a pressable `FileGrid` → gallery preview; a
|
|
286
299
|
file-capable edit form injected via `renderEditForm`) + THE kit `Composer` (attach via
|
|
287
300
|
`actionsButton`, staged files in its `files` slot) — never a bespoke comment box.
|
|
@@ -295,7 +308,7 @@ billing, and quick-capture templates. Top → bottom:
|
|
|
295
308
|
handoff shape for the rules; for a record that genuinely needs a CHECKLIST, the task-list
|
|
296
309
|
grammar is worked in `tpl_item_list`.
|
|
297
310
|
- **Documents** — the Agents "Document desk" pattern (this template is its worked example —
|
|
298
|
-
see the Agents chapter below): the register `Table` (search
|
|
311
|
+
see the Agents chapter below): the register `Table` (search, Add files) whose selection
|
|
299
312
|
feeds the `FloatingActionBar` → ONE "Use AI" fork (extract / cross-check / edit-with-AI).
|
|
300
313
|
The template is the worked example of the THREE-way, WHOLE-SURFACE file intake: the Add-files
|
|
301
314
|
CTA plus a `<FileDropTarget … paste disabled={aiOpen}>` wrapping the ENTIRE section stack (not
|
|
@@ -308,32 +321,43 @@ billing, and quick-capture templates. Top → bottom:
|
|
|
308
321
|
fills it as a real thumbnail, a document centers its badge in it — never a bare `FileBadge`
|
|
309
322
|
(mixed footprints misalign the identity column). The desk holds what ARRIVES; generation
|
|
310
323
|
lives in the Document set OUTPUT section below.
|
|
311
|
-
- **Customer —
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
Empty state = the find-or-create
|
|
319
|
-
its field errors ride `DetailRow error`, not a loose line).
|
|
324
|
+
- **Customer — a REFERENCE, and therefore one of General's field rows**, not a section of its
|
|
325
|
+
own (see [composition.md's reference law](./composition.md)). It wears the inline editor's
|
|
326
|
+
resting surface with an `InlineButton` **Open** inside it; pressing the surface PEEKS the
|
|
327
|
+
facts, and the peek's footer carries detach LEFT / Open RIGHT — so the row itself shows no
|
|
328
|
+
destructive verb at all. The tax-ID problem that gates invoicing is the row's `warning`, not
|
|
329
|
+
a floating `Callout`. Transport's three party rows are the same shape repeated, which is where
|
|
330
|
+
it earns itself: N references as field rows scan as one list, where N cards would be a wall.
|
|
331
|
+
Empty state = the find-or-create `Combobox` in the same value slot, custom row → the create
|
|
332
|
+
`Dialog` (tax ID + Fetch; its field errors ride `DetailRow error`, not a loose line).
|
|
333
|
+
**Reference (the buyer's own) is the `actions` worked example**: its `Copy` is an
|
|
334
|
+
`InlineButton` ON the field, passed unconditionally and `disabled` while empty — not in the
|
|
335
|
+
row's `trailing`, and not conditional, which would resize the field as you type.
|
|
320
336
|
- **Fees — the DETAILED money ledger** (both directions), distinct from Billing's invoice
|
|
321
|
-
documents: a `SummaryLine` (collected
|
|
322
|
-
a `priority`-annotated register `Table` (Fee
|
|
337
|
+
documents: a `SummaryLine` (collected, to collect, to pay, warning-toned when overdue) →
|
|
338
|
+
a `priority`-annotated register `Table` (Fee, Type, Party, Amount, Status — status in
|
|
323
339
|
plain ink, danger only when overdue) → EVERY row opens a right-docked entity `Drawer`
|
|
324
340
|
(◀ ▶ + position stepping) with all fields inline-editable and a confirmed Remove in the
|
|
325
341
|
`DrawerFooter`. "Add fee" is create-then-refine: a blank fee opens straight in the drawer.
|
|
326
|
-
- **Billing
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
342
|
+
- **Billing — a REGISTER, the same shape as Fees.** Both sections are a list of money items
|
|
343
|
+
where each item has detail and a per-item act, so both are a compact `Table` whose row opens
|
|
344
|
+
its FULL detail in a right-docked `Drawer` (◀ ▶ step the invoices) — the drill-down law.
|
|
345
|
+
It was three inline bands stacked, each with its own charge table, callout and action row,
|
|
346
|
+
plus two more bands after them: five subsections and four primaries in one section, which is
|
|
347
|
+
what made the area read as spread. A row now carries title, lines, total, a DERIVED state
|
|
348
|
+
`Badge`, the issued ref; the charge editors (ghost list prices with one-tap "Standard …"
|
|
349
|
+
fill, "How paid…" selects) live in the drawer with the lines they bill, and the section ends
|
|
350
|
+
with ONE summary (`SummaryLine`: to collect, deposit, issued) and ONE action row.
|
|
351
|
+
THE ACTION-GATING LAW is still the worked example: a not-ready Issue is DISABLED and the
|
|
352
|
+
reason is a co-located `Callout` at the gate's SCOPE, once — an invoice's own inconsistency
|
|
353
|
+
(a charged line with no method) in its drawer, a broken record premise (no customer / invalid
|
|
354
|
+
tax ID) at the section top; self-evident empties stay silent, and NEVER prose beside a CTA.
|
|
355
|
+
Issuing gates on record premises only — never silently on stage. Issue sits in the DRAWER's
|
|
356
|
+
footer, which is the one place a CTA right-aligns (an overlay footer; on the page it would
|
|
357
|
+
ride the control column). It is also the worked example of **an irreversible action taking an
|
|
358
|
+
ID, not a captured object** — the confirm re-resolves the invoice from its key, so the quoted
|
|
359
|
+
total is the one that will be billed even when the press was held for a charge cell's write
|
|
360
|
+
(data_entry.md § Inline edit).
|
|
337
361
|
- **Document set — the OUTPUT desk, the last WORK section** (the composition rules' output
|
|
338
362
|
law worked on the record surface: the top is intake, the bottom produces on demand).
|
|
339
363
|
Forms group PER PARTY on the `SubsectionStack` beat — each party a `SubsectionHeading` +
|
|
@@ -374,17 +398,11 @@ billing, and quick-capture templates. Top → bottom:
|
|
|
374
398
|
a label-left capture form (auto-stamped date/receipt-no, record-prefilled fields, the
|
|
375
399
|
right input each) whose CTA and produced `FileRow` ride an empty-label `DetailRow` — the
|
|
376
400
|
form-action alignment law (data_entry.md), worked.
|
|
377
|
-
- **Activity
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
here: the dialog collects the receiver (`MemberSelect`, confirm disabled until chosen) +
|
|
383
|
-
an optional note; confirm writes the trail mark and the SIBLING record, shown as the same
|
|
384
|
-
linked-record box (its drawer holds the reference info + Open record + **Recall**). After
|
|
385
|
-
the handoff there is NO further CTA — the act to leave the NEW desk rides that desk's stage
|
|
386
|
-
in Progress, and this section shows only what the handoff produced (with an empty state when
|
|
387
|
-
it has produced nothing yet).
|
|
401
|
+
- **No Activity or Handoff section.** Progress owns both: it says where the record SITS and
|
|
402
|
+
carries the act that moves it, so a second section repeating the handoff — and a `Timeline` of
|
|
403
|
+
every change beside it — were two more places to look for one answer. What that costs is
|
|
404
|
+
explicit: the sibling record a handoff creates has no home on this surface, and there is no
|
|
405
|
+
audit trail. Both belong on the Progress stage that produced them if an app needs them back.
|
|
388
406
|
|
|
389
407
|
Recall is the undo: confirmed, it withdraws the sibling, clears the custody stamp that
|
|
390
408
|
handoff wrote, and returns the record to the desk BEFORE it — one step, never a jump to the
|
|
@@ -437,8 +455,8 @@ divorced from the state that gated it. A stage says the position outright, and t
|
|
|
437
455
|
stage it belongs to. Reach for a checklist only when the rows are genuinely N independent
|
|
438
456
|
tickable items (`TaskList`); a record's own progress is a pipeline.
|
|
439
457
|
|
|
440
|
-
Built on the `Pipeline` compound (`Pipeline
|
|
441
|
-
`PipelineNote
|
|
458
|
+
Built on the `Pipeline` compound (`Pipeline`, `PipelineStage`, `PipelineField`,
|
|
459
|
+
`PipelineNote`, `PipelineActions`), which composes over `Stepper` — `Stepper` renders POSITION,
|
|
442
460
|
this adds the anatomy that turns a position into a workspace.
|
|
443
461
|
|
|
444
462
|
Three rules the anatomy holds, each learned from getting it wrong:
|
|
@@ -466,9 +484,9 @@ and tickable in any order are a `tpl_task_board`, not a pipeline.
|
|
|
466
484
|
|
|
467
485
|
### `tpl_task_board` — the columns shape
|
|
468
486
|
|
|
469
|
-
The manager's board: a search
|
|
470
|
-
LIVE editable cells — assignee/due/status/tags ALL via `Inline*` editors in **`variant="
|
|
471
|
-
(`InlineMemberSelect
|
|
487
|
+
The manager's board: a search, group-by, filter toolbar over a grouped, sortable grid of
|
|
488
|
+
LIVE editable cells — assignee/due/status/tags ALL via `Inline*` editors in **`variant="bare"`**
|
|
489
|
+
(`InlineMemberSelect`, `InlineDatePicker`, `InlineSelect`, and tags an `InlineSelect multi
|
|
472
490
|
allowCustom` — create-a-tag included), set directly in the cell. Every editable cell + the custom
|
|
473
491
|
Files/Action cells share ONE hover language (the `zinc-100` wash); no cell reveals a border. It builds on
|
|
474
492
|
the `DataGrid` primitive (the inline-managed grouped-table shape) and **owns only its data,
|
|
@@ -483,8 +501,8 @@ grouped sections, and the aligned rows. It demonstrates:
|
|
|
483
501
|
match the inline editors.
|
|
484
502
|
- A trailing `ActionMenu` (the `⋯` overflow — a `danger` Remove behind a confirm `Alert`).
|
|
485
503
|
- CUSTOM pressable cells (files, the action link) hover with the **background-tint WASH**
|
|
486
|
-
(`zinc-100`) — the
|
|
487
|
-
|
|
504
|
+
(`zinc-100`) — the ROW's own hover language, distinct from the field frame a `bare` editor
|
|
505
|
+
fades in on its own hover. (The row washes; the field's edge draws — the two
|
|
488
506
|
hover languages, one per surface.)
|
|
489
507
|
- Per-group add rows that pre-set the group's field (align with the exported `gridRowStyle`),
|
|
490
508
|
and a Draft-from-notes CTA.
|
|
@@ -532,8 +550,8 @@ week so the copied screen is evergreen.
|
|
|
532
550
|
|
|
533
551
|
### `tpl_attendance` — the attendance desk
|
|
534
552
|
|
|
535
|
-
Who is in, who is late, who is out, plus the weekly per-person grid. Header + month `Picker
|
|
536
|
-
|
|
553
|
+
Who is in, who is late, who is out, plus the weekly per-person grid. Header + month `Picker`,
|
|
554
|
+
`KPIStrip` (the four counts), `Tabs` Today / This week. "Today" = one banded roster
|
|
537
555
|
`Card` — each row press-opens the person's workspace `Drawer` (◀ ▶ sequencing) and carries a
|
|
538
556
|
`⋯ ActionMenu` with the day's quick fixes. "This week" = per-person `Accordion` rows: day
|
|
539
557
|
markers expanding to the day-by-day detail in place. Presence is one accent; badge colors
|
|
@@ -587,8 +605,8 @@ phase runs inside a `FollowScroll` (swapped back to `DialogScrollArea` on the ph
|
|
|
587
605
|
so review opens at the top):
|
|
588
606
|
|
|
589
607
|
- **Extract** (read the documents, fill the record) — the open `ChangeFields` review: ONE
|
|
590
|
-
`Change` section per record whose body stacks a `ChangeField` per proposed value (label
|
|
591
|
-
the − band when replacing
|
|
608
|
+
`Change` section per record whose body stacks a `ChangeField` per proposed value (label,
|
|
609
|
+
the − band when replacing, the editable + value). **Editing IS the review**; an add has no
|
|
592
610
|
before, a removal is the − band alone, and a conflict shows the read-only outcome band over
|
|
593
611
|
candidate rows + a type-another-value third option. Proposed line items arrive as
|
|
594
612
|
`ChangeRecord` cards; Keep-all + one outcome-named Apply commit.
|
|
@@ -11,13 +11,13 @@ import { RemainderMeter } from "@lotics/ui/remainder_meter";
|
|
|
11
11
|
import { formatMoney } from "@lotics/ui/format_money";
|
|
12
12
|
|
|
13
13
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
-
// Template
|
|
14
|
+
// Template, Allocation / split — apply ONE source across MANY targets until the
|
|
15
15
|
// remainder is zero. Here: cash application — a received payment spread across a
|
|
16
16
|
// customer's open invoices (the same shape serves stock-to-orders, landed-cost,
|
|
17
17
|
// budget distribution). The inverse of the batch builder: batch SUMS parts up to
|
|
18
18
|
// a total; allocation SPLITS a fixed total DOWN, with a remainder that must hit
|
|
19
|
-
// zero. The RemainderMeter is the spine — under (left on account)
|
|
20
|
-
//
|
|
19
|
+
// zero. The RemainderMeter is the spine — under (left on account), exact (apply),
|
|
20
|
+
// over (blocked). Oldest-first auto-allocates; each row can be filled or typed.
|
|
21
21
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
22
|
|
|
23
23
|
interface Invoice {
|
|
@@ -77,7 +77,7 @@ export function TplAllocate() {
|
|
|
77
77
|
<View style={{ gap: 2, flexGrow: 1, flexBasis: 220 }}>
|
|
78
78
|
<Text size="xs" color="muted" weight="medium">Payment received</Text>
|
|
79
79
|
<Text size="xxxl" weight="semibold" tabular>{formatMoney(PAYMENT.amount)}</Text>
|
|
80
|
-
<Text size="sm" color="muted">{`${PAYMENT.from}
|
|
80
|
+
<Text size="sm" color="muted">{`${PAYMENT.from}, ref ${PAYMENT.ref}, ${PAYMENT.date}`}</Text>
|
|
81
81
|
</View>
|
|
82
82
|
<View style={{ gap: 10, flexGrow: 1, flexBasis: 300 }}>
|
|
83
83
|
<RemainderMeter total={PAYMENT.amount} allocated={allocated} format={formatMoney} />
|
|
@@ -92,7 +92,7 @@ export function TplAllocate() {
|
|
|
92
92
|
{/* the targets — the open invoices, each absorbing part of the payment */}
|
|
93
93
|
<View>
|
|
94
94
|
<View style={{ paddingHorizontal: 20, paddingVertical: 12 }}>
|
|
95
|
-
<Text size="xs" color="muted" weight="medium">{`Open invoices
|
|
95
|
+
<Text size="xs" color="muted" weight="medium">{`Open invoices (${INVOICES.length})`}</Text>
|
|
96
96
|
</View>
|
|
97
97
|
<Divider />
|
|
98
98
|
{INVOICES.map((inv, i) => (
|
|
@@ -17,11 +17,11 @@ import { Picker } from "@lotics/ui/picker";
|
|
|
17
17
|
import { Tabs } from "@lotics/ui/tabs";
|
|
18
18
|
|
|
19
19
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
-
// Template
|
|
20
|
+
// Template, Attendance. The daily attendance desk: who is in, who
|
|
21
21
|
// is late, who is out, plus the weekly per-person grid. Emerald = presence;
|
|
22
22
|
// badge colors carry the status meaning (amber late / red absent / blue leave).
|
|
23
23
|
//
|
|
24
|
-
// Grammar: zinc-50 canvas
|
|
24
|
+
// Grammar: zinc-50 canvas, header + month Picker, KPIStrip (four counts),
|
|
25
25
|
// Tabs Today / This week. "Today" = one banded roster Card — each row
|
|
26
26
|
// press-opens the employee workspace Drawer (◀ ▶ sequencing) and carries a
|
|
27
27
|
// ⋯ ActionMenu with the day's quick fixes. "This week" = per-person Accordion
|
|
@@ -19,12 +19,12 @@ import {
|
|
|
19
19
|
} from "@lotics/ui/calendar";
|
|
20
20
|
|
|
21
21
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
-
// Template
|
|
22
|
+
// Template, Calendar — the delivery/schedule desk. One real
|
|
23
23
|
// CalendarView (week grid) over the current week's deliveries / reconciliations /
|
|
24
24
|
// customer visits, then a "Today" agenda card listing today's three slots.
|
|
25
25
|
//
|
|
26
|
-
// Grammar: zinc-50 canvas
|
|
27
|
-
// calendar card at a real height
|
|
26
|
+
// Grammar: zinc-50 canvas, header band (title + week label + Today),
|
|
27
|
+
// calendar card at a real height, agenda card of pressable rows. Every slot
|
|
28
28
|
// is a door: press (grid event or agenda row) opens the sequenced workspace
|
|
29
29
|
// Drawer; agenda rows also carry the ⋯ quick-actions menu. Event colors carry
|
|
30
30
|
// meaning: blue = delivery, emerald = reconciliation, amber = customer.
|
|
@@ -141,8 +141,8 @@ const SLOT_ACTIONS = [
|
|
|
141
141
|
// Compact slot workspace — identity + key facts + the two real actions.
|
|
142
142
|
function SlotWorkspace({ e }: { e: CalendarEvent<AgendaMeta> }) {
|
|
143
143
|
const thoiGian = e.allDay
|
|
144
|
-
? `${fmtDate(e.start)} – ${e.end ? fmtDate(e.end) : ""}
|
|
145
|
-
: `${fmtDate(e.start)}
|
|
144
|
+
? `${fmtDate(e.start)} – ${e.end ? fmtDate(e.end) : ""} (all day)`
|
|
145
|
+
: `${fmtDate(e.start)}, ${fmtTime(e.start)} – ${e.end ? fmtTime(e.end) : ""}`;
|
|
146
146
|
const rows: [string, React.ReactNode][] = [
|
|
147
147
|
["Time", <Text key="tg" size="sm" tabular>{thoiGian}</Text>],
|
|
148
148
|
...(e.data
|
|
@@ -236,7 +236,7 @@ export function TplCalendar() {
|
|
|
236
236
|
<Card style={{ padding: 0 }}>
|
|
237
237
|
<CardHeader>
|
|
238
238
|
<CardHeaderTitle>Today</CardHeaderTitle>
|
|
239
|
-
<CardHeaderMeta>{`${todayLabel}
|
|
239
|
+
<CardHeaderMeta>{`${todayLabel} (${todayEvents.length} slots)`}</CardHeaderMeta>
|
|
240
240
|
</CardHeader>
|
|
241
241
|
<View style={{ paddingHorizontal: 16, paddingVertical: 6 }}>
|
|
242
242
|
{todayEvents.map((e) => (
|
|
@@ -36,15 +36,15 @@ import { Funnel } from "@lotics/ui/funnel";
|
|
|
36
36
|
import { formatMoney } from "@lotics/ui/format_money";
|
|
37
37
|
|
|
38
38
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
-
// Template
|
|
39
|
+
// Template, Dashboard — the executive operations dashboard. One
|
|
40
40
|
// screen that answers "how is this month going, where is it stuck, who pays
|
|
41
41
|
// the bills".
|
|
42
42
|
//
|
|
43
|
-
// Bands: header + period badge
|
|
43
|
+
// Bands: header + period badge, KPI strip (KPIStrip), revenue by
|
|
44
44
|
// month (BarChart, emerald = money) next to the sales pipeline
|
|
45
|
-
// (StackedProgressBar, blue = pipeline)
|
|
45
|
+
// (StackedProgressBar, blue = pipeline), Needs attention (Accordion rows —
|
|
46
46
|
// icon + label + count Badge; expanding opens the records behind the
|
|
47
|
-
// count, each record carries its domain ⋯ ActionMenu)
|
|
47
|
+
// count, each record carries its domain ⋯ ActionMenu), Top customers
|
|
48
48
|
// (Avatar + revenue + margin badge; the name Peeks the dossier).
|
|
49
49
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
50
50
|
|
|
@@ -78,19 +78,19 @@ const KY_DU_LIEU: Record<
|
|
|
78
78
|
doanhThu: 986_000_000, doanhThuTrend: -6, doanhThuCaption: "Mar: ₫1.05B",
|
|
79
79
|
bienLai: 16.8, bienLaiTrend: -1, bienLaiCaption: "Mar: 17.4%",
|
|
80
80
|
donDangXuLy: 31, donCaption: "2 orders past delivery date",
|
|
81
|
-
congNo: 512_000_000, congNoCaption: "8 customers overdue
|
|
81
|
+
congNo: 512_000_000, congNoCaption: "8 customers overdue, up 4%",
|
|
82
82
|
},
|
|
83
83
|
"2026-05": {
|
|
84
84
|
doanhThu: 1_152_000_000, doanhThuTrend: 17, doanhThuCaption: "Apr: ₫986M",
|
|
85
85
|
bienLai: 17.1, bienLaiTrend: 2, bienLaiCaption: "Apr: 16.8%",
|
|
86
86
|
donDangXuLy: 34, donCaption: "3 orders past delivery date",
|
|
87
|
-
congNo: 449_000_000, congNoCaption: "5 customers overdue
|
|
87
|
+
congNo: 449_000_000, congNoCaption: "5 customers overdue, down 12%",
|
|
88
88
|
},
|
|
89
89
|
"2026-06": {
|
|
90
90
|
doanhThu: 1_284_000_000, doanhThuTrend: 11, doanhThuCaption: "May: ₫1.15B",
|
|
91
91
|
bienLai: 18.4, bienLaiTrend: 1, bienLaiCaption: "May: 17.1%",
|
|
92
92
|
donDangXuLy: 37, donCaption: "4 orders past delivery date",
|
|
93
|
-
congNo: 486_000_000, congNoCaption: "6 customers overdue
|
|
93
|
+
congNo: 486_000_000, congNoCaption: "6 customers overdue, up 8%",
|
|
94
94
|
},
|
|
95
95
|
};
|
|
96
96
|
const KY_CUOI = "2026-06";
|
|
@@ -329,7 +329,7 @@ export function TplDashboard() {
|
|
|
329
329
|
]}
|
|
330
330
|
/>
|
|
331
331
|
|
|
332
|
-
{/* chart row 1: revenue by month (BarChart + TrendFooter)
|
|
332
|
+
{/* chart row 1: revenue by month (BarChart + TrendFooter), margin trend (LineChart) */}
|
|
333
333
|
<View style={{ flexDirection: "row", gap: 16, alignItems: "stretch" }}>
|
|
334
334
|
<Card style={{ padding: 0, flex: 3 }}>
|
|
335
335
|
<CardHeader>
|
|
@@ -364,7 +364,7 @@ export function TplDashboard() {
|
|
|
364
364
|
</Card>
|
|
365
365
|
</View>
|
|
366
366
|
|
|
367
|
-
{/* chart row 2: pipeline funnel
|
|
367
|
+
{/* chart row 2: pipeline funnel, revenue mix, on-time delivery */}
|
|
368
368
|
<View style={{ flexDirection: "row", gap: 16, alignItems: "stretch" }}>
|
|
369
369
|
<Card style={{ padding: 0, flex: 3 }}>
|
|
370
370
|
<CardHeader>
|
package/examples/tpl_dieline.tsx
CHANGED
|
@@ -24,7 +24,7 @@ const MOCK_PHOTO: DisplayFile = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
-
// Template
|
|
27
|
+
// Template, AI design canvas (the carton "dieline" app) — the FLAGSHIP shape:
|
|
28
28
|
// the page IS the design. The dieline fills the centre, a FLOATING composer sits
|
|
29
29
|
// at the bottom, and a pinned PARAMS PANEL (a hand-composed label/value field-card
|
|
30
30
|
// in live-edit mode) floats at the centre-right. Empty → drop a photo in the
|
|
@@ -248,7 +248,7 @@ function ParamsPanel({ dims, onEditParam, onDownload }: { dims: Dims; onEditPara
|
|
|
248
248
|
<View style={panel.header}>
|
|
249
249
|
<View style={{ flex: 1, gap: 1 }}>
|
|
250
250
|
<Text size="sm" weight="semibold">Parameters</Text>
|
|
251
|
-
{open ? <Text size="xs" color="muted">FEFCO 0201
|
|
251
|
+
{open ? <Text size="xs" color="muted">FEFCO 0201, B-flute</Text> : null}
|
|
252
252
|
</View>
|
|
253
253
|
<IconButton icon={open ? "minus" : "plus"} accessibilityLabel={open ? "Minimize parameters" : "Expand parameters"} onPress={() => setOpen((o) => !o)} />
|
|
254
254
|
</View>
|