feat(view-preset): shared view presets with fork-on-edit semantics
Lands the view-preset / server-persisted personal-layer feature that has been running in production, plus the hotfix for "the same preset shows different filter configs to different users" (物流问题-已出轨迹 on tblgsJTXhqwiDpsjeL5 / viwyszJmLQNOLhA9sh5). Root cause of the divergence: - usePersonalViewSync implicitly flushed any overlay edit by a view|update user INTO the shared preset row (last-writer-wins). Overlays are stale snapshots seeded at apply time, so one user's seed silently erased colleagues' filter changes (2026-09-17 02:55 the preset lost the 流转角色=售后 condition), and active overlays never converged because preset updates do not propagate. Fix (semantics: presets are shared definitions, edits are personal): - fork-on-edit: the first user edit strips the seed markers from the overlay (toolbar switches to the personal config); edits only ever write the user's personal row - shared presets change ONLY through the new explicit, confirmed "Update to current config" row action (canManage users) - un-edited preset overlays auto-follow redefinitions: applyPreset stamps a presetVersion (preset.lastModifiedTime); when the preset list refetches with a newer row the overlay re-seeds. Edited overlays carry no markers and are never clobbered. Legacy rows without a version re-seed exactly once - exits strip both seed markers from the persisted personal row Also lands the pending WIP shipped with the deployed build: select-choice visibility constraint, personal-view server sync + proxy, grid async records scope/request tracking, CLI resource display, preset i18n (10 locales), view_preset migrations, docs/skill updates, .zcode gitignore. Verified end-to-end on the live instance (headless chromium, real session): apply -> edit flips toolbar to Personal with ZERO shared-row PUTs; explicit update PUTs the shared row; API-side redefinition auto-reseeds an un-edited overlay on the next list refetch.
This commit is contained in:
Vendored
+15
@@ -154,6 +154,21 @@ class Client {
|
||||
deleteView(viewId) {
|
||||
return this.call('DELETE', `/api/view/${viewId}`);
|
||||
}
|
||||
// ---- view preset (self-built contract) ----
|
||||
// Preset view configurations: VIEW-scoped named bundles of
|
||||
// filter/sort/group(/options/columnMeta) shared with all collaborators.
|
||||
listViewPresets(tableId, viewId) {
|
||||
return this.call('GET', `/api/table/${tableId}/view-preset${viewId ? `?viewId=${encodeURIComponent(viewId)}` : ''}`);
|
||||
}
|
||||
createViewPreset(tableId, body) {
|
||||
return this.call('POST', `/api/table/${tableId}/view-preset`, body);
|
||||
}
|
||||
updateViewPreset(tableId, presetId, body) {
|
||||
return this.call('PUT', `/api/table/${tableId}/view-preset/${presetId}`, body);
|
||||
}
|
||||
deleteViewPreset(tableId, presetId) {
|
||||
return this.call('DELETE', `/api/table/${tableId}/view-preset/${presetId}`);
|
||||
}
|
||||
// ---- workflow (self-built contract) ----
|
||||
listWorkflows(baseId) {
|
||||
return this.call('GET', `/api/base/${baseId}/workflow`);
|
||||
|
||||
Reference in New Issue
Block a user