Skip to content

feat: Shared Views — save the current table view as a (shareable) bookmark (#41)#74

Merged
smartlabsAT merged 21 commits into
mainfrom
feature/issue-41-shared-views
Jun 30, 2026
Merged

feat: Shared Views — save the current table view as a (shareable) bookmark (#41)#74
smartlabsAT merged 21 commits into
mainfrom
feature/issue-41-shared-views

Conversation

@smartlabsAT

Copy link
Copy Markdown
Owner

Summary

Closes #41.

Adds a "Save view" button to the Super Table header that saves the current table view as a native Directus bookmark, with a visibility scope:

  • Just me — a personal bookmark (what native Directus already offers)
  • My role — a role-wide shared view (admins)
  • Everyone — a global shared view (admins)

The real gap behind #41 was sharing. Directus' built-in "Save as bookmark" hardcodes the current user, so a configured view could only ever be personal. This adds the missing in-layout way to create shared (role-wide / global) views. Saving the full layout configuration into a bookmark already worked natively — the missing piece was the shared scope.

How it works (native — no custom storage)

  • Saving goes through the native presetsStore (via useStores()), never a custom collection or raw API:
    • mepresetsStore.savePreset(...) (core stamps the current user)
    • role / allpresetsStore.create({ ..., user: null, role }) (the high-level helpers can't, because they force the current user)
  • Loading / switching stays 100% native (the existing bookmark nav + ?bookmark= routing restores the view).
  • Scope options are gated on directus_presets create permission + userStore.isAdmin, with a 403 fallback (the "own presets only" rule lives in a validation constraint that /permissions/me omits, so admin is the only client-side-reliable signal).

Changes

  • src/types/sharedViews.types.ts — shared types (ViewScope, etc.)
  • src/utils/buildViewPreset.ts — pure, layout-keyed preset payload builder
  • src/utils/resolveAvailableScopes.ts — pure scope-availability logic
  • src/composables/useSharedViews.ts — orchestration over the native stores
  • src/actions.vue — the header button + dialog (placed left of the existing "Save as Quick Filter" button)

Testing

  • 450 unit tests passing (18 new, covering payload building, scope gating, and the scope→method/null/permission paths)
  • Live E2E on the running instance (admin): a "Everyone" save creates a global directus_presets row (user=null, role=null) carrying the correct layout-keyed layout_query/layout_options, the URL switches to ?bookmark=…, the bookmark appears in the nav, and the view restores on load
  • pnpm run quality (vue-tsc + eslint --max-warnings=0 + prettier) green

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

Quality Check Results

TypeScript Type Check

Passed - No type errors found

ESLint

Passed - No linting errors

Prettier Format Check

Passed - Code is properly formatted

Build

Passed - Extension builds successfully


Updated: 2026-06-29T20:00:58.927Z

@smartlabsAT

Copy link
Copy Markdown
Owner Author

Extended: multi-target view sharing (+ 3 follow-up bug fixes)

This PR now also carries the multi-target enhancement on top of the original 3-scope Shared Views feature (folded in via fast-forward).

What was added

An admin can share a saved view with multiple specific roles and/or users in one save (scope Specific targets), not only Just me / My role / Everyone. Each chosen target becomes its own native directus_presets row, upserted (GET → update/create) so re-saving the same name updates instead of duplicating, with a stable sharedViewId group key stamped into layout_options for a future manage/revoke UI.

  • src/types/sharedViews.types.ts — discriminated SaveViewInput, ShareTarget, RoleOption/UserOption
  • src/composables/useShareTargets.ts — permission-aware role/user lists (403 → empty, never throws)
  • src/composables/useSharedViews.ts — per-target upsert loop via Promise.allSettled + partial-failure summary
  • src/components/SaveViewDialog.vue — extracted dialog with role/user multi-pickers (color/scroll helpers de-duplicated into src/utils/)
  • src/utils/buildShareTargets.ts — pure target builder with recipient de-duplication

Permissions

Specific/Everyone stay admin-gated (computed + dialog + the server-side 400 backstop). A non-admin only ever sees Just me — verified live with a temporary non-admin user.

Follow-up bug fixes (each live-reproduced and re-verified)

  1. Self-share duplicates — the current user is now excluded from the user picker.
  2. Optimistic nav pollutionpresetsStore.hydrate() after the share loop drops foreign-owner rows the native create optimistically pushed into the sharer's nav (the view no longer appears N times until reload).
  3. Recipient double-bookmark — a user already covered by a selected role is skipped, so a recipient matched via role AND personally is bookmarked once (verified by switching between real users across roles).

Testing

  • Vitest 465/465, pnpm run quality green (vue-tsc + eslint --max-warnings=0 + prettier).
  • Live E2E on Directus 11.11.0: me / all / specific (1+1 and 2+2), upsert, notifications, navigation rules, non-admin gating, and a multi-user recipient test across distinct roles incl. the role+personal overlap. All temporary users/roles/data cleaned up.

@smartlabsAT smartlabsAT merged commit 05bb665 into main Jun 30, 2026
22 checks passed
@smartlabsAT smartlabsAT deleted the feature/issue-41-shared-views branch June 30, 2026 08:29
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.

Bookmark save button absent

1 participant