Files
ComfyUI_frontend/src/platform/workspace/composables/useWorkspaceUI.ts
Simula_r dd1fefe843 fix: credit display and top up and other UI display if personal membe… (#8784)
## Summary

Consolidate scattered role checks for credits, top-up, and subscribe
buttons into centralized workspace permissions (canTopUp,
canManageSubscription), ensuring "Add Credits" requires an active
subscription, subscribe buttons only appear when needed, and team
members see appropriately restricted billing UI.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8784-fix-credit-display-and-top-up-and-other-UI-display-if-personal-membe-3036d73d3650810fbc2de084f738943c)
by [Unito](https://www.unito.io)
2026-02-11 14:26:35 -08:00

182 lines
4.7 KiB
TypeScript

import { computed, ref } from 'vue'
import { createSharedComposable } from '@vueuse/core'
import type { WorkspaceRole, WorkspaceType } from '../api/workspaceApi'
import { useTeamWorkspaceStore } from '../stores/teamWorkspaceStore'
/** Permission flags for workspace actions */
interface WorkspacePermissions {
canViewOtherMembers: boolean
canViewPendingInvites: boolean
canInviteMembers: boolean
canManageInvites: boolean
canRemoveMembers: boolean
canLeaveWorkspace: boolean
canAccessWorkspaceMenu: boolean
canManageSubscription: boolean
canTopUp: boolean
}
/** UI configuration for workspace role */
interface WorkspaceUIConfig {
showMembersList: boolean
showPendingTab: boolean
showSearch: boolean
showDateColumn: boolean
showRoleBadge: boolean
membersGridCols: string
pendingGridCols: string
headerGridCols: string
showEditWorkspaceMenuItem: boolean
workspaceMenuAction: 'leave' | 'delete' | null
workspaceMenuDisabledTooltip: string | null
}
function getPermissions(
type: WorkspaceType,
role: WorkspaceRole
): WorkspacePermissions {
if (type === 'personal') {
return {
canViewOtherMembers: false,
canViewPendingInvites: false,
canInviteMembers: false,
canManageInvites: false,
canRemoveMembers: false,
canLeaveWorkspace: false,
canAccessWorkspaceMenu: false,
canManageSubscription: true,
canTopUp: true
}
}
if (role === 'owner') {
return {
canViewOtherMembers: true,
canViewPendingInvites: true,
canInviteMembers: true,
canManageInvites: true,
canRemoveMembers: true,
canLeaveWorkspace: true,
canAccessWorkspaceMenu: true,
canManageSubscription: true,
canTopUp: true
}
}
// member role
return {
canViewOtherMembers: true,
canViewPendingInvites: false,
canInviteMembers: false,
canManageInvites: false,
canRemoveMembers: false,
canLeaveWorkspace: true,
canAccessWorkspaceMenu: true,
canManageSubscription: false,
canTopUp: false
}
}
function getUIConfig(
type: WorkspaceType,
role: WorkspaceRole
): WorkspaceUIConfig {
if (type === 'personal') {
return {
showMembersList: false,
showPendingTab: false,
showSearch: false,
showDateColumn: false,
showRoleBadge: false,
membersGridCols: 'grid-cols-1',
pendingGridCols: 'grid-cols-[50%_20%_20%_10%]',
headerGridCols: 'grid-cols-1',
showEditWorkspaceMenuItem: false,
workspaceMenuAction: null,
workspaceMenuDisabledTooltip: null
}
}
if (role === 'owner') {
return {
showMembersList: true,
showPendingTab: true,
showSearch: true,
showDateColumn: true,
showRoleBadge: true,
membersGridCols: 'grid-cols-[50%_40%_10%]',
pendingGridCols: 'grid-cols-[50%_20%_20%_10%]',
headerGridCols: 'grid-cols-[50%_40%_10%]',
showEditWorkspaceMenuItem: true,
workspaceMenuAction: 'delete',
workspaceMenuDisabledTooltip:
'workspacePanel.menu.deleteWorkspaceDisabledTooltip'
}
}
// member role
return {
showMembersList: true,
showPendingTab: false,
showSearch: true,
showDateColumn: true,
showRoleBadge: true,
membersGridCols: 'grid-cols-[1fr_auto]',
pendingGridCols: 'grid-cols-[50%_20%_20%_10%]',
headerGridCols: 'grid-cols-[1fr_auto]',
showEditWorkspaceMenuItem: false,
workspaceMenuAction: 'leave',
workspaceMenuDisabledTooltip: null
}
}
/**
* Internal implementation of UI configuration composable.
*/
function useWorkspaceUIInternal() {
const store = useTeamWorkspaceStore()
// Tab management (shared UI state)
const activeTab = ref<string>('plan')
function setActiveTab(tab: string | number) {
activeTab.value = String(tab)
}
const workspaceType = computed<WorkspaceType>(
() => store.activeWorkspace?.type ?? 'personal'
)
const workspaceRole = computed<WorkspaceRole>(
() => store.activeWorkspace?.role ?? 'owner'
)
const permissions = computed<WorkspacePermissions>(() =>
getPermissions(workspaceType.value, workspaceRole.value)
)
const uiConfig = computed<WorkspaceUIConfig>(() =>
getUIConfig(workspaceType.value, workspaceRole.value)
)
return {
// Tab management
activeTab: computed(() => activeTab.value),
setActiveTab,
// Permissions and config
permissions,
uiConfig,
workspaceType,
workspaceRole
}
}
/**
* UI configuration composable derived from workspace state.
* Controls what UI elements are visible/enabled based on role and workspace type.
* Uses createSharedComposable to ensure tab state is shared across components.
*/
export const useWorkspaceUI = createSharedComposable(useWorkspaceUIInternal)