Open
Conversation
…d Workspace model (id, name, isDefault, createdAt, userId)\n- Add WorkspaceMember model (workspaceId, userId, relation) with CASCADE delete\n- Add optional workspaceId FK on Project (ON DELETE SET NULL)\n- Drop unused Team model and User.teamId column\n- Recreate DashboardProject view with workspaceId column\n- Recreate latestBuildVirtual wrapper using LATERAL join\n- Remove teamId from dashboard storybook mock\n- Partial unique index enforces one default workspace per user
- Create default "Personal" workspace at user signup in genericCreateAccount - Add data migration to backfill existing users with default workspaces - Assign unassigned projects to their owner's default workspace - Add Workspace type export from @webstudio-is/project - Regenerate PostgREST types with Workspace/WorkspaceMember tables - Add workspaceId to storybook/test mock data - Add @webstudio-is/postgrest dependency to project package
Add create, rename, remove, and findMany functions for workspaces. - create: non-default workspace with name validation (min 2 chars) - rename: owner-only, with name validation - remove: owner-only, rejects default workspace and non-empty workspaces - findMany: returns owned + member-of workspaces, default first Wire workspace tRPC router into the app router with matching procedures: workspace.create, workspace.rename, workspace.delete, workspace.list.
Projects are created inside a workspace and listed per workspace. - project.create accepts optional workspaceId, sets it on the new project - dashboard findMany accepts optional workspaceId filter - Dashboard route loader loads workspaces list and reads selected workspace from cookie when the workspaces flag is on - DashboardData type extended with workspaces and currentWorkspaceId When the flag is off, all behavior is unchanged.
…selection Replace cookie-based workspace selection with ?workspaceId= URL param. No param means the default workspace. Simpler, shareable, no cookie infrastructure needed.
Add/remove/list workspace members by email. - addMember: owner-only, looks up user by email, inserts as administrator, rejects duplicates and self-add - removeMember: owner-only, prevents removing the owner - listMembers: visible to owner and members, returns user email/username tRPC procedures: workspace.addMember, workspace.removeMember, workspace.listMembers.
…kspaceProjectAuthorization SQL view (workspace owner = own, members = their relation)\n- Update authorization check() to query workspace view when flag is on\n- Dashboard findMany filters by workspaceId instead of userId for workspace members\n- Builder resolves plan features from workspace owner when project belongs to a workspace\n- Add @webstudio-is/feature-flags dependency to trpc-interface and dashboard packages
…ector component with design system Select\n- Wire workspace selector into dashboard sidebar (feature-flagged)\n- Update shouldRevalidate to reload data on workspace switch\n- Pass currentWorkspaceId through to CreateProject so new projects\n are created in the currently viewed workspace\n- Rename sidebar section from \"Workspace\" to \"Navigation\" when\n workspace selector is visible
…n\n\n- Add color='ghost' variant to SelectButton (transparent bg, hover highlight)\n- Pass color prop through Select to SelectButton\n- Rewrite workspace selector to use Select with color='ghost'\n- Render workspace selector directly in sidebar nav (no CollapsibleSection)
- Add workspace dialogs: create, rename, manage members, delete - Rewrite workspace selector as dropdown menu with action items - Support comma-separated multi-email member invites - Create placeholder users for non-existing email invites - Add ScrollAreaNative component with native thin scrollbar - Show newest members first in member list - Preserve workspaceId query param in navigation links - Fix DashboardSetup to use useEffect for data sync
- Pass userId to WorkspaceSelector for ownership checks - Disable Rename/Delete menu items for non-owners - Hide invite input and remove buttons for non-owners in ManageMembersDialog - Add empty state for member list - Add Avatar with small size variant (data-size attribute) to workspace selector - Use userId prop instead of isOwner boolean for future role extensibility
- Backend: accept deleteProjects flag to soft-delete all projects before removing workspace - Frontend: warning text mentions all projects will be deleted, with 'All projects' in red - Dashboard: detect stale workspaceId in URL and redirect to default workspace
- Extract softDeleteProject helper so unique domain invariant lives in one place - Projects created in a workspace are owned by workspace owner, not creator - Remove unnecessary useEffect around $data.set - Remove unused parseTrpcError helper - Remove unsafe `as TokenAuthPermit` cast - Add sortWorkspaces tests - Document idempotent retry safety in workspace remove()
…ck when creating projects in a workspace\n- Remove placeholder user creation on invite (prevent DB pollution)\n- Show invited emails client-side to prevent email enumeration\n- Show only email in member rows (no username leak)\n- Throw on workspace plan resolution error instead of silent fallback\n- DRY navigation items in dashboard sidebar\n- Replace null with empty string in listMembers return\n- Restore useEffect in DashboardSetup to fix React render warning
…mber response parity
- Add unified getPermissions() combining role + plan permissions - Gate project actions (create, delete, publish, share, etc.) by workspace role - Introduce MemberRelation schema (owners/admins/builders/viewers) - Compute $permissions from $userPlanFeatures + $authPermit in nano-states - Set $userPlanFeatures globally in DashboardSetup (matching builder pattern) - Add workspace dialogs for managing members and invitations - Enforce permissions across dashboard, builder, and settings UI
…ns prop-drilling, unify shared stores
- Rename userRelation → workspaceRelation across schema, DB, router, and UI
- Rename MemberRelation type → WorkspaceRelation everywhere
- Create canonical UserPlanFeatures/UserPurchase module in trpc-interface
- Make userPlanFeatures non-optional in AppContext
- Separate purchases from UserPlanFeatures
- Compute workspaceRelation in route loaders instead of consumer sites
- Add $workspaceRelation atom, use real relation in $permissions computed
- Remove permissions prop-drilling from all dashboard components
- Leaf components use useStore($permissions) directly
- Create setSharedStores() for unified store initialization
- Combine workspace + membership into single PostgREST query in builder route
- Replace purchases.length checks with canDownloadAssets/canRestoreBackups
- getUserPlanFeatures() → getUserPlanInfo() returning { userPlanFeatures, purchases }
- Rename dialog title and dropdown item from "Manage members" to "Members"
- Remove icons from workspace selector dropdown menu items
- Remove separator before Delete item
- Set withIndicator={false} to fix indentation without icons
# Conflicts: # apps/builder/app/builder/features/menu/menu.tsx # apps/builder/app/dashboard/dashboard.stories.tsx
- Show user name next to profile avatar in dashboard sidebar - Separate welcome view from templates into its own component - Welcome subtitle with create blank project button (permission-gated) - Allow PROJECT_TEMPLATES to skip marketplace approval check - Templates view shows clean "Starter templates" title
- Replace workspaces feature flag with maxWorkspaces-based gating - Default maxWorkspaces to 0 (controlled via MAX_WORKSPACES env var) - Skip workspace queries in dashboard loader when maxWorkspaces is 0 - Extract pure functions (resolveCurrentWorkspace, isWorkspaceRelationPermitted, getView) with tests - Remove workspaces feature flag from all files
…space-dropdown.tsx with reusable dropdown (trigger + radio groups + children slot)\n- Refactor workspace-selector to use WorkspaceDropdown with color=ghost and action items as children\n- Refactor transfer-dialog to use WorkspaceDropdown without color prop and no action items\n- Add OR separator between workspace and recipient sections
…mpossible NoWorkspaces transfer story
…r/move guards - notification.create returns notification ID - addMember returns notificationId (fake UUID for non-existent users) - workspace router exposes notificationId in addMember response - workspace-dialogs: trash button calls notification.cancel with stored ID - replace $userEmail atom with $user storing full User object - add maxProjectsAllowedPerUser to UserPlanFeatures + all plan constructors - plan-limit check on transfer accept - global dedup: one pending transfer per project - notification.cancel endpoint - cross-owner move restriction in moveProject - self-transfer check in transfer dialog
Add a universal topic-based polling system (polly) that replaces the old notification-store with a more extensible architecture: - Backoff, polling-client, polling-manager with full test coverage (86 tests) - Server-side topic resolvers + tRPC subscription router - Dashboard subscription wiring with loader-seeded notifications - In-app toast as primary alert, browser Notification as supplement - Notification popover reads from shared nanostore atom - project-queue.ts uses polly backoff, tests migrated to vi.mock()
Only one browser tab actively polls the server. Other tabs receive data via BroadcastChannel, eliminating duplicate network requests. - Leader election via heartbeat protocol (lowest tab-id wins) - Instant failover on leader tab close (abdication broadcast) - Follower topic sync so leader tracks all tabs' subscriptions - Drop-in replacement for createPollingManager (same interface) - Dashboard subscription now uses createCrossTabPollingManager - 10 new tests (leader election, failover, topic sync, destroy) - Future topic ideas documented in types.ts registry comment
Move description generation to the server (notification.list) so the popover renders a pre-built string. Adding a new notification type now only requires updating describeNotification on the server — the UI stays generic. - Remove discriminated union types and WorkspaceInvitePayload import from notification-popover.tsx - NotificationItem is now derived from the server return type - Move getDescription tests → describeNotification tests - Remove unused __testing__ export from cross-tab-manager.ts
The story data object was missing the `notifications` field added to DashboardData, causing seedNotifications to call .filter() on undefined.
…n\n- Delete unused OwnedWorkspaces story from transfer-dialog.stories\n- Add missing projectTitle/workspaceName to notification story items\n- Fix JsonifyObject incompatibility for notifications in dashboard route
…move order, approval filter, cleanup
Changed builder auth check from 'own' to 'edit' permit so workspace administrators, builders, and editors can access projects.
moveProject previously allowed silently reassigning project ownership when moving to another user's workspace (e.g. as an admin). This meant the workspace owner never got a transfer notification — the project just appeared. Now moveProject rejects cross-owner moves, forcing the transfer flow (transferProject) which always creates a notification the recipient must accept. The transfer dialog no longer shows shared workspaces in move mode — only own workspaces are listed without an email.
Added a projectCount polling topic so the sender's dashboard revalidates when a transfer they initiated is accepted. The polled count changes as soon as the project ownership flips, triggering a revalidation that removes the transferred project from view.
Changed builder access check from 'edit' to 'view' permit so workspace viewers can also open projects. Per-action permissions are still enforced inside the builder.
- Add Animate.css swing keyframes to bell icon when notifications are pending - Stop animation permanently once the popover has been opened
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.
No description provided.