web(board): group a board by a DATE field — week/month/quarter/year columns (#307) - #332
Merged
Conversation
…olumns (#307) A board can now be a roadmap. Grouping by a date field produces one column per period, and dragging a card into a column moves the record's date. New `date-buckets.ts` holds the three operations together — key, label, and the value a drag writes back — so they can't disagree: dateBucketKey '2026-08-12' → '2026-Q3' bucketLabel '2026-Q3' → 'Q3 2026' bucketStartISO '2026-Q3' → '2026-07-01' The load-bearing property is the round trip: the date a drop writes must re-bucket into the column it was dropped in, or a card would jump columns the moment you released it. That's asserted across all four granularities. Columns are derived from the DATA, not from a min/max range: two records three years apart must not emit 36 empty month columns. All arithmetic is UTC, matching `dateDayKey`'s existing convention, so two people in different timezones never see a card in different columns. Widened together, in this one commit, as the convention requires: canGroupBoardBy + boardGroupDisabledReason (web) and boardGroupError (API) The multi-value rejections are untouched and still tested — a date is single-valued, so the rule this all enforces still holds. `group_by_granularity` joins the view config (month default, so a legacy or half-configured board still renders). Toolbar shows the period picker only when a board is actually grouped by a date. Note: two pre-existing assertions FAILED when date became groupable — "rejects date" and "date says not built yet". That's the tests doing their job; both are updated to the new truth rather than deleted. Verified: 16 new bucket tests + 4 new server tests; web 507 passed / 36 files; API 1828 passed / 187 files (only backup-restore.test.ts fails — needs Docker, documented); lint, typecheck, build green; SDK regenerated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
storyos-docs | 27ab655 | Commit Preview URL Branch Preview URL |
Aug 12 2026, 11:21 PM |
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.
Closes #307 (the roadmap slice of #225).
What it does
Group a board by a date field → one column per period, chronologically. Drag a
card into a column → the record's date moves to that period. That's the
difference between a roadmap and a report; the original ticket calls the read-only
workaround "a poop solution".
The design
date-buckets.tskeeps three operations in one module so they can never disagree:The load-bearing property is the round trip — the value a drop writes must
re-bucket into the column it was dropped into, otherwise a card jumps columns the
instant you release it. Asserted across all four granularities and several dates
including year boundaries.
Two other decisions worth flagging:
must not emit 36 empty month columns. Tested.
dateDayKey's existing convention — otherwisetwo people in different timezones see the same card in different columns.
entirely and makes the key directly convertible back to a date.
2026-01-01(aThursday) correctly buckets to the week starting
2025-12-29.Widened together, in one commit
canGroupBoardBy+boardGroupDisabledReason(web) andboardGroupError(API)— per
docs/architecture/field-surfaces.md, a picker that offers what the rendereror the server refuses is the bug. The multi-value rejections are untouched and
still tested: a date is single-valued, so the rule they enforce still holds.
group_by_granularityjoins the view config, defaulting to month so a legacy orhalf-configured board still renders. The toolbar shows the period picker only when a
board is actually grouped by a date.
Two existing tests failed — on purpose
rejects dateanddate says "not built yet"both broke when date becamegroupable. That's the suite doing its job. Both are updated to the new truth,
not deleted, and the old expectation is noted in a comment so the change is legible.
Verification
backup-restore.test.tsfails — needs Docker, documented in CLAUDE.md).pnpm lint,pnpm typecheck,pnpm buildgreen;pnpm sdk:generatedrift committed.the period picker, drag a card between columns and check the date field updated.
Not included (rest of #225)
Number bins, and text/lookup/formula grouping. The picker already says so in the
user's own words — "number grouping (into bins) is not built yet".
🤖 Generated with Claude Code