WebUI: table+form rework, system-metadata timestamps, sortable Created/Modified#336
Conversation
Iterates on top of #329's CloudKit JS mode toggle. The single-mode JSON- textarea CRUD grid is replaced by a Notes table beside a Title/Index form: clicking a row loads it for edit, per-row Delete buttons, "New" to clear. Auto-refreshes after every mutation and after mode switches, so the same notes can be observed fetched through either backend. WebUI: - Two-column responsive layout: Notes table left, edit/create form right; stacks to one column below 820px. - Created and Modified columns formatted with the locale's dateStyle:short/timeStyle:short (e.g., "5/12/26, 4:30 PM"); full ISO is in each cell's tooltip. - Clickable Created/Modified column headers cycle unsorted → ascending → descending. Sort forwards to both backends: MistKit body `sortBy:[{field, ascending}]`, CloudKit JS `sortBy:[{fieldName, ascending}]`. Default is no sort, so the demo still lists records before the new schema deploys. - Record name is removed as a column and surfaced as a row tooltip. Note schema: - Drop custom `createdAt` (TIMESTAMP) and `modified` (INT64) — they duplicated CloudKit's system metadata. CKRecord.creationDate / .modificationDate and the Web Services `created.timestamp` / `modified.timestamp` cover the same information without manual bookkeeping. Schema, native Note model, RecordDetailView, QueryView, NativeCloudKitService, integration phases, README, and the CLI query examples are updated. - Add `___createTime` and `___modTime` to the schema with QUERYABLE SORTABLE so the sort feature actually works against the live container (system fields default to non-sortable; the schema must explicitly opt them in). Server: - New WebJSON.encoder()/.decoder() with .millisecondsSince1970 date strategy. The browser receives created/modified timestamps as plain epoch-millis numbers, matching CloudKit JS's Date shape. - WebRequests.Update + .Delete grow optional `recordChangeTag` — the browser holds it from the last query, so MistKit-mode update/delete no longer need a server-side fetch round-trip. Fixes CloudKit's `BadRequestException: missing required field 'recordChangeTag'`. - WebRequests.Create + .Update.fields are `[String: FieldValue]` decoded through MistKit's FieldValue Codable (which accepts raw JSON primitives — string/int/double). Fixes the 400 thrown when the form sent `"index": 5` (a JSON number) against the prior `[String: String]` type. - WebRequests.Query gains `sortBy: [QuerySortField]?`; WebBackend takes the request-shape sort directly (no MistKit-internal type leakage). CloudKitService extension is the only site that knows about MistKit's QuerySort. WebCommand: - Catch AsyncTimeoutError.cancelled so Ctrl+C is a normal shutdown rather than a top-level fatal error. Tests: - WebServerTests+QuerySort — sort forwarding + nil default. - Updated CRUD tests cover recordChangeTag forwarding (update + delete), mixed-type fields (int + double in create), and absent recordChangeTag tolerance on update. - MockBackend.QueryCall.sortBy captures the request-shape sort; flatten() handles int64/double for assertion. The new SORTABLE system fields in schema.ckdb need to be deployed to the live CloudKit container before sort works end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 330-interactive-mistdemo #336 +/- ##
============================================================
+ Coverage 70.27% 70.30% +0.03%
============================================================
Files 545 547 +2
Lines 15003 15102 +99
============================================================
+ Hits 10543 10618 +75
- Misses 4460 4484 +24
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Code Review — PR #336: WebUI table+form rework, system-metadata timestamps, sortable Created/ModifiedOverviewThis PR is a well-scoped, purposeful rework of the MistDemo web UI that simultaneously:
The change set is coherent and the PR description is thorough. Most concerns below are minor. ✅ StrengthsSchema cleanup is correct. Dropping
XSS hygiene is solid. Test coverage matches the feature scope. New tests for sort forwarding, numeric fields in create, Issues & Suggestions1.
|
- index.html: deleteNote(note, statusEl = tableStatusEl) so per-row
Delete feedback lands above the table; the form panel's Delete
passes formStatusEl explicitly. Removes a dead recordType fallback
reachable only if normalizeRecords() dropped recordType — which it
doesn't. Adds a comment that per-row Delete intentionally skips
confirm(); the raw response panel makes accidents visible.
- WebJSON.swift: removes unused decoder() (FieldValue's own Codable
handles request-side dates; Hummingbird's request.decode runs the
framework decoder). Encoder remains as the singular response-side
contract; docstring updated accordingly.
- WebRequests.swift: doc comment on QuerySortField.field flagging
that CloudKit JS calls the same concept `fieldName` and the browser
maps between them.
- MockBackend.swift: comment on flatten()'s default case explaining
the intentional drop of asset/date/reference/location/list/bytes —
tests needing those should inspect the FieldValue directly.
- WebJSONTests.swift (new): locks the encoder's epoch-millis contract
with a round-trip test. The browser's `toDate(value)` in index.html
depends on receiving plain millis numbers.
Skipped from the review with reasoning preserved in the plan file
(/Users/leo/.claude/plans/async-wibbling-pearl.md):
- server-side recordChangeTag guard (CloudKit's 400 is already
clear, browser is the only realistic caller)
- generic CancellationError catch in WebCommand (the explicit
AsyncTimeoutError.cancelled is more meaningful)
- sort response with timestamps test (encoder round-trip covers
the same contract more directly)
- schema.ckdb trailing blank line (was already present pre-PR)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review — PR #336: WebUI table+form rework, system-metadata timestamps, sortable Created/ModifiedOverviewSolid iterative improvement on top of #335. The switch from custom Bug Fixes — Well Done
Code Quality
Schema
One pre-existing pattern: TestsGood additions:
Missing coverage worth considering (not blockers for this PR):
Minor Points
SummaryThe changes are architecturally sound, the bug fixes are complete, and the new test surface is appropriate. Two items worth addressing before merge:
Everything else is clean. |
Summary
Iterates on top of #329's CloudKit JS mode toggle (PR #335). The single-mode JSON-textarea CRUD grid is replaced by a Notes table beside a Title/Index form; clicking a row loads it for edit, per-row Delete, "New" to clear. Auto-refreshes after every mutation and on mode switch.
dateStyle:short/timeStyle:short; ISO in cell tooltip. Created/Modified column headers are clickable: unsorted → ↑ → ↓ → unsorted. Record name is now a row tooltip (no longer a column).createdAt(TIMESTAMP) andmodified(INT64) fields; the demo now reads CloudKit system metadata (CKRecord.creationDate/.modificationDate, Web Servicescreated.timestamp/modified.timestamp). Native model, views, integration phases, README, and CLI examples follow.___createTimeand___modTimewithQUERYABLE SORTABLE. System fields default to non-sortable; they must be opted in for the new sort feature to work end-to-end.WebJSON.encoder()/.decoder()with.millisecondsSince1970date strategy so the browser receives epoch millis.WebRequests.QuerygainssortBy.WebRequests.Update/.Deletegain optionalrecordChangeTag.WebRequests.Create/.Update.fieldsare now[String: FieldValue]decoded through MistKit's existing custom Codable.BadRequestException: missing recordChangeTagon delete/update (now threaded through from the loaded row);HTTP 400on create when sending"index": 5as a JSON number against the prior[String: String]decoder;Error raised at top levelcrash on Ctrl+C (WebCommand now catchesAsyncTimeoutError.cancelledas a clean shutdown).Schema deployment
The new
___createTime/___modTimeSORTABLE entries and the droppedcreatedAt/modifiedfields need to be deployed to the live CloudKit container manually (cktoolor the dashboard) before sort works end-to-end. Default sort isnil, so unsorted listings work against either schema version.Test plan
swift testfromExamples/MistDemo— 924 tests pass.Scripts/lint.sh— exits successfully (swift-format + swiftlint + build + header check all green; trailing periphery warnings are pre-existing on the MistKit core).recordChangeTagerror.BadRequestException.mistdemo webserver → printsServer stopped.and exits 0.Out of scope
🤖 Generated with Claude Code