Skip to content

Dashboard view + public-repo polish + upstream sync workflow#1

Merged
akim136 merged 2 commits into
mainfrom
sync/dashboard-and-polish
Jun 11, 2026
Merged

Dashboard view + public-repo polish + upstream sync workflow#1
akim136 merged 2 commits into
mainfrom
sync/dashboard-and-polish

Conversation

@akim136

@akim136 akim136 commented Jun 10, 2026

Copy link
Copy Markdown
Owner

First maintenance pass on the public repo.

Dashboard view (first upstream→OSS sync)

A dashboard view type charts a table's numeric/formula fields over a date — ported from upstream as the canonical first sync.

  • lib/dashboard.ts (+ 7 tests), DashboardView + MetricChart (recharts), ViewSwitcher entry, app dispatch, packages/api types + VIEW_TYPES allowlist, recharts dep.
  • A demo Trends dashboard on Deals (Amount + Probability over Close Date) seeded in scripts/seed.mjs.
  • 27 tests pass; apps/web builds clean.

Public-repo polish

  • LICENSE owner filled in; CI workflow (typecheck + test + build); SECURITY.md; issue + PR templates; README badges.

Upstream sync workflow

  • scripts/sync-from-upstream.mjs — opt-in, dry-run-by-default port of the generic shared surface; OSS-specific files denied; a structural scrub gate (id shapes, never literal private values) + an optional gitignored .sync-scrub for exact project strings. Documented in docs/SYNC.md.

🤖 Generated with Claude Code

Dashboard view type (recharts), synced from upstream as the first real
upstream→OSS port:
- lib/dashboard.ts (+test), DashboardView + MetricChart, ViewSwitcher entry,
  app dispatch, packages/api types + VIEW_TYPES allowlist, recharts dep, a demo
  "Trends" dashboard on Deals. 27 tests pass; apps/web builds.

Public-repo polish:
- LICENSE owner filled in; CI workflow (typecheck/test/build); SECURITY.md;
  issue + PR templates; README badges.

Sync workflow:
- scripts/sync-from-upstream.mjs — opt-in, dry-run-by-default port of the generic
  shared surface; OSS-specific files denied; a STRUCTURAL scrub gate (id shapes,
  no literal private values) plus an optional gitignored .sync-scrub for exact
  project strings. docs/SYNC.md documents it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(api): aggregation endpoint + dashboards persistence (composer Part A)

Backend for a workspace-level reports/dashboards composer.

- GET /v1/tables/:id/aggregate — grouped aggregation (count/sum/avg/min/max,
  optional date bucket day/week/month/year, filter). aggregate.ts builds the SQL
  from fixed allowlists (agg fn, bucket format) + registry-validated columns
  (assertIdent) + bound filter values — no user input interpolated. 9 tests.
- meta_dashboards table (0004) — workspace-level (NOT table-bound); dashboards.ts
  CRUD (POST/PATCH/DELETE /v1/dashboards) validates every widget's table/field
  refs against the registry. loadDashboards in the registry; /v1/meta exposes them.
- Verified on local D1 with the demo CRM: sum(amount) by stage + by month(close_date)
  return correct numbers; migration applies; 36 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Composer frontend: /dashboards report builder UI (#3)

* feat(web): reports/dashboards composer UI (composer Part B)

A /dashboards surface to compose a page of widgets from any table.

- Routes: /d (list + create), /d/[dashboardId] (render). Sidebar "Dashboards" nav.
- DashboardRenderer (add/edit/remove/reorder widgets → PATCH config + refresh);
  DashboardWidget (KPI number · line · bar · small table); WidgetBuilder modal
  (type → table → measure/metric → group-by → date bucket → title); BarMini
  (recharts), reuses MetricChart. Per-widget data computed server-side
  (computeWidgets) so the API secret stays off the client; client mutates via BFF.
- A demo "Sales pipeline" dashboard seeded over the CRM (KPIs + by-stage bar +
  amount-by-month line + deals table).
- Review hardening (no high/med findings): aggregate filter now bounded
  (shared assertFilterBounds), widget filter field refs validated.

36 tests pass; apps/web builds; typecheck clean. Stacked on #2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: review findings on the composer (3-branch review pass)

From running code-review + security-review + simplify across the branches:

- seed.mjs: rename the dashboard-block `DEALS` const (it shadowed the demo-rows
  `DEALS` array → SyntaxError that broke `node scripts/seed.mjs` entirely).
- KPI correctness: computeWidgets never groups a KPI (ignores any stale
  groupBy/bucket), so a KPI always shows the grand total, not the first group's
  value; WidgetBuilder also clears groupBy/bucket when switching to KPI/table.
- dashboards validateConfig: fail fast on un-runnable widgets (sum/avg/min/max
  without a metric; line/bar without a group-by) instead of erroring at query
  time; cap widgets per dashboard (MAX_WIDGETS=50) to bound the per-render
  query fan-out.

Security review: no exploitable issues; public-repo leak check clean (only the
owner's public handle appears). 36 tests pass; build + typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: linked-field sort/filter, nested filter groups, kanban field config, branding (#4)

Port of the four grid feature gaps into gridbase (packages/api + apps/web),
mirroring search-system's grid implementation.

A) Sort/filter by a linked record's sub-field
   - types.ts: FilterCondition + sort entries gain optional linkedFieldId; lookups
     traverse their via->target automatically.
   - filter.ts: new resolveLinkTarget/compare/linkedFieldClause; buildWhere emits a
     correlated EXISTS joining the link join-table to the linked table; buildOrderBy
     emits a correlated scalar subquery (LIMIT 1). Both now take the Registry.
   - repo.ts + aggregate.ts: thread reg into buildWhere/buildOrderBy.
   - index.ts parseListOpts: parse "fieldId>linkedFieldId:dir" sort token.
   - apps/web: lib/types.ts mirror; gridApi.ts serializes the linked sort token;
     ViewToolbar filter/sort UI gains a linked sub-field dropdown (default = primary);
     link/lookup fields are no longer excluded from filter/sort dropdowns.

B) Kanban card field config
   - KanbanView: card fields now follow the view FieldEditor (visibleFields) minus the
     stack + primary; removed the hardcoded link exclusion and slice(0,3); added
     view.config.kanban.maxPreviewFields (default 8); link/lookup arrays render via labels.

