-
Notifications
You must be signed in to change notification settings - Fork 619
feat(workspace): creator-only canManageSubscriptionLifecycle permission (FE-770) #12829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
6583e65
b4ee092
1bc10fa
8c098ee
96ac29b
a51183a
0092a45
2d109b2
8278c9d
dde38af
4ecc913
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import { computed } from 'vue' | ||
| import { computed, watch } from 'vue' | ||
| import { createSharedComposable } from '@vueuse/core' | ||
|
|
||
| import type { WorkspaceRole, WorkspaceType } from '../api/workspaceApi' | ||
|
|
@@ -14,6 +14,10 @@ interface WorkspacePermissions { | |
| canLeaveWorkspace: boolean | ||
| canAccessWorkspaceMenu: boolean | ||
| canManageSubscription: boolean | ||
| // Creator-only subscription lifecycle: cancel / reactivate / downgrade. | ||
| // Any owner has `canManageSubscription` (manage payment, top-up, change | ||
| // commit); only the original owner gets `canManageSubscriptionLifecycle`. | ||
| canManageSubscriptionLifecycle: boolean | ||
| canTopUp: boolean | ||
| } | ||
|
|
||
|
|
@@ -34,7 +38,8 @@ interface WorkspaceUIConfig { | |
|
|
||
| function getPermissions( | ||
| type: WorkspaceType, | ||
| role: WorkspaceRole | ||
| role: WorkspaceRole, | ||
| isOriginalOwner: boolean | ||
| ): WorkspacePermissions { | ||
| if (type === 'personal') { | ||
| return { | ||
|
|
@@ -46,6 +51,8 @@ function getPermissions( | |
| canLeaveWorkspace: false, | ||
| canAccessWorkspaceMenu: false, | ||
| canManageSubscription: true, | ||
| // Personal workspace is single-member: the user is the sole owner/creator. | ||
| canManageSubscriptionLifecycle: true, | ||
| canTopUp: true | ||
| } | ||
| } | ||
|
|
@@ -60,6 +67,7 @@ function getPermissions( | |
| canLeaveWorkspace: true, | ||
| canAccessWorkspaceMenu: true, | ||
| canManageSubscription: true, | ||
| canManageSubscriptionLifecycle: isOriginalOwner, | ||
| canTopUp: true | ||
| } | ||
| } | ||
|
|
@@ -74,6 +82,7 @@ function getPermissions( | |
| canLeaveWorkspace: true, | ||
| canAccessWorkspaceMenu: true, | ||
| canManageSubscription: false, | ||
| canManageSubscriptionLifecycle: false, | ||
| canTopUp: false | ||
| } | ||
| } | ||
|
|
@@ -145,8 +154,31 @@ function useWorkspaceUIInternal() { | |
| () => store.activeWorkspace?.role ?? 'owner' | ||
| ) | ||
|
|
||
| // The original-owner signal lives on the members-list self-row, so the | ||
| // members list must be loaded before the team gate resolves. Trigger a fetch | ||
| // for team workspaces whose members aren't loaded yet; until they arrive the | ||
| // store getter fails closed, hiding lifecycle actions during the load window. | ||
| // Watch the workspace id (not the object) so a fetch-induced identity change | ||
| // can't retrigger the watch; an empty members response then can't loop. | ||
| watch( | ||
| () => store.activeWorkspace?.id, | ||
| () => { | ||
| const workspace = store.activeWorkspace | ||
| if (workspace?.type === 'team' && store.members.length === 0) { | ||
| void store.fetchMembers() | ||
| } | ||
| }, | ||
| { immediate: true } | ||
| ) | ||
|
|
||
| const isOriginalOwner = computed(() => store.isCurrentUserOriginalOwner) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks redundant, can just use
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 8278c9d — inlined |
||
|
|
||
| const permissions = computed<WorkspacePermissions>(() => | ||
| getPermissions(workspaceType.value, workspaceRole.value) | ||
| getPermissions( | ||
| workspaceType.value, | ||
| workspaceRole.value, | ||
| isOriginalOwner.value | ||
| ) | ||
| ) | ||
|
|
||
| const uiConfig = computed<WorkspaceUIConfig>(() => | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be better off living in the store as an "ensureMembersLoaded", guarded for in flight duplicate calls, catch the failed request and log if failed, and with a "membersLoaded" flag instead of the .length === 0 check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved into the store as
ensureMembersLoadedin 8278c9d: no-ops for personal/already-loaded workspaces, dedupes in-flight calls, and catches+logs failures (leaving the workspace unloaded so a later call retries). Used a per-workspace loaded Set rather than a singlemembersLoadedboolean — a global flag would skip loading the new team on workspace switch.