diff --git a/examples/app-showcase/src/data/objects/expense-report.object.ts b/examples/app-showcase/src/data/objects/expense-report.object.ts index e958c93273..10eb212be5 100644 --- a/examples/app-showcase/src/data/objects/expense-report.object.ts +++ b/examples/app-showcase/src/data/objects/expense-report.object.ts @@ -164,6 +164,22 @@ export const ExpenseLine = ObjectSchema.create({ { label: 'Rejected', value: 'rejected', color: '#EF4444' }, ], }), + /** + * The exact instant on the receipt — a meal at 19:45, a cab at 07:12. + * + * Deliberately a `datetime` sitting NEXT TO the `date` below: the two are + * the inline grid's temporal-type fixture (objectui#3569). A renderer that + * folds `datetime` onto the `date` control does not merely hide the clock — + * `` emits a bare `YYYY-MM-DD`, so a user correcting the + * DAY writes the time out of the record. Nothing in showcase could catch + * that before, because no inline-grid child carried a datetime at all. + * + * Keep both columns: the pair is what makes the distinction observable. + * With seven editable fields the grid's six-column default budget parks one + * of them in the column chooser (`incurred_on`, by declaration order) — that + * is the intended "personalize columns" behaviour, not a dropped field. + */ + incurred_at: Field.datetime({ label: 'Incurred At' }), incurred_on: Field.date({ label: 'Incurred On' }), }, }); diff --git a/examples/app-showcase/src/data/seed/index.ts b/examples/app-showcase/src/data/seed/index.ts index 91fdca7a5c..0b395113b0 100644 --- a/examples/app-showcase/src/data/seed/index.ts +++ b/examples/app-showcase/src/data/seed/index.ts @@ -388,27 +388,36 @@ const expenseReports = defineSeed(ExpenseReport, { ], }); +/** + * Every line carries BOTH temporal shapes — `incurred_on` (calendar day) and + * `incurred_at` (instant) — and every `incurred_at` has a NON-ZERO time + * component on purpose (objectui#3569). A wall clock of `00:00` would make the + * defect invisible: a renderer that drops the time would look identical to one + * that keeps it. `daysAgo(n)` is a calendar-day function resolving to UTC + * midnight, so the clock is added with the sub-day idiom the formula stdlib + * documents for exactly this — ` + duration("Nh…")`. + */ const expenseLines = defineSeed(ExpenseLine, { mode: 'upsert', externalId: 'merchant', records: [ // EXP-2001 → total 1500.50 · approved 960 · reimbursable 512 · rejected 0 · over$500 2 - { merchant: 'United Airlines', expense_report: 'EXP-2001', category: 'travel', amount: 620, billable: false, status: 'approved', incurred_on: cel`daysAgo(9)` }, - { merchant: 'Marriott Downtown', expense_report: 'EXP-2001', category: 'lodging', amount: 340, billable: false, status: 'approved', incurred_on: cel`daysAgo(8)` }, - { merchant: 'Chipotle', expense_report: 'EXP-2001', category: 'meals', amount: 28.5, billable: false, status: 'submitted', incurred_on: cel`daysAgo(8)` }, - { merchant: 'AWS', expense_report: 'EXP-2001', category: 'software', amount: 512, billable: true, status: 'submitted', incurred_on: cel`daysAgo(7)` }, + { merchant: 'United Airlines', expense_report: 'EXP-2001', category: 'travel', amount: 620, billable: false, status: 'approved', incurred_on: cel`daysAgo(9)`, incurred_at: cel`daysAgo(9) + duration("6h20m")` }, + { merchant: 'Marriott Downtown', expense_report: 'EXP-2001', category: 'lodging', amount: 340, billable: false, status: 'approved', incurred_on: cel`daysAgo(8)`, incurred_at: cel`daysAgo(8) + duration("15h05m")` }, + { merchant: 'Chipotle', expense_report: 'EXP-2001', category: 'meals', amount: 28.5, billable: false, status: 'submitted', incurred_on: cel`daysAgo(8)`, incurred_at: cel`daysAgo(8) + duration("19h45m")` }, + { merchant: 'AWS', expense_report: 'EXP-2001', category: 'software', amount: 512, billable: true, status: 'submitted', incurred_on: cel`daysAgo(7)`, incurred_at: cel`daysAgo(7) + duration("2h10m")` }, // EXP-2002 → total 917 · approved 825 · reimbursable 825 · rejected 1 · over$500 1 - { merchant: 'Delta Air Lines', expense_report: 'EXP-2002', category: 'travel', amount: 780, billable: true, status: 'approved', incurred_on: cel`daysAgo(15)` }, - { merchant: 'Uber', expense_report: 'EXP-2002', category: 'travel', amount: 45, billable: true, status: 'approved', incurred_on: cel`daysAgo(14)` }, - { merchant: 'Staples', expense_report: 'EXP-2002', category: 'supplies', amount: 92, billable: false, status: 'rejected', incurred_on: cel`daysAgo(14)` }, + { merchant: 'Delta Air Lines', expense_report: 'EXP-2002', category: 'travel', amount: 780, billable: true, status: 'approved', incurred_on: cel`daysAgo(15)`, incurred_at: cel`daysAgo(15) + duration("7h35m")` }, + { merchant: 'Uber', expense_report: 'EXP-2002', category: 'travel', amount: 45, billable: true, status: 'approved', incurred_on: cel`daysAgo(14)`, incurred_at: cel`daysAgo(14) + duration("21h50m")` }, + { merchant: 'Staples', expense_report: 'EXP-2002', category: 'supplies', amount: 92, billable: false, status: 'rejected', incurred_on: cel`daysAgo(14)`, incurred_at: cel`daysAgo(14) + duration("11h15m")` }, // EXP-2003 (draft) → total 225.75 · approved 0 · reimbursable 0 · rejected 0 · over$500 0 - { merchant: 'Hilton Garden Inn', expense_report: 'EXP-2003', category: 'lodging', amount: 210, billable: false, status: 'submitted', incurred_on: cel`daysAgo(3)` }, - { merchant: 'Starbucks', expense_report: 'EXP-2003', category: 'meals', amount: 15.75, billable: false, status: 'submitted', incurred_on: cel`daysAgo(2)` }, + { merchant: 'Hilton Garden Inn', expense_report: 'EXP-2003', category: 'lodging', amount: 210, billable: false, status: 'submitted', incurred_on: cel`daysAgo(3)`, incurred_at: cel`daysAgo(3) + duration("14h30m")` }, + { merchant: 'Starbucks', expense_report: 'EXP-2003', category: 'meals', amount: 15.75, billable: false, status: 'submitted', incurred_on: cel`daysAgo(2)`, incurred_at: cel`daysAgo(2) + duration("8h05m")` }, // EXP-DEMO → total 8900 (≥ $5000, trips the committee-quorum threshold) - { merchant: 'Dreamforce Conference', expense_report: 'EXP-DEMO', category: 'other', amount: 3200, billable: true, status: 'submitted', incurred_on: cel`daysAgo(4)` }, - { merchant: 'Lufthansa', expense_report: 'EXP-DEMO', category: 'travel', amount: 2400, billable: true, status: 'submitted', incurred_on: cel`daysAgo(4)` }, - { merchant: 'Grand Hyatt', expense_report: 'EXP-DEMO', category: 'lodging', amount: 1800, billable: true, status: 'submitted', incurred_on: cel`daysAgo(3)` }, - { merchant: 'Apple Store', expense_report: 'EXP-DEMO', category: 'software', amount: 1500, billable: true, status: 'submitted', incurred_on: cel`daysAgo(3)` }, + { merchant: 'Dreamforce Conference', expense_report: 'EXP-DEMO', category: 'other', amount: 3200, billable: true, status: 'submitted', incurred_on: cel`daysAgo(4)`, incurred_at: cel`daysAgo(4) + duration("9h40m")` }, + { merchant: 'Lufthansa', expense_report: 'EXP-DEMO', category: 'travel', amount: 2400, billable: true, status: 'submitted', incurred_on: cel`daysAgo(4)`, incurred_at: cel`daysAgo(4) + duration("5h25m")` }, + { merchant: 'Grand Hyatt', expense_report: 'EXP-DEMO', category: 'lodging', amount: 1800, billable: true, status: 'submitted', incurred_on: cel`daysAgo(3)`, incurred_at: cel`daysAgo(3) + duration("17h55m")` }, + { merchant: 'Apple Store', expense_report: 'EXP-DEMO', category: 'software', amount: 1500, billable: true, status: 'submitted', incurred_on: cel`daysAgo(3)`, incurred_at: cel`daysAgo(3) + duration("13h40m")` }, ], }); diff --git a/examples/app-showcase/src/system/translations/index.ts b/examples/app-showcase/src/system/translations/index.ts index fac1cd9cc8..284ea770c2 100644 --- a/examples/app-showcase/src/system/translations/index.ts +++ b/examples/app-showcase/src/system/translations/index.ts @@ -105,6 +105,22 @@ export const ShowcaseTranslationBundle = { total: { label: 'Total' }, }, }, + // Translated at birth, like `globalActions` below and for the same + // reason: `incurred_at` is a NEW declared label (objectui#3569's inline- + // grid datetime fixture), and check-i18n-coverage freezes this example at + // its current untranslated count — a new label that skips zh-CN widens + // the debt and fails the ratchet. + // + // DELIBERATELY only this one field. The rest of the expense family + // (object labels, `incurred_on`, `merchant`, …) predates the ratchet and + // is part of the frozen baseline; translating it here too would push the + // count BELOW the baseline, which the same gate rejects as an + // un-ratcheted improvement. Paying that debt down is its own change. + showcase_expense_line: { + fields: { + incurred_at: { label: 'Incurred At' }, + }, + }, showcase_preference: { label: 'Setting', pluralLabel: 'Settings', @@ -372,6 +388,13 @@ export const ShowcaseTranslationBundle = { total: { label: '合计' }, }, }, + // See the `en` side for why this entry translates exactly ONE field and + // no more (check-i18n-coverage is a two-sided ratchet). + showcase_expense_line: { + fields: { + incurred_at: { label: '发生时间' }, + }, + }, showcase_preference: { label: '设置', pluralLabel: '设置',