Repository navigation
web: the app's own date field and one date format for the dealer's locale - #156
Merged
Merged
Conversation
A Vitest leg over every .ts source under web/apps/ (tests excluded) that fails on a native date, datetime-local, month or time input, and on toLocaleDateString(, toLocaleTimeString( or Intl.DateTimeFormat( outside lib/dates.ts, naming each file and line. It fails on today's code: the native date inputs are still in the pages and dates are formatted on their own all over the app. Signed-off-by: colton <colton@futurebuild.ai>
…l config (G1 walk note N15) The dealer profile carries locale (a BCP 47 tag the write validates, refused in words otherwise; a write without the field keeps the stored one), added by migration 119 with the en-US default; the demo seed sets en-CA and lifts an already seeded demo profile still on the default, keeping an operator's own tag. GET /api/v1/dealer-profile/locale answers the locale alone to every signed in desk role (the profile read's own guard is unchanged; a branch bound key's class is stated in the allowlist), and the portal's public config and login response carry the same locale for the portal's pages. The contract fragment, the assembled openapi.yaml, the census, the generated TypeScript schema, the portal goldens (the locale line only) and a CONTRACT-CHANGES row naming item 130 and N15 ride along, with wire legs on the module and the composed server and the migration's test. Signed-off-by: colton <colton@futurebuild.ai>
…e N15) lib/dates.ts reads the dealer's locale once at start (the desk route for the desk's pages, the portal's public config for the portal's; en-US on any failure, quietly) and formats every date in one style: the dealer's locale with the month as a short word, never a numeric month first or day first form. formatDay keeps the business day rule (built from its parts, so a west of UTC machine never shows the day before) and moves here from utils.ts, which re-exports it; formatDate and formatDateTime read a moment in the person's own time zone; parseDay reads a typed day in the module's style or YYYY-MM-DD. gable-date-field is the app's own date field: a text box in the module's style with the calendar in the app's own popover (the week starts as the locale starts it; arrows a day or a week, Page Up and Page Down a month, Home and End the week, Enter picks, Escape closes; today marked; min and max honoured), the value always YYYY-MM-DD for the API, change fired on a commit, and an empty field saying Choose a date, never mm/dd/yyyy. The sweep: every native date and datetime-local input in the desk is the field now, with the same values, defaults and change handling (the defaults that built today from UTC take todayDay/addDays, so they hold west of UTC too); every date shown to a person goes through the module, including the spots that showed the raw YYYY-MM-DD (the statement's lines, the aging header, the quote analytics axis, the daily till) and the two date-fns call sites. The guard (dates.guard.test.ts) is green on this tree. Signed-off-by: colton <colton@futurebuild.ai>
The hand-written desk type for the portal's public config gains the locale the wire now answers. Signed-off-by: colton <colton@futurebuild.ai>
PR 148 is in; both sides kept where they meet. Signed-off-by: colton <colton@futurebuild.ai>
A business day sent as a midnight UTC timestamp must format from its day part, and formatDay must never show raw machine text for a value that parses. On today's code all three legs fail: the raw timestamp passes through to the screen, so the journal entries list and the bank reconciliation rows read 2026-10-14T00:00:00Z. Signed-off-by: colton <colton@futurebuild.ai>
…ew P1) A DATE column the server sends as a midnight UTC timestamp (the journal entry's entry_date, the bank transaction's transaction_date, the session's period start and end, the portal's due_date and scheduled_date) is formatted from its day part, and any other value that parses is formatted as a moment's day in the person's own zone: formatDay never puts raw machine text on the screen. Only a value that does not parse at all comes back as sent. The portal's three call sites drop their own slices. Every other formatDay caller was walked against its Go type: all carry plain YYYY-MM-DD strings (invoice, credit memo, payment, statement, reporting, daily till, delivery strings), so nothing else shifts. Signed-off-by: colton <colton@futurebuild.ai>
…ed, PR 156 review P2-1) On today's code PUT /api/v1/dealer-profile with locale en_US (or root, x-whatever, any tag the column's shape check refuses) answers 500 internal_error, and und and qaa are accepted though no person means them. The legs want a 400 naming locale for each. Signed-off-by: colton <colton@futurebuild.ai>
…156 review P2-1) CheckLocale now requires the column's own shape (letters and hyphens, no singleton subtags, no underscore), a successful parse, the canonical form up to case, and refuses the root tag and the local use block qaa through qtz. A tag like en_US is refused in words (400 naming locale) instead of failing the shape check as a 500. Signed-off-by: colton <colton@futurebuild.ai>
…d, PR 156 review P2-2) At Jan 31 with the calendar open and the 31st focused, ArrowRight turns the grid to February; the old cell's node is gone and focus drops to the body, leaving the keyboard person stranded with an open calendar. The leg asserts the Feb 1 button holds the focus; it fails on today's code. Signed-off-by: colton <colton@futurebuild.ai>
…P2-2) The grid tracks whether it holds the keyboard focus (focusin/focusout); when a move turns the month and the old cell's node goes away, the render gives the focus to the day the move landed on. The arrow, Page Up and Page Down and Home and End moves clamp to min and max, so the focus never sits on a disabled day; the min and max leg now pins the clamp. Signed-off-by: colton <colton@futurebuild.ai>
…, toUTCString, date-fns (red, PR 156 review P2-3) The guard also reads unquoted and bound native date inputs now. It fails on today's code for the two inventory tabs' Generated lines, which still show the browser's numeric format. Signed-off-by: colton <colton@futurebuild.ai>
…dropped (PR 156 review P2-3) ProductCollateralTab and ProductContentTab showed the browser's numeric form; both now use formatDateTime. date-fns was unused after the sweep and is removed from the desk's dependencies; the strengthened guard keeps all of it from returning. Signed-off-by: colton <colton@futurebuild.ai>
… PR 156 review P2-4) An activity is an event with a time; the sweep left the form saving every activity at noon. The legs want a time entry (HH:MM, defaulting to now, refused in words) and the payload carrying the picked time; both fail on today's code. Signed-off-by: colton <colton@futurebuild.ai>
The form pairs the day's field with the app's own time entry (HH:MM, the current hour and minute by default, refused in words when it is not a time), and the payload carries the picked day at the picked time in the person's own zone, so the feed shows when the call or meeting happened. Signed-off-by: colton <colton@futurebuild.ai>
…156 review P2-5) The popover is absolute under the field today, clipped by a scrolling or hidden overflow modal and overflowing at a right edge. The legs pin the placement: fixed at the field's box, clamped sideways, flipped upward when the space below is short. They fail on today's code. Signed-off-by: colton <colton@futurebuild.ai>
…6 review P2-5) The popover is placed from the field's own box with fixed positioning, so a modal's scroll or hidden overflow never clips it; placePopover clamps it into the viewport sideways and flips it above the field when the space below is short (clamped to the top margin when neither side fits). A scroll or resize closes it rather than leaving it floating free of the field. Signed-off-by: colton <colton@futurebuild.ai>
…56 review P2-6) On a light card (the Log Activity modal) the field draws white text on white. The leg wants tone light to draw it in the dark-on-light tokens; it fails on today's code. Signed-off-by: colton <colton@futurebuild.ai>
A tone property: the dark tokens by default, tone light on a light card (the Log Activity modal), where it draws dark on the zinc border and ink text. The popover stays the app's own dark card, readable over either. Signed-off-by: colton <colton@futurebuild.ai>
…red, PR 156 review P2-7) Late in the day west of UTC the UTC slice is already tomorrow; the leg wants the balance sheet, the profit and loss, the trial balance and the journal entries' date defaults on the local day. It fails on today's code. Signed-off-by: colton <colton@futurebuild.ai>
…6 review P2-7) Balance Sheet, Profit and Loss, Trial Balance and the journal entry form take todayDay() from the date module: the local day, not the UTC day's tomorrow late in the day west of UTC. Signed-off-by: colton <colton@futurebuild.ai>
…156 r2 P1) A card or dialog with a blur or transform style becomes the reference for the calendar's fixed position, so on the statement, the journal entry card and Create Route it opens away from its field or is clipped. The leg mounts the field inside a transform and overflow hidden ancestor and wants the dialog to carry popover=manual with showPopover called; it fails on today's code (jsdom has no popover API, so the calls are stubbed). Signed-off-by: colton <colton@futurebuild.ai>
The dialog carries popover=manual and is shown with showPopover() while open, so no ancestor's transform, filter, backdrop blur or overflow can offset or clip it (the statement's card, the New Journal Entry card and Create Route were all containing blocks for the fixed position). Manual keeps dismissal with the field's own outside click and Escape handlers; the coordinates still come from the field's box through placePopover. The top layer needs no z-index arms race. K1's PR 155 has not landed yet, so no shared helper exists; placePopover stays in gable-date-field.ts. Signed-off-by: colton <colton@futurebuild.ai>
A planted leg runs the rule against the forms this codebase writes: type=date alone on its own line (the usual multi-line Lit form) matches nothing today, because the rule demands a trailing space, brace or slash. Signed-off-by: colton <colton@futurebuild.ai>
…6 r2 P2) The rule's terminator becomes a lookahead that also accepts the end of the line, so the multi-line form (type=date alone on its line) is caught again; the planted leg covers the line end, the inline, the unquoted and the bound forms and the near misses (text, number, daterange, prototype). Signed-off-by: colton <colton@futurebuild.ai>
…(red, PR 156 r2 P2) Chromium fires focusout (relatedTarget null) on the focused cell when the render removes it, clearing the grid's flag before updated() runs; jsdom fires nothing, so the existing leg cannot see it. The leg replays the sequence by hand and fails on today's code. Signed-off-by: colton <colton@futurebuild.ai>
…2 P2) A refocus flag is set by the grid's key handler and the month buttons (a key or a click there proves the calendar had the interaction) and consumed in updated(), so the focus lands on the moved-to day whatever focus events the engine fires while the old cell's node goes away; a focusout whose target is already disconnected is ignored. The flag clears on close. Signed-off-by: colton <colton@futurebuild.ai>
The comment, the refusal toast and the placeholder said the pattern in letters; they now say it in words or by example (the placeholder shows 09:30), so the pre-push names grep no longer trips on the diff. Signed-off-by: colton <colton@futurebuild.ai>
…eared (red, PR 156 r3) The browser centres every popover (inset 0, margin auto); setting only top and left leaves the calendar centred in the space below and right of its field. The leg wants margin cleared to zero and inset cleared to auto ahead of the placement; it fails on today's code. Signed-off-by: colton <colton@futurebuild.ai>
…56 r3) The user agent stylesheet centres every popover (inset 0, margin auto); writing inset auto and margin 0 ahead of top and left puts the calendar at the field's box, as the app's select list in PR 155 does. Signed-off-by: colton <colton@futurebuild.ai>
Assigning the field's value property fires no change, so the modal kept its default day; the leg passed only while the runner's day equalled the picked day and failed in CI after the day turned. The leg types the day and leaves the box, and runs under a western zone asserting the UTC literal, so the zone handling is what is proven. Signed-off-by: colton <colton@futurebuild.ai>
The light sweep's tokens kept on the pages both sides touch; the date field and module stay on top. Signed-off-by: colton <colton@futurebuild.ai> # Conflicts: # web/apps/desk/src/components/crm/LogActivityModal.ts # web/apps/desk/src/components/logistics/CreateRouteModal.ts # web/apps/desk/src/pages/reports/CustomerStatementPage.ts
The style leg now asserts the unused edges directly: the calendar's inline style writes margin 0 and every edge (the unused two auto), the same guarantee the inset shorthand gave, in a form the test environment can read. The legs that set the runner's zone restore it by deleting the variable when it was unset (assigning back an unset one stored the word undefined, an unknown zone that reads as UTC). And the field's tone switch is removed: the merged Log Activity modal is the desk's card on tokens, so the field's default follows the mode and the light variant is dead. Signed-off-by: colton <colton@futurebuild.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The statement's From and To opened the browser's white system calendar, AR Aging's As of read "mm/dd/yyyy" when empty, and dates showed in three formats across the app (G1 walk note N15). This change gives the app its own date field and one date format, set for the dealer's locale.
What
locale(a BCP 47 tag, migration 119, defaulten-US, the PUT validates the tag in words and a write without the field keeps the stored one; the demo seed setsen-CAand lifts an already seeded demo profile still on the default).GET /api/v1/dealer-profile/localeanswers the locale alone to every signed in desk role (the profile read's own guard is unchanged), and the portal's public config and login response carry the samelocale. The contract fragment, the assembled openapi.yaml, the census, the generated TypeScript schema and a CONTRACT-CHANGES row naming item 130 and N15 ride along; the portal goldens gain only the locale line.lib/dates.tsis the one date module. The locale is read once at start (the desk route for the desk's pages, the portal config for the portal's;en-USon any failure, quietly).formatDaykeeps the business day rule (built from its parts, so a west of UTC machine never shows the day before) and moves here, re-exported fromutils.ts;formatDate,formatDateTimeandformatTimeread a moment in the person's own time zone. One style for every page: the dealer's locale with the month as a short word, never a numeric month first or day first form.<gable-date-field>is the app's own date field: a text box in the module's style that also accepts YYYY-MM-DD, a calendar in the app's own popover (the week starts as the locale starts it; arrows move a day or a week, Page Up and Page Down a month, Home and End the week, Enter picks, Escape closes; today marked; min and max honoured), the value always YYYY-MM-DD for the API,changefired on a commit, and an empty field saying "Choose a date".dates.guard.test.ts, committed red first) fails any new native date input and any own date formatting outside the module, naming each file and line.Tests
en-US,en-CAanden-GBand the west of UTC rule; the field's legs cover typing both forms, the calendar keys, min and max, and empty; the composed-server leg proves every desk role reads the locale and no role is refused; the migration's test covers the default, the shape check, the down and a re-apply.go build,go vet,gofmtclean; the touched packages plus./internal/app/serve/...,./internal/app/seed/...and./internal/characterization/...against a fresh database;make contract-goandmake contract-ts; the web typecheck,npx eslint .,npm testandnpm run buildall clean.Written by Kimi K3 for the Gable lead.