C) Nested AND/OR filter (Airtable-style, one level)
   - types.ts: FilterSpec.conditions is Array<FilterCondition | FilterGroup> with an
     isFilterGroup guard; old flat specs stay valid.
   - filter.ts buildWhere: groups recurse one level, parenthesized, joined by the
     group conjunction.
   - index.ts assertFilterBounds: cap leaf conditions + group count + depth=1.
   - dashboards.ts validateConfig: flatten one group level to validate leaf fields.
   - ViewToolbar FilterEditor: Add condition / Add group; group conjunction toggle.

D) Branding
   - app/icon.png + public/grid.png (from Downloads/grid.png); layout metadata title/
     description/icon; Sidebar logo (next/image) in expanded + collapsed states; app
     name kept as "gridbase".

Tests: packages/api filter.test.ts extended (19 passing) — nested-group WHERE,
linked-field WHERE EXISTS, linked-field ORDER BY subquery, lookup traversal.
@gridbase/api test (28 passing) + typecheck clean; @gridbase/web typecheck + next
build clean.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@akim136 akim136 merged commit 376812b into main Jun 11, 2026
1 check failed
akim136 added a commit that referenced this pull request Jun 12, 2026
… rollup fields

Mirror of the grid UX + rollup work (built & verified in search-system/apps/grid first):
- #1 Field reorder: drag-and-drop (@dnd-kit/sortable) in FieldEditor.
- #2 Pagination: client TableView appends pages via offset cursor (loadMoreRecords).
- #3 Bulk delete: checkbox column + select-all + bulk Delete; chunked deleteRecords.
- #4 Kanban config: Kanban toolbar panel to set the stack field; columns from
  choices ∪ present values.
- #5 Rollup field type: COUNT/SUM/AVG/MIN/MAX across a link (resolveRollups), read-only;
  IN(...) chunked under D1's 100-param cap. Example rollups added to the demo schema.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
