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: '设置',