akim136 added a commit that referenced this pull request Jun 12, 2026
…f-service fields, rollups, hardening (#5)

* Port the dashboard view + public-repo polish + upstream sync workflow

Dashboard view type (recharts), synced from upstream as the first real
upstream→OSS port:
- lib/dashboard.ts (+test), DashboardView + MetricChart, ViewSwitcher entry,
  app dispatch, packages/api types + VIEW_TYPES allowlist, recharts dep, a demo
  "Trends" dashboard on Deals. 27 tests pass; apps/web builds.

Public-repo polish:
- LICENSE owner filled in; CI workflow (typecheck/test/build); SECURITY.md;
  issue + PR templates; README badges.

Sync workflow:
- scripts/sync-from-upstream.mjs — opt-in, dry-run-by-default port of the generic
  shared surface; OSS-specific files denied; a STRUCTURAL scrub gate (id shapes,
  no literal private values) plus an optional gitignored .sync-scrub for exact
  project strings. docs/SYNC.md documents it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(api): aggregation endpoint + dashboards persistence (composer Part A)

Backend for a workspace-level reports/dashboards composer.

- GET /v1/tables/:id/aggregate — grouped aggregation (count/sum/avg/min/max,
  optional date bucket day/week/month/year, filter). aggregate.ts builds the SQL
  from fixed allowlists (agg fn, bucket format) + registry-validated columns
  (assertIdent) + bound filter values — no user input interpolated. 9 tests.
- meta_dashboards table (0004) — workspace-level (NOT table-bound); dashboards.ts
  CRUD (POST/PATCH/DELETE /v1/dashboards) validates every widget's table/field
  refs against the registry. loadDashboards in the registry; /v1/meta exposes them.
- Verified on local D1 with the demo CRM: sum(amount) by stage + by month(close_date)
  return correct numbers; migration applies; 36 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(web): reports/dashboards composer UI (composer Part B)

A /dashboards surface to compose a page of widgets from any table.

- Routes: /d (list + create), /d/[dashboardId] (render). Sidebar "Dashboards" nav.
- DashboardRenderer (add/edit/remove/reorder widgets → PATCH config + refresh);
  DashboardWidget (KPI number · line · bar · small table); WidgetBuilder modal
  (type → table → measure/metric → group-by → date bucket → title); BarMini
  (recharts), reuses MetricChart. Per-widget data computed server-side
  (computeWidgets) so the API secret stays off the client; client mutates via BFF.
- A demo "Sales pipeline" dashboard seeded over the CRM (KPIs + by-stage bar +
  amount-by-month line + deals table).
- Review hardening (no high/med findings): aggregate filter now bounded
  (shared assertFilterBounds), widget filter field refs validated.

36 tests pass; apps/web builds; typecheck clean. Stacked on #2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: review findings on the composer (3-branch review pass)

From running code-review + security-review + simplify across the branches:

- seed.mjs: rename the dashboard-block `DEALS` const (it shadowed the demo-rows
  `DEALS` array → SyntaxError that broke `node scripts/seed.mjs` entirely).
- KPI correctness: computeWidgets never groups a KPI (ignores any stale
  groupBy/bucket), so a KPI always shows the grand total, not the first group's
  value; WidgetBuilder also clears groupBy/bucket when switching to KPI/table.
- dashboards validateConfig: fail fast on un-runnable widgets (sum/avg/min/max
  without a metric; line/bar without a group-by) instead of erroring at query
  time; cap widgets per dashboard (MAX_WIDGETS=50) to bound the per-render
  query fan-out.

Security review: no exploitable issues; public-repo leak check clean (only the
owner's public handle appears). 36 tests pass; build + typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: linked-field sort/filter, nested filter groups, kanban field config, branding

Port of the four grid feature gaps into gridbase (packages/api + apps/web),
mirroring search-system's grid implementation.

A) Sort/filter by a linked record's sub-field
   - types.ts: FilterCondition + sort entries gain optional linkedFieldId; lookups
     traverse their via->target automatically.
   - filter.ts: new resolveLinkTarget/compare/linkedFieldClause; buildWhere emits a
     correlated EXISTS joining the link join-table to the linked table; buildOrderBy
     emits a correlated scalar subquery (LIMIT 1). Both now take the Registry.
   - repo.ts + aggregate.ts: thread reg into buildWhere/buildOrderBy.
   - index.ts parseListOpts: parse "fieldId>linkedFieldId:dir" sort token.
   - apps/web: lib/types.ts mirror; gridApi.ts serializes the linked sort token;
     ViewToolbar filter/sort UI gains a linked sub-field dropdown (default = primary);
     link/lookup fields are no longer excluded from filter/sort dropdowns.

B) Kanban card field config
   - KanbanView: card fields now follow the view FieldEditor (visibleFields) minus the
     stack + primary; removed the hardcoded link exclusion and slice(0,3); added
     view.config.kanban.maxPreviewFields (default 8); link/lookup arrays render via labels.

C) Nested AND/OR filter (Airtable-style, one level)
   - types.ts: FilterSpec.conditions is Array<FilterCondition | FilterGroup> with an
     isFilterGroup guard; old flat specs stay valid.
   - filter.ts buildWhere: groups recurse one level, parenthesized, joined by the
     group conjunction.
   - index.ts assertFilterBounds: cap leaf conditions + group count + depth=1.
   - dashboards.ts validateConfig: flatten one group level to validate leaf fields.
   - ViewToolbar FilterEditor: Add condition / Add group; group conjunction toggle.

D) Branding
   - app/icon.png + public/grid.png (from Downloads/grid.png); layout metadata title/
     description/icon; Sidebar logo (next/image) in expanded + collapsed states; app
     name kept as "gridbase".

Tests: packages/api filter.test.ts extended (19 passing) — nested-group WHERE,
linked-field WHERE EXISTS, linked-field ORDER BY subquery, lookup traversal.
@gridbase/api test (28 passing) + typecheck clean; @gridbase/web typecheck + next
build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(grid): field DnD reorder, load-more, bulk delete, kanban config, rollup fields

Mirror of the grid UX + rollup work (built & verified in search-system/apps/grid first):
- #1 Field reorder: drag-and-drop (@dnd-kit/sortable) in FieldEditor.
- #2 Pagination: client TableView appends pages via offset cursor (loadMoreRecords).
- #3 Bulk delete: checkbox column + select-all + bulk Delete; chunked deleteRecords.
- #4 Kanban config: Kanban toolbar panel to set the stack field; columns from
  choices ∪ present values.
- #5 Rollup field type: COUNT/SUM/AVG/MIN/MAX across a link (resolveRollups), read-only;
  IN(...) chunked under D1's 100-param cap. Example rollups added to the demo schema.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(grid): bigger left drag handle + custom Kanban column order

Mirror of the grid handle + Kanban column-order changes:
- Field reorder handle on the left, enlarged, touch-none, scrollbar-clear.
- Kanban config gains a drag-to-reorder "Column order" list (config.kanban.columnOrder);
  KanbanView honors it, Uncategorized last.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(grid): self-service add/delete fields (incl. rollups) from the Fields menu

Mirror of the field self-service feature:
- packages/api: POST/DELETE /v1/tables/:id/fields (fields.ts). Computed fields
  insert a meta row; stored fields ALTER TABLE ADD COLUMN. Delete guards the primary
  field + referenced fields and DROP COLUMNs stored ones. Schema cache invalidated.
- UI: "+ Add field" form (with a rollup builder) + hover-✕ delete in the Fields menu;
  new fields auto-show in the current view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(grid): Calendar date-field config panel (mirrors Kanban setup)

Mirror: a Calendar toolbar panel to pick the date field (config.calendar.dateFieldId),
paralleling the Kanban stack-field picker, replacing the "no date field configured"
dead-end.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: Gallery + Gantt view types with config panels (synced from upstream)

- GalleryView: responsive card grid (primary-field title, Fields-editor card
  fields, optional url-field cover, maxPreviewFields cap) + Gallery toolbar panel.
- GanttView: per-record timeline bars from start/end date fields with month
  ticks + Gantt toolbar panel (start/end pickers).
- Registered in ViewSwitcher, view page, ViewConfig types, VIEW_TYPES allowlist;
  previewValue extracted to lib/preview (shared by Kanban + Gallery).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: review + security findings on dashboards/gallery/gantt (synced from upstream)

- WidgetBuilder materializes the bucket default + clears fieldIds/filter on table
  change; DashboardRenderer disables controls during in-flight saves.
- validateConfig: widgetId required+unique, numeric stored metric for non-count
  aggs, stored group-by for charts, numeric position; PATCH rejects malformed JSON.
- loadDashboards tolerates a missing meta_dashboards table (deploy-before-migrate).
- createField pins rollup references: via on the field's table, target a number
  field on the linked table (a dangling reference 500'd every read of the table).
- GanttView pins zone-less datetimes to UTC; single dated/undated pass; tick left%
  precomputed. Detail views regain overflow-hidden.
- Cleanups: shared meta_dashboards row mapper; VIEW_TYPES/AGG_FNS const single
  sources; ViewToolbar config panels collapsed into a typeEditors map; CountInput
  commit-on-blur; DashboardWidget reuses previewValue.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor: simplify-pass cleanups (synced from upstream)

recordTitle + FieldSelect shared helpers, WidgetResult single declaration,
fieldsForTable in WidgetBuilder/DashboardWidget (+ isComputed group-by filter
that was missing rollup), ViewSwitcher TYPE_INFO registry keyed by ViewType,
views/dashboards PATCH reject malformed JSON consistently.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: column resize, mobile layout fixes, full-set layout views (synced from upstream)

- Drag-to-resize column widths persisted to config.fields[].width
- h-dvh + viewport-fit=cover + safe-area padding; wrapping header/toolbar rows;
  viewport-clamped popovers
- Layout views (kanban/calendar/gallery/gantt) page through the whole record set
  (bounded) instead of showing only the first 100

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: grouped rows, column header menu, kanban quick-add + collapse, mobile sidebar (synced)

- Grouped rows via config.groupBy with collapsible section headers
- Column header ▾ menu: sort asc/desc, group by, hide field
- Kanban per-column quick-add + collapsible columns (config.kanban.collapsedColumns)
- Mobile sidebar: icon-rail default + overlay expansion with backdrop

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: kanban card ordering, keyboard nav + clipboard, summary footer, row colors, side peek (synced)

- Sortable cards within kanban columns (config.kanban.cardOrder), cross-column
  drops slot into position
- Cell cursor: arrows / Enter / Esc / ⌘C / ⌘V on the grid
- Sticky summary footer (config.summaries) with count/sum/avg/min/max
- Row tinting by a select field (config.colorBy) via the header menu
- Record side-peek drawer with inline editing (⌘-click for full page)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: README — full feature overview (views, grid/kanban power features, dashboards, rollups, mobile)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant