@burdenoff/microfe-organization 2026.608.1 → 2026.616.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/global-operations.js +78 -63
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js.map +1 -1
- package/dist/organization-management/UserOrganizationRoutes.js +13 -9
- package/dist/organization-management/UserOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/modules/members/hooks/useInvites.js +79 -65
- package/dist/organization-management/modules/members/hooks/useInvites.js.map +1 -1
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js +287 -0
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js.map +1 -0
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js +112 -112
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js +8 -7
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js +2 -5
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js +86 -134
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js.map +1 -1
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaces.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js +4 -18
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js +4 -22
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWorkspaces.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/hooks/useWorkspaces.ts"],"sourcesContent":["import {\n useGetWorkspacesQuery,\n useGetMyWorkspacesQuery,\n useGetWorkspaceQuery,\n useGetTenantsQuery,\n useCreateWorkspaceMutation,\n useUpdateWorkspaceMutation,\n useSuspendWorkspaceMutation,\n GetWorkspacesDocument,\n} from '../../../../generated/global-operations';\nimport type {\n Workspace,\n WorkspaceFilterInput,\n CreateWorkspaceInput,\n UpdateWorkspaceInput,\n} from '../types';\n\n/**\n * Scope of the workspace listing:\n * - `'my'` — membership-scoped via `myWorkspaces` (@rbac scopeType: \"self\").\n * The correct scope for user-facing app shells; a regular user only\n * ever sees the workspaces they belong to. `myWorkspaces` takes no\n * server-side filter arg, so callers filter client-side.\n * - `'all'` — platform-wide via `allWorkspaces` (@rbac scopeType: \"global\",\n * super-admin / platform-admin only). Reserved for the admin app.\n */\nexport type WorkspacesScope = 'my' | 'all';\n\n/**\n * Hook to fetch workspaces with filtering and pagination.\n * Uses offset-based pagination (limit/offset).\n *\n * `scope` selects the backing query so the user shells (`'my'`) never call the\n * global-scoped `allWorkspaces`; the admin app keeps `'all'`. Both queries are\n * declared (rules-of-hooks) and the inactive one is skipped.\n */\nexport function useWorkspaces(\n filter?: WorkspaceFilterInput,\n limit: number = 20,\n offset: number = 0,\n scope: WorkspacesScope = 'all'\n) {\n const allResult = useGetWorkspacesQuery({\n variables: { filter, limit, offset },\n skip: scope !== 'all',\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n const myResult = useGetMyWorkspacesQuery({\n variables: { limit, offset },\n skip: scope !== 'my',\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n const { loading, error, refetch, fetchMore } = scope === 'my' ? myResult : allResult;\n const list = scope === 'my' ? myResult.data?.myWorkspaces : allResult.data?.allWorkspaces;\n\n return {\n workspaces: (list?.items as Workspace[]) || [],\n total: list?.total || 0,\n limit: list?.limit || limit,\n offset: list?.offset || offset,\n hasMore: list?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a single workspace by ID\n */\nexport function useWorkspace(id: string) {\n const { data, loading, error, refetch } = useGetWorkspaceQuery({\n variables: { id },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n workspace: data?.workspace as Workspace | undefined,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch workspaces by organization ID\n */\nexport function useWorkspacesByOrganization(\n organizationId: string,\n limit: number = 20,\n offset: number = 0\n) {\n // User-facing organization pages must stay membership-scoped. `myWorkspaces`\n // has no organization filter arg, so fetch the caller-visible list and narrow\n // it client-side to the current organization.\n const effectiveLimit = Math.max(limit + offset, 200);\n const { data, loading, error, refetch, fetchMore } = useGetMyWorkspacesQuery({\n variables: { limit: effectiveLimit, offset: 0 },\n skip: !organizationId,\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n const organizationWorkspaces = (\n (data?.myWorkspaces?.items as Workspace[] | undefined) ?? []\n ).filter((workspace) => workspace.organizationId === organizationId);\n const paginatedWorkspaces = organizationWorkspaces.slice(offset, offset + limit);\n\n return {\n workspaces: paginatedWorkspaces,\n total: organizationWorkspaces.length,\n hasMore: offset + paginatedWorkspaces.length < organizationWorkspaces.length,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to create a new workspace\n */\nexport function useCreateWorkspace() {\n const [createWorkspace, { data, loading, error }] = useCreateWorkspaceMutation({\n refetchQueries: [GetWorkspacesDocument],\n });\n\n return {\n createWorkspace: (input: CreateWorkspaceInput) => createWorkspace({ variables: { input } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to update a workspace\n */\nexport function useUpdateWorkspace() {\n const [updateWorkspace, { data, loading, error }] = useUpdateWorkspaceMutation();\n\n return {\n updateWorkspace: (id: string, input: UpdateWorkspaceInput) =>\n updateWorkspace({ variables: { id, input } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to delete (suspend) a workspace\n * Note: Backend doesn't have deleteWorkspace, using suspendWorkspace instead\n */\nexport function useDeleteWorkspace() {\n const [deleteWorkspace, { data, loading, error }] = useSuspendWorkspaceMutation({\n refetchQueries: [GetWorkspacesDocument],\n });\n\n return {\n deleteWorkspace: (id: string) => deleteWorkspace({ variables: { id } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Tenant type for the tenants query\n */\ninterface Tenant {\n id: string;\n name: string;\n slug: string;\n status: string;\n}\n\n/**\n * Hook to fetch tenants available to the current actor for workspace creation\n */\nexport function useTenants(limit: number = 100, offset: number = 0, enabled: boolean = true) {\n const { data, loading, error, refetch } = useGetTenantsQuery({\n variables: { limit, offset },\n skip: !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n tenants: (data?.myTenants?.items as Tenant[]) || [],\n total: data?.myTenants?.total || 0,\n loading,\n error,\n refetch,\n };\n}\n"],"mappings":";;AAoCA,SAAgB,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB,IAAyB,OACzB;CACA,IAAM,IAAY,EAAsB;EACtC,WAAW;GAAE;GAAQ;GAAO;GAAQ;EACpC,MAAM,MAAU;EAChB,aAAa;EACb,iBAAiB;EAClB,CAAC,EACI,IAAW,EAAwB;EACvC,WAAW;GAAE;GAAO;GAAQ;EAC5B,MAAM,MAAU;EAChB,aAAa;EACb,iBAAiB;EAClB,CAAC,EAEI,EAAE,YAAS,UAAO,YAAS,iBAAc,MAAU,OAAO,IAAW,GACrE,IAAO,MAAU,OAAO,EAAS,MAAM,eAAe,EAAU,MAAM;AAE5E,QAAO;EACL,YAAa,GAAM,SAAyB,EAAE;EAC9C,OAAO,GAAM,SAAS;EACtB,OAAO,GAAM,SAAS;EACtB,QAAQ,GAAM,UAAU;EACxB,SAAS,GAAM,WAAW;EAC1B;EACA;EACA;EACA;EACD;;AAMH,SAAgB,EAAa,GAAY;CACvC,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAqB;EAC7D,WAAW,EAAE,OAAI;EACjB,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,WAAW,GAAM;EACjB;EACA;EACA;EACD;;AAMH,SAAgB,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB;CAKA,IAAM,EAAE,SAAM,YAAS,UAAO,YAAS,iBAAc,EAAwB;EAC3E,WAAW;GAAE,OAFQ,KAAK,IAAI,IAAQ,GAAQ,IAAI;GAEd,QAAQ;GAAG;EAC/C,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC,EAEI,KACH,GAAM,cAAc,SAAqC,EAAE,EAC5D,QAAQ,MAAc,EAAU,mBAAmB,EAAe,EAC9D,IAAsB,EAAuB,MAAM,GAAQ,IAAS,EAAM;AAEhF,QAAO;EACL,YAAY;EACZ,OAAO,EAAuB;EAC9B,SAAS,IAAS,EAAoB,SAAS,EAAuB;EACtE;EACA;EACA;EACA;EACD;;AAMH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,EAA2B,EAC7E,gBAAgB,CAAC,EAAsB,EACxC,CAAC;AAEF,QAAO;EACL,kBAAkB,MAAgC,EAAgB,EAAE,WAAW,EAAE,UAAO,EAAE,CAAC;EAC3F;EACA;EACA;EACD;;AAMH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,GAA4B;AAEhF,QAAO;EACL,kBAAkB,GAAY,MAC5B,EAAgB,EAAE,WAAW;GAAE;GAAI;GAAO,EAAE,CAAC;EAC/C;EACA;EACA;EACD;;AAOH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,EAA4B,EAC9E,gBAAgB,CAAC,EAAsB,EACxC,CAAC;AAEF,QAAO;EACL,kBAAkB,MAAe,EAAgB,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC;EACvE;EACA;EACA;EACD;;
|
|
1
|
+
{"version":3,"file":"useWorkspaces.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/hooks/useWorkspaces.ts"],"sourcesContent":["import {\n useGetWorkspacesQuery,\n useGetMyWorkspacesQuery,\n useGetWorkspaceQuery,\n useGetTenantsQuery,\n useCreateWorkspaceMutation,\n useUpdateWorkspaceMutation,\n useSuspendWorkspaceMutation,\n GetWorkspacesDocument,\n} from '../../../../generated/global-operations';\nimport type {\n Workspace,\n WorkspaceFilterInput,\n CreateWorkspaceInput,\n UpdateWorkspaceInput,\n} from '../types';\n\n/**\n * Scope of the workspace listing:\n * - `'my'` — membership-scoped via `myWorkspaces` (@rbac scopeType: \"self\").\n * The correct scope for user-facing app shells; a regular user only\n * ever sees the workspaces they belong to. `myWorkspaces` takes no\n * server-side filter arg, so callers filter client-side.\n * - `'all'` — platform-wide via `allWorkspaces` (@rbac scopeType: \"global\",\n * super-admin / platform-admin only). Reserved for the admin app.\n */\nexport type WorkspacesScope = 'my' | 'all';\n\n/**\n * Hook to fetch workspaces with filtering and pagination.\n * Uses offset-based pagination (limit/offset).\n *\n * `scope` selects the backing query so the user shells (`'my'`) never call the\n * global-scoped `allWorkspaces`; the admin app keeps `'all'`. Both queries are\n * declared (rules-of-hooks) and the inactive one is skipped.\n */\nexport function useWorkspaces(\n filter?: WorkspaceFilterInput,\n limit: number = 20,\n offset: number = 0,\n scope: WorkspacesScope = 'all'\n) {\n const allResult = useGetWorkspacesQuery({\n variables: { filter, limit, offset },\n skip: scope !== 'all',\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n const myResult = useGetMyWorkspacesQuery({\n variables: { limit, offset },\n skip: scope !== 'my',\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n const { loading, error, refetch, fetchMore } = scope === 'my' ? myResult : allResult;\n const list = scope === 'my' ? myResult.data?.myWorkspaces : allResult.data?.allWorkspaces;\n\n return {\n workspaces: (list?.items as Workspace[]) || [],\n total: list?.total || 0,\n limit: list?.limit || limit,\n offset: list?.offset || offset,\n hasMore: list?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a single workspace by ID\n */\nexport function useWorkspace(id: string) {\n const { data, loading, error, refetch } = useGetWorkspaceQuery({\n variables: { id },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n workspace: data?.workspace as Workspace | undefined,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch workspaces by organization ID\n */\nexport function useWorkspacesByOrganization(\n organizationId: string,\n limit: number = 20,\n offset: number = 0\n) {\n // User-facing organization pages must stay membership-scoped. `myWorkspaces`\n // has no organization filter arg, so fetch the caller-visible list and narrow\n // it client-side to the current organization.\n const effectiveLimit = Math.max(limit + offset, 200);\n const { data, loading, error, refetch, fetchMore } = useGetMyWorkspacesQuery({\n variables: { limit: effectiveLimit, offset: 0 },\n skip: !organizationId,\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n const organizationWorkspaces = (\n (data?.myWorkspaces?.items as Workspace[] | undefined) ?? []\n ).filter((workspace) => workspace.organizationId === organizationId);\n const paginatedWorkspaces = organizationWorkspaces.slice(offset, offset + limit);\n\n return {\n workspaces: paginatedWorkspaces,\n total: organizationWorkspaces.length,\n hasMore: offset + paginatedWorkspaces.length < organizationWorkspaces.length,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to create a new workspace\n */\nexport function useCreateWorkspace() {\n const [createWorkspace, { data, loading, error }] = useCreateWorkspaceMutation({\n refetchQueries: [GetWorkspacesDocument],\n });\n\n return {\n createWorkspace: (input: CreateWorkspaceInput) => createWorkspace({ variables: { input } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to update a workspace\n */\nexport function useUpdateWorkspace() {\n const [updateWorkspace, { data, loading, error }] = useUpdateWorkspaceMutation();\n\n return {\n updateWorkspace: (id: string, input: UpdateWorkspaceInput) =>\n updateWorkspace({ variables: { id, input } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to delete (suspend) a workspace\n * Note: Backend doesn't have deleteWorkspace, using suspendWorkspace instead\n */\nexport function useDeleteWorkspace() {\n const [deleteWorkspace, { data, loading, error }] = useSuspendWorkspaceMutation({\n refetchQueries: [GetWorkspacesDocument],\n });\n\n return {\n deleteWorkspace: (id: string) => deleteWorkspace({ variables: { id } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Tenant type for the tenants query\n */\ninterface Tenant {\n id: string;\n name: string;\n slug: string;\n status: string;\n /** SHARED | DEDICATED — lets the UI prefer the shared tenant when auto-selecting. */\n type?: string;\n}\n\n/**\n * Hook to fetch tenants available to the current actor for workspace creation\n */\nexport function useTenants(limit: number = 100, offset: number = 0, enabled: boolean = true) {\n const { data, loading, error, refetch } = useGetTenantsQuery({\n variables: { limit, offset },\n skip: !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n tenants: (data?.myTenants?.items as Tenant[]) || [],\n total: data?.myTenants?.total || 0,\n loading,\n error,\n refetch,\n };\n}\n"],"mappings":";;AAoCA,SAAgB,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB,IAAyB,OACzB;CACA,IAAM,IAAY,EAAsB;EACtC,WAAW;GAAE;GAAQ;GAAO;GAAQ;EACpC,MAAM,MAAU;EAChB,aAAa;EACb,iBAAiB;EAClB,CAAC,EACI,IAAW,EAAwB;EACvC,WAAW;GAAE;GAAO;GAAQ;EAC5B,MAAM,MAAU;EAChB,aAAa;EACb,iBAAiB;EAClB,CAAC,EAEI,EAAE,YAAS,UAAO,YAAS,iBAAc,MAAU,OAAO,IAAW,GACrE,IAAO,MAAU,OAAO,EAAS,MAAM,eAAe,EAAU,MAAM;AAE5E,QAAO;EACL,YAAa,GAAM,SAAyB,EAAE;EAC9C,OAAO,GAAM,SAAS;EACtB,OAAO,GAAM,SAAS;EACtB,QAAQ,GAAM,UAAU;EACxB,SAAS,GAAM,WAAW;EAC1B;EACA;EACA;EACA;EACD;;AAMH,SAAgB,EAAa,GAAY;CACvC,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAqB;EAC7D,WAAW,EAAE,OAAI;EACjB,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,WAAW,GAAM;EACjB;EACA;EACA;EACD;;AAMH,SAAgB,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB;CAKA,IAAM,EAAE,SAAM,YAAS,UAAO,YAAS,iBAAc,EAAwB;EAC3E,WAAW;GAAE,OAFQ,KAAK,IAAI,IAAQ,GAAQ,IAAI;GAEd,QAAQ;GAAG;EAC/C,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC,EAEI,KACH,GAAM,cAAc,SAAqC,EAAE,EAC5D,QAAQ,MAAc,EAAU,mBAAmB,EAAe,EAC9D,IAAsB,EAAuB,MAAM,GAAQ,IAAS,EAAM;AAEhF,QAAO;EACL,YAAY;EACZ,OAAO,EAAuB;EAC9B,SAAS,IAAS,EAAoB,SAAS,EAAuB;EACtE;EACA;EACA;EACA;EACD;;AAMH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,EAA2B,EAC7E,gBAAgB,CAAC,EAAsB,EACxC,CAAC;AAEF,QAAO;EACL,kBAAkB,MAAgC,EAAgB,EAAE,WAAW,EAAE,UAAO,EAAE,CAAC;EAC3F;EACA;EACA;EACD;;AAMH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,GAA4B;AAEhF,QAAO;EACL,kBAAkB,GAAY,MAC5B,EAAgB,EAAE,WAAW;GAAE;GAAI;GAAO,EAAE,CAAC;EAC/C;EACA;EACA;EACD;;AAOH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,EAA4B,EAC9E,gBAAgB,CAAC,EAAsB,EACxC,CAAC;AAEF,QAAO;EACL,kBAAkB,MAAe,EAAgB,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC;EACvE;EACA;EACA;EACD;;AAkBH,SAAgB,EAAW,IAAgB,KAAK,IAAiB,GAAG,IAAmB,IAAM;CAC3F,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAmB;EAC3D,WAAW;GAAE;GAAO;GAAQ;EAC5B,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,SAAU,GAAM,WAAW,SAAsB,EAAE;EACnD,OAAO,GAAM,WAAW,SAAS;EACjC;EACA;EACA;EACD"}
|
|
@@ -108,17 +108,10 @@ function O() {
|
|
|
108
108
|
className: "min-w-0 space-y-2",
|
|
109
109
|
children: [/* @__PURE__ */ S("div", {
|
|
110
110
|
className: "flex flex-wrap items-start gap-2",
|
|
111
|
-
children: [
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}),
|
|
116
|
-
/* @__PURE__ */ x(u, { status: I.status }),
|
|
117
|
-
/* @__PURE__ */ x("span", {
|
|
118
|
-
className: "inline-flex items-center rounded-md bg-[var(--color-bg-subtle)] px-2 py-1 text-xs font-medium text-[var(--color-text-secondary)]",
|
|
119
|
-
children: I.type
|
|
120
|
-
})
|
|
121
|
-
]
|
|
111
|
+
children: [/* @__PURE__ */ x("h1", {
|
|
112
|
+
className: "text-3xl font-bold text-[var(--color-text-primary)]",
|
|
113
|
+
children: I.name
|
|
114
|
+
}), /* @__PURE__ */ x(u, { status: I.status })]
|
|
122
115
|
}), /* @__PURE__ */ S("p", {
|
|
123
116
|
className: "break-all font-mono text-sm text-[var(--color-text-secondary)]",
|
|
124
117
|
children: ["/", I.slug]
|
|
@@ -195,13 +188,6 @@ function O() {
|
|
|
195
188
|
className: "mt-1",
|
|
196
189
|
children: /* @__PURE__ */ x(u, { status: I.status })
|
|
197
190
|
})] }),
|
|
198
|
-
/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("dt", {
|
|
199
|
-
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
200
|
-
children: O("workspaces.list.column.type", "Type")
|
|
201
|
-
}), /* @__PURE__ */ x("dd", {
|
|
202
|
-
className: "mt-1 text-sm text-[var(--color-text-primary)]",
|
|
203
|
-
children: I.type
|
|
204
|
-
})] }),
|
|
205
191
|
/* @__PURE__ */ S("div", {
|
|
206
192
|
className: "sm:col-span-2",
|
|
207
193
|
children: [/* @__PURE__ */ x("dt", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/pages/WorkspaceDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Briefcase, Edit, Trash2, UserPlus, Users } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n ActorIdentity,\n buildActorFallback,\n useActorProfiles,\n type ActorInput,\n} from '@burdenoff/fe-libs/shared';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useWorkspace } from '../hooks/useWorkspaces';\nimport { useRemoveWorkspaceMember, useWorkspaceMembers } from '../hooks/useWorkspaceMembers';\nimport { WorkspaceStatusBadge } from '../components/WorkspaceStatusBadge';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { MemberRoleBadge } from '../../members/components/MemberRoleBadge';\nimport { AddWorkspaceMemberDialog } from '../components/AddWorkspaceMemberDialog';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ntype ActiveTab = 'overview' | 'members';\n\nfunction formatDate(dateString: string) {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport function WorkspaceDetailPage() {\n const tr = useTr();\n const { id } = useParams<{ id: string }>();\n const { navigate, basePath = '/organizations', apiGatewayUrl, authToken } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { workspace, loading, error } = useWorkspace(id ?? '');\n const [activeTab, setActiveTab] = useState<ActiveTab>('overview');\n const [isAddMemberDialogOpen, setIsAddMemberDialogOpen] = useState(false);\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n const shouldFetchMembers = activeTab === 'members' && Boolean(id);\n const {\n members,\n loading: membersLoading,\n error: membersError,\n refetch: refetchMembers,\n } = useWorkspaceMembers(shouldFetchMembers ? (id ?? '') : '', 50, 0);\n const { removeWorkspaceMember, loading: removingMember } = useRemoveWorkspaceMember();\n const resolvedMemberCount =\n members.length > 0 || activeTab === 'members' ? members.length : (workspace?.memberCount ?? 0);\n\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [members]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: workspace?.organizationId,\n workspaceId: id,\n });\n\n const tabItems = useMemo(\n () => [\n { id: 'overview' as const, label: tr('workspaces.detail.tab.overview', 'Overview') },\n {\n id: 'members' as const,\n label: `${tr('workspaces.detail.tab.members', 'Members')} (${resolvedMemberCount})`,\n },\n ],\n [resolvedMemberCount, tr]\n );\n\n const handleRemoveMember = (userId: string) => {\n if (!id) return;\n\n openConfirmDialog({\n title: tr('workspaces.removeMember.title', 'Remove workspace member'),\n description: `${tr('workspaces.removeMember.prefix', 'Remove')} ${userId} ${tr('workspaces.removeMember.suffix', 'from this workspace? They will keep their organization membership.')}`,\n confirmText: tr('common.remove', 'Remove'),\n variant: 'danger',\n onConfirm: () => {\n void (async () => {\n try {\n const result = await removeWorkspaceMember(id, userId);\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr('workspaces.removeMember.failed', 'Failed to remove workspace member')\n );\n }\n\n await refetchMembers();\n toast.success(\n tr('workspaces.removeMember.success', 'Workspace member removed successfully')\n );\n } catch (removeError) {\n toast.error(\n removeError instanceof Error\n ? removeError.message\n : tr('workspaces.removeMember.failed', 'Failed to remove workspace member')\n );\n }\n })();\n },\n });\n };\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.WORKSPACE_READ)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.detail.noPermission',\n \"You don't have permission to view this workspace.\"\n )}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('workspaces.detail.loading', 'Loading workspace details...')} />\n </div>\n );\n }\n\n if (error || !workspace) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error?.message || tr('workspaces.detail.notFound', 'Workspace not found')}\n action={{\n label: tr('workspaces.create.backToWorkspaces', 'Back to Workspaces'),\n onClick: () => navigate(`${basePath}/workspaces`),\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"space-y-4\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces`)}\n className=\"inline-flex min-h-10 items-center gap-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:text-[var(--color-text-primary)] active:scale-[0.98]\"\n >\n <ArrowLeft size={20} aria-hidden=\"true\" />\n <span className=\"text-sm font-medium\">\n {tr('workspaces.create.backToWorkspaces', 'Back to Workspaces')}\n </span>\n </button>\n\n <div className=\"flex flex-col gap-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 shadow-[var(--shadow-elevation-1)] lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"flex min-w-0 items-start gap-4\">\n <div className=\"flex size-16 items-center justify-center rounded-xl bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Briefcase size={28} aria-hidden=\"true\" />\n </div>\n\n <div className=\"min-w-0 space-y-2\">\n <div className=\"flex flex-wrap items-start gap-2\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {workspace.name}\n </h1>\n <WorkspaceStatusBadge status={workspace.status} />\n <span className=\"inline-flex items-center rounded-md bg-[var(--color-bg-subtle)] px-2 py-1 text-xs font-medium text-[var(--color-text-secondary)]\">\n {workspace.type}\n </span>\n </div>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-secondary)]\">\n /{workspace.slug}\n </p>\n </div>\n </div>\n\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${workspace.organizationId}`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Users size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.detail.viewOrganization', 'View Organization')}</span>\n </button>\n {hasPermission(PERMISSIONS.WORKSPACE_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/${id}/edit`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Edit size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.edit.title', 'Edit Workspace')}</span>\n </button>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)]\">\n <div className=\"overflow-x-auto border-b border-[var(--color-border-default)]\">\n <div className=\"flex min-w-max gap-2 px-4 pt-4 sm:px-6\">\n {tabItems.map((tabItem) => {\n const isActive = activeTab === tabItem.id;\n return (\n <button\n type=\"button\"\n key={tabItem.id}\n onClick={() => setActiveTab(tabItem.id)}\n className={`inline-flex min-h-11 items-center rounded-t-lg border-b-2 px-4 py-3 text-sm font-medium transition-colors ${\n isActive\n ? 'border-[var(--color-action-primary-bg)] text-[var(--color-action-primary-bg)]'\n : 'border-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'\n }`}\n aria-pressed={isActive}\n >\n {tabItem.label}\n </button>\n );\n })}\n </div>\n </div>\n\n <div className=\"p-4 sm:p-6\">\n {activeTab === 'overview' ? (\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,1fr)_320px]\">\n <section className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('workspaces.detail.detailsSection', 'Workspace details')}\n </h2>\n\n <dl className=\"mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.name', 'Name')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {workspace.name}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.slug', 'Slug')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {workspace.slug}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </dt>\n <dd className=\"mt-1\">\n <WorkspaceStatusBadge status={workspace.status} />\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.column.type', 'Type')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {workspace.type}\n </dd>\n </div>\n <div className=\"sm:col-span-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('members.detail.orgId', 'Organization ID')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {workspace.organizationId}\n </dd>\n </div>\n {workspace.endpoint ? (\n <div className=\"sm:col-span-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('workspaces.detail.endpoint', 'Endpoint')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {workspace.endpoint}\n </dd>\n </div>\n ) : null}\n </dl>\n </section>\n\n <aside className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('common.metadata', 'Metadata')}\n </h2>\n\n <dl className=\"mt-5 space-y-4\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.members', 'Members')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {resolvedMemberCount}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {formatDate(workspace.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.lastUpdated', 'Last updated')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {formatDate(workspace.updatedAt)}\n </dd>\n </div>\n {workspace.region ? (\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('workspaces.detail.region', 'Region')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {workspace.region}\n </dd>\n </div>\n ) : null}\n </dl>\n </aside>\n </div>\n ) : (\n <section className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('workspaces.detail.membersTitle', 'Workspace members')}\n </h2>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.detail.membersSubtitle',\n 'Add organization members to this workspace and manage access.'\n )}\n </p>\n </div>\n {hasPermission(PERMISSIONS.MEMBER_INVITE) && (\n <button\n type=\"button\"\n onClick={() => setIsAddMemberDialogOpen(true)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <UserPlus size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.detail.addMember', 'Add Member')}</span>\n </button>\n )}\n </div>\n\n {membersLoading ? (\n <div className=\"py-8\">\n <LoadingState\n message={tr(\n 'workspaces.detail.loadingMembers',\n 'Loading workspace members...'\n )}\n />\n </div>\n ) : membersError ? (\n <ErrorState\n message={membersError.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetchMembers();\n },\n }}\n />\n ) : members.length > 0 ? (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {members.map((member) => (\n <article\n key={member.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4\"\n >\n <div className=\"space-y-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <ActorIdentity\n profile={\n profileMap[member.userId] ??\n buildActorFallback({\n actorId: member.userId,\n actorType: 'user',\n })\n }\n variant=\"inline\"\n />\n <p className=\"mt-1 text-sm text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}{' '}\n {formatDate(member.joinedAt)}\n </p>\n </div>\n <MemberRoleBadge role={member.role} />\n </div>\n\n <dl className=\"grid grid-cols-1 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-[var(--color-text-primary)]\">\n {member.invitedBy || '—'}\n </dd>\n </div>\n </dl>\n\n <button\n type=\"button\"\n onClick={() => handleRemoveMember(member.userId)}\n disabled={removingMember}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-sm text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('common.remove', 'Remove')}\n </button>\n </div>\n </article>\n ))}\n </div>\n\n <div className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] md:block\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.detail.userId', 'User ID')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {members.map((member) => (\n <tr\n key={member.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4\">\n <ActorIdentity\n profile={\n profileMap[member.userId] ??\n buildActorFallback({\n actorId: member.userId,\n actorType: 'user',\n })\n }\n variant=\"cell\"\n />\n </td>\n <td className=\"px-6 py-4\">\n <MemberRoleBadge role={member.role} />\n </td>\n <td className=\"px-6 py-4 text-sm text-[var(--color-text-secondary)]\">\n {formatDate(member.joinedAt)}\n </td>\n <td className=\"px-6 py-4 break-all font-mono text-sm text-[var(--color-text-secondary)]\">\n {member.invitedBy || '—'}\n </td>\n <td className=\"px-6 py-4 text-right\">\n <button\n type=\"button\"\n onClick={() => handleRemoveMember(member.userId)}\n disabled={removingMember}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label={`${tr('workspaces.removeMember.ariaPrefix', 'Remove')} ${member.userId} ${tr('workspaces.removeMember.ariaSuffix', 'from workspace')}`}\n title={`${tr('workspaces.removeMember.ariaPrefix', 'Remove')} ${member.userId} ${tr('workspaces.removeMember.ariaSuffix', 'from workspace')}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n ) : (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-6\">\n <EmptyState\n icon={<Users size={40} aria-hidden=\"true\" />}\n title={tr('workspaces.detail.emptyMembersTitle', 'No workspace members yet')}\n description={tr(\n 'workspaces.detail.emptyMembersDescription',\n 'Add people from the parent organization to grant workspace access.'\n )}\n action={{\n label: tr('workspaces.detail.addMember', 'Add Member'),\n onClick: () => setIsAddMemberDialogOpen(true),\n }}\n />\n </div>\n )}\n </section>\n )}\n </div>\n </div>\n </div>\n\n <AddWorkspaceMemberDialog\n open={isAddMemberDialogOpen}\n onOpenChange={setIsAddMemberDialogOpen}\n workspaceId={workspace.id}\n organizationId={workspace.organizationId}\n existingMembers={members}\n onInviteOrganizationMember={() =>\n navigate(`${basePath}/${workspace.organizationId}/members/invite`)\n }\n />\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WorkspaceDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/pages/WorkspaceDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Briefcase, Edit, Trash2, UserPlus, Users } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n ActorIdentity,\n buildActorFallback,\n useActorProfiles,\n type ActorInput,\n} from '@burdenoff/fe-libs/shared';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useWorkspace } from '../hooks/useWorkspaces';\nimport { useRemoveWorkspaceMember, useWorkspaceMembers } from '../hooks/useWorkspaceMembers';\nimport { WorkspaceStatusBadge } from '../components/WorkspaceStatusBadge';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { MemberRoleBadge } from '../../members/components/MemberRoleBadge';\nimport { AddWorkspaceMemberDialog } from '../components/AddWorkspaceMemberDialog';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ntype ActiveTab = 'overview' | 'members';\n\nfunction formatDate(dateString: string) {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport function WorkspaceDetailPage() {\n const tr = useTr();\n const { id } = useParams<{ id: string }>();\n const { navigate, basePath = '/organizations', apiGatewayUrl, authToken } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { workspace, loading, error } = useWorkspace(id ?? '');\n const [activeTab, setActiveTab] = useState<ActiveTab>('overview');\n const [isAddMemberDialogOpen, setIsAddMemberDialogOpen] = useState(false);\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n const shouldFetchMembers = activeTab === 'members' && Boolean(id);\n const {\n members,\n loading: membersLoading,\n error: membersError,\n refetch: refetchMembers,\n } = useWorkspaceMembers(shouldFetchMembers ? (id ?? '') : '', 50, 0);\n const { removeWorkspaceMember, loading: removingMember } = useRemoveWorkspaceMember();\n const resolvedMemberCount =\n members.length > 0 || activeTab === 'members' ? members.length : (workspace?.memberCount ?? 0);\n\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [members]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: workspace?.organizationId,\n workspaceId: id,\n });\n\n const tabItems = useMemo(\n () => [\n { id: 'overview' as const, label: tr('workspaces.detail.tab.overview', 'Overview') },\n {\n id: 'members' as const,\n label: `${tr('workspaces.detail.tab.members', 'Members')} (${resolvedMemberCount})`,\n },\n ],\n [resolvedMemberCount, tr]\n );\n\n const handleRemoveMember = (userId: string) => {\n if (!id) return;\n\n openConfirmDialog({\n title: tr('workspaces.removeMember.title', 'Remove workspace member'),\n description: `${tr('workspaces.removeMember.prefix', 'Remove')} ${userId} ${tr('workspaces.removeMember.suffix', 'from this workspace? They will keep their organization membership.')}`,\n confirmText: tr('common.remove', 'Remove'),\n variant: 'danger',\n onConfirm: () => {\n void (async () => {\n try {\n const result = await removeWorkspaceMember(id, userId);\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr('workspaces.removeMember.failed', 'Failed to remove workspace member')\n );\n }\n\n await refetchMembers();\n toast.success(\n tr('workspaces.removeMember.success', 'Workspace member removed successfully')\n );\n } catch (removeError) {\n toast.error(\n removeError instanceof Error\n ? removeError.message\n : tr('workspaces.removeMember.failed', 'Failed to remove workspace member')\n );\n }\n })();\n },\n });\n };\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.WORKSPACE_READ)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.detail.noPermission',\n \"You don't have permission to view this workspace.\"\n )}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('workspaces.detail.loading', 'Loading workspace details...')} />\n </div>\n );\n }\n\n if (error || !workspace) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error?.message || tr('workspaces.detail.notFound', 'Workspace not found')}\n action={{\n label: tr('workspaces.create.backToWorkspaces', 'Back to Workspaces'),\n onClick: () => navigate(`${basePath}/workspaces`),\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"space-y-4\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces`)}\n className=\"inline-flex min-h-10 items-center gap-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:text-[var(--color-text-primary)] active:scale-[0.98]\"\n >\n <ArrowLeft size={20} aria-hidden=\"true\" />\n <span className=\"text-sm font-medium\">\n {tr('workspaces.create.backToWorkspaces', 'Back to Workspaces')}\n </span>\n </button>\n\n <div className=\"flex flex-col gap-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 shadow-[var(--shadow-elevation-1)] lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"flex min-w-0 items-start gap-4\">\n <div className=\"flex size-16 items-center justify-center rounded-xl bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Briefcase size={28} aria-hidden=\"true\" />\n </div>\n\n <div className=\"min-w-0 space-y-2\">\n <div className=\"flex flex-wrap items-start gap-2\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {workspace.name}\n </h1>\n <WorkspaceStatusBadge status={workspace.status} />\n </div>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-secondary)]\">\n /{workspace.slug}\n </p>\n </div>\n </div>\n\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${workspace.organizationId}`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Users size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.detail.viewOrganization', 'View Organization')}</span>\n </button>\n {hasPermission(PERMISSIONS.WORKSPACE_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/${id}/edit`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Edit size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.edit.title', 'Edit Workspace')}</span>\n </button>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)]\">\n <div className=\"overflow-x-auto border-b border-[var(--color-border-default)]\">\n <div className=\"flex min-w-max gap-2 px-4 pt-4 sm:px-6\">\n {tabItems.map((tabItem) => {\n const isActive = activeTab === tabItem.id;\n return (\n <button\n type=\"button\"\n key={tabItem.id}\n onClick={() => setActiveTab(tabItem.id)}\n className={`inline-flex min-h-11 items-center rounded-t-lg border-b-2 px-4 py-3 text-sm font-medium transition-colors ${\n isActive\n ? 'border-[var(--color-action-primary-bg)] text-[var(--color-action-primary-bg)]'\n : 'border-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'\n }`}\n aria-pressed={isActive}\n >\n {tabItem.label}\n </button>\n );\n })}\n </div>\n </div>\n\n <div className=\"p-4 sm:p-6\">\n {activeTab === 'overview' ? (\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,1fr)_320px]\">\n <section className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('workspaces.detail.detailsSection', 'Workspace details')}\n </h2>\n\n <dl className=\"mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.name', 'Name')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {workspace.name}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.slug', 'Slug')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {workspace.slug}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </dt>\n <dd className=\"mt-1\">\n <WorkspaceStatusBadge status={workspace.status} />\n </dd>\n </div>\n <div className=\"sm:col-span-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('members.detail.orgId', 'Organization ID')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {workspace.organizationId}\n </dd>\n </div>\n {workspace.endpoint ? (\n <div className=\"sm:col-span-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('workspaces.detail.endpoint', 'Endpoint')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {workspace.endpoint}\n </dd>\n </div>\n ) : null}\n </dl>\n </section>\n\n <aside className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('common.metadata', 'Metadata')}\n </h2>\n\n <dl className=\"mt-5 space-y-4\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.members', 'Members')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {resolvedMemberCount}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {formatDate(workspace.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.lastUpdated', 'Last updated')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {formatDate(workspace.updatedAt)}\n </dd>\n </div>\n {workspace.region ? (\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('workspaces.detail.region', 'Region')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {workspace.region}\n </dd>\n </div>\n ) : null}\n </dl>\n </aside>\n </div>\n ) : (\n <section className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('workspaces.detail.membersTitle', 'Workspace members')}\n </h2>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.detail.membersSubtitle',\n 'Add organization members to this workspace and manage access.'\n )}\n </p>\n </div>\n {hasPermission(PERMISSIONS.MEMBER_INVITE) && (\n <button\n type=\"button\"\n onClick={() => setIsAddMemberDialogOpen(true)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <UserPlus size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.detail.addMember', 'Add Member')}</span>\n </button>\n )}\n </div>\n\n {membersLoading ? (\n <div className=\"py-8\">\n <LoadingState\n message={tr(\n 'workspaces.detail.loadingMembers',\n 'Loading workspace members...'\n )}\n />\n </div>\n ) : membersError ? (\n <ErrorState\n message={membersError.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetchMembers();\n },\n }}\n />\n ) : members.length > 0 ? (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {members.map((member) => (\n <article\n key={member.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4\"\n >\n <div className=\"space-y-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <ActorIdentity\n profile={\n profileMap[member.userId] ??\n buildActorFallback({\n actorId: member.userId,\n actorType: 'user',\n })\n }\n variant=\"inline\"\n />\n <p className=\"mt-1 text-sm text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}{' '}\n {formatDate(member.joinedAt)}\n </p>\n </div>\n <MemberRoleBadge role={member.role} />\n </div>\n\n <dl className=\"grid grid-cols-1 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-[var(--color-text-primary)]\">\n {member.invitedBy || '—'}\n </dd>\n </div>\n </dl>\n\n <button\n type=\"button\"\n onClick={() => handleRemoveMember(member.userId)}\n disabled={removingMember}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-sm text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('common.remove', 'Remove')}\n </button>\n </div>\n </article>\n ))}\n </div>\n\n <div className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] md:block\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.detail.userId', 'User ID')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {members.map((member) => (\n <tr\n key={member.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4\">\n <ActorIdentity\n profile={\n profileMap[member.userId] ??\n buildActorFallback({\n actorId: member.userId,\n actorType: 'user',\n })\n }\n variant=\"cell\"\n />\n </td>\n <td className=\"px-6 py-4\">\n <MemberRoleBadge role={member.role} />\n </td>\n <td className=\"px-6 py-4 text-sm text-[var(--color-text-secondary)]\">\n {formatDate(member.joinedAt)}\n </td>\n <td className=\"px-6 py-4 break-all font-mono text-sm text-[var(--color-text-secondary)]\">\n {member.invitedBy || '—'}\n </td>\n <td className=\"px-6 py-4 text-right\">\n <button\n type=\"button\"\n onClick={() => handleRemoveMember(member.userId)}\n disabled={removingMember}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label={`${tr('workspaces.removeMember.ariaPrefix', 'Remove')} ${member.userId} ${tr('workspaces.removeMember.ariaSuffix', 'from workspace')}`}\n title={`${tr('workspaces.removeMember.ariaPrefix', 'Remove')} ${member.userId} ${tr('workspaces.removeMember.ariaSuffix', 'from workspace')}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n ) : (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-6\">\n <EmptyState\n icon={<Users size={40} aria-hidden=\"true\" />}\n title={tr('workspaces.detail.emptyMembersTitle', 'No workspace members yet')}\n description={tr(\n 'workspaces.detail.emptyMembersDescription',\n 'Add people from the parent organization to grant workspace access.'\n )}\n action={{\n label: tr('workspaces.detail.addMember', 'Add Member'),\n onClick: () => setIsAddMemberDialogOpen(true),\n }}\n />\n </div>\n )}\n </section>\n )}\n </div>\n </div>\n </div>\n\n <AddWorkspaceMemberDialog\n open={isAddMemberDialogOpen}\n onOpenChange={setIsAddMemberDialogOpen}\n workspaceId={workspace.id}\n organizationId={workspace.organizationId}\n existingMembers={members}\n onInviteOrganizationMember={() =>\n navigate(`${basePath}/${workspace.organizationId}/members/invite`)\n }\n />\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -281,17 +281,10 @@ function E({ mode: E = "my" } = {}) {
|
|
|
281
281
|
className: "min-w-0 flex-1 space-y-3",
|
|
282
282
|
children: [/* @__PURE__ */ w("div", { children: [/* @__PURE__ */ w("div", {
|
|
283
283
|
className: "flex flex-wrap items-start gap-2",
|
|
284
|
-
children: [
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}),
|
|
289
|
-
/* @__PURE__ */ C(h, { status: e.status }),
|
|
290
|
-
/* @__PURE__ */ C("span", {
|
|
291
|
-
className: "inline-flex items-center rounded-md bg-[var(--color-bg-subtle)] px-2 py-1 text-xs font-medium text-[var(--color-text-secondary)]",
|
|
292
|
-
children: e.type
|
|
293
|
-
})
|
|
294
|
-
]
|
|
284
|
+
children: [/* @__PURE__ */ C("h2", {
|
|
285
|
+
className: "text-base font-semibold text-[var(--color-text-primary)]",
|
|
286
|
+
children: e.name
|
|
287
|
+
}), /* @__PURE__ */ C(h, { status: e.status })]
|
|
295
288
|
}), /* @__PURE__ */ w("p", {
|
|
296
289
|
className: "mt-1 break-all font-mono text-sm text-[var(--color-text-muted)]",
|
|
297
290
|
children: ["/", e.slug]
|
|
@@ -372,10 +365,6 @@ function E({ mode: E = "my" } = {}) {
|
|
|
372
365
|
className: "px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]",
|
|
373
366
|
children: D("common.status", "Status")
|
|
374
367
|
}),
|
|
375
|
-
/* @__PURE__ */ C("th", {
|
|
376
|
-
className: "px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]",
|
|
377
|
-
children: D("workspaces.list.column.type", "Type")
|
|
378
|
-
}),
|
|
379
368
|
/* @__PURE__ */ C("th", {
|
|
380
369
|
className: "px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]",
|
|
381
370
|
children: D("common.created", "Created")
|
|
@@ -412,13 +401,6 @@ function E({ mode: E = "my" } = {}) {
|
|
|
412
401
|
className: "px-6 py-4 align-top",
|
|
413
402
|
children: /* @__PURE__ */ C(h, { status: e.status })
|
|
414
403
|
}),
|
|
415
|
-
/* @__PURE__ */ C("td", {
|
|
416
|
-
className: "px-6 py-4 align-top",
|
|
417
|
-
children: /* @__PURE__ */ C("span", {
|
|
418
|
-
className: "inline-flex items-center rounded-md bg-[var(--color-bg-subtle)] px-2 py-1 text-xs font-medium text-[var(--color-text-secondary)]",
|
|
419
|
-
children: e.type
|
|
420
|
-
})
|
|
421
|
-
}),
|
|
422
404
|
/* @__PURE__ */ C("td", {
|
|
423
405
|
className: "px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]",
|
|
424
406
|
children: T(e.createdAt)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspacesListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/pages/WorkspacesListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Briefcase, Eye, Plus, RefreshCw, SquarePen, Trash2 } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useDeleteWorkspace, useWorkspaces } from '../hooks/useWorkspaces';\nimport { WorkspaceStatusBadge } from '../components/WorkspaceStatusBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport type { WorkspaceFilterInput } from '../types';\nimport { WorkspaceStatus } from '../types';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../../../utils/nativeBridge';\n\nfunction formatDate(date: string) {\n return new Date(date).toLocaleDateString();\n}\n\n/**\n * `mode` controls the scope of the listing:\n * - `'my'` — membership-scoped (`myWorkspaces`); the default for user-facing\n * app shells. A regular user only sees workspaces they belong to.\n * - `'admin'` — platform-wide (`allWorkspaces`, super-admin only); the admin app.\n */\nexport interface WorkspacesListPageProps {\n mode?: 'my' | 'admin';\n}\n\nexport function WorkspacesListPage({ mode = 'my' }: WorkspacesListPageProps = {}) {\n const tr = useTr();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);\n const [filter, setFilter] = useState<WorkspaceFilterInput>({});\n const [selectedWorkspaceIds, setSelectedWorkspaceIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n // 'my' shells call the self-scoped `myWorkspaces` (no server-side filter arg),\n // so we over-fetch a page and filter client-side below. The admin app keeps\n // the server-filtered, paginated `allWorkspaces`.\n const scope = mode === 'admin' ? 'all' : 'my';\n const {\n workspaces: fetchedWorkspaces,\n loading,\n error,\n refetch,\n } = useWorkspaces(\n { ...filter, search: debouncedSearchQuery || undefined },\n scope === 'my' ? 100 : 20,\n 0,\n scope\n );\n\n const workspaces = useMemo(() => {\n if (scope !== 'my') return fetchedWorkspaces;\n const query = debouncedSearchQuery.trim().toLowerCase();\n return fetchedWorkspaces.filter((workspace) => {\n const matchesSearch =\n !query ||\n workspace.name.toLowerCase().includes(query) ||\n (workspace.slug?.toLowerCase().includes(query) ?? false);\n const matchesStatus = !filter.status || workspace.status === filter.status;\n return matchesSearch && matchesStatus;\n });\n }, [fetchedWorkspaces, scope, debouncedSearchQuery, filter.status]);\n\n const { deleteWorkspace, loading: deleteLoading } = useDeleteWorkspace();\n\n useEffect(() => {\n setSelectedWorkspaceIds((previousSelection) => {\n const visibleIds = new Set(workspaces.map((workspace) => workspace.id));\n const nextSelection = new Set(\n [...previousSelection].filter((workspaceId) => visibleIds.has(workspaceId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [workspaces]);\n\n const selectedWorkspaces = useMemo(\n () => workspaces.filter((workspace) => selectedWorkspaceIds.has(workspace.id)),\n [selectedWorkspaceIds, workspaces]\n );\n\n const allVisibleSelected =\n workspaces.length > 0 &&\n workspaces.every((workspace) => selectedWorkspaceIds.has(workspace.id));\n const partiallySelected =\n workspaces.some((workspace) => selectedWorkspaceIds.has(workspace.id)) && !allVisibleSelected;\n\n const executeSuspend = async (workspaceIds: string[]) => {\n try {\n const results = await Promise.allSettled(\n workspaceIds.map(async (workspaceId) => {\n const result = await deleteWorkspace(workspaceId);\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr('workspaces.suspend.failed', 'Failed to suspend workspace')\n );\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedWorkspaceIds(new Set());\n await refetch();\n const successMsg =\n workspaceIds.length === 1\n ? tr('workspaces.suspend.successSingle', 'Workspace suspended successfully')\n : `${workspaceIds.length} ${tr('workspaces.suspend.successMany', 'workspaces suspended successfully')}`;\n toast.success(successMsg);\n void nativeNotify('success');\n if (isNative()) void nativeToast(successMsg);\n } catch (suspendError) {\n const errorMsg =\n suspendError instanceof Error\n ? suspendError.message\n : tr('workspaces.suspend.failed', 'Failed to suspend workspace');\n toast.error(errorMsg);\n void nativeNotify('error');\n if (isNative()) void nativeToast(errorMsg);\n }\n };\n\n const handleDelete = (workspaceId: string, workspaceName: string) => {\n nativeImpact('medium');\n const title = tr('workspaces.suspend.title', 'Suspend workspace');\n const description = `${tr('workspaces.suspend.prefix', 'Suspend')} ${workspaceName}? ${tr(\n 'workspaces.suspend.suffix',\n 'Workspace members will lose access until it is reactivated.'\n )}`;\n void nativeConfirm({\n title,\n message: description,\n okButtonTitle: tr('workspaces.suspend.confirm', 'Suspend'),\n }).then((nativeResult) => {\n if (nativeResult === null) {\n openConfirmDialog({\n title,\n description,\n confirmText: tr('workspaces.suspend.confirm', 'Suspend'),\n variant: 'warning',\n onConfirm: () => {\n void executeSuspend([workspaceId]);\n },\n });\n } else if (nativeResult) {\n void executeSuspend([workspaceId]);\n }\n });\n };\n\n const handleBulkDelete = () => {\n if (selectedWorkspaces.length === 0) return;\n\n openConfirmDialog({\n title: tr('workspaces.suspend.bulkTitle', 'Suspend selected workspaces'),\n description: `${tr('workspaces.suspend.prefix', 'Suspend')} ${selectedWorkspaces.length} ${tr(\n selectedWorkspaces.length === 1\n ? 'workspaces.suspend.bulkSuffixSingle'\n : 'workspaces.suspend.bulkSuffixMany',\n selectedWorkspaces.length === 1 ? 'selected workspace?' : 'selected workspaces?'\n )}`,\n confirmText: `${tr('workspaces.suspend.confirm', 'Suspend')} ${selectedWorkspaces.length}`,\n variant: 'warning',\n onConfirm: () => {\n void executeSuspend(selectedWorkspaces.map((workspace) => workspace.id));\n },\n });\n };\n\n const toggleWorkspaceSelection = (workspaceId: string, checked: boolean) => {\n setSelectedWorkspaceIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(workspaceId);\n } else {\n nextSelection.delete(workspaceId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllWorkspaces = (checked: boolean) => {\n setSelectedWorkspaceIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n workspaces.forEach((workspace) => {\n if (checked) {\n nextSelection.add(workspace.id);\n } else {\n nextSelection.delete(workspace.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.WORKSPACE_LIST)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.noPermission', \"You don't have permission to view workspaces.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('workspaces.list.loading', 'Loading workspaces...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('workspaces.list.title', 'Workspaces')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.list.subtitle',\n 'Manage organization workspaces, review their status, and open the workspace detail flow.'\n )}\n </p>\n </div>\n\n {hasPermission(PERMISSIONS.WORKSPACE_CREATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/new`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Plus size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('workspaces.create.button', 'Create Workspace')}\n </span>\n </button>\n )}\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('workspaces.list.searchPlaceholder', 'Search workspaces...')}\n label={tr('workspaces.list.searchLabel', 'Search workspaces')}\n inputId=\"workspaces-search\"\n className=\"flex-1\"\n />\n\n <label className=\"flex min-h-11 min-w-[180px] flex-col justify-center rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-sm text-[var(--color-text-secondary)]\">\n <span className=\"sr-only\">\n {tr('workspaces.list.filterByStatus', 'Filter by workspace status')}\n </span>\n <select\n value={filter.status || ''}\n onChange={(event) =>\n setFilter({\n ...filter,\n status: event.target.value\n ? (event.target.value as WorkspaceStatus)\n : undefined,\n })\n }\n className=\"bg-transparent text-[var(--color-text-primary)] focus:outline-none\"\n aria-label={tr('workspaces.list.filterByStatus', 'Filter by workspace status')}\n >\n <option value=\"\">{tr('workspaces.list.allStatuses', 'All statuses')}</option>\n <option value={WorkspaceStatus.ACTIVE}>\n {tr('workspaces.status.active', 'Active')}\n </option>\n <option value={WorkspaceStatus.INACTIVE}>\n {tr('workspaces.status.inactive', 'Inactive')}\n </option>\n <option value={WorkspaceStatus.ARCHIVED}>\n {tr('workspaces.status.archived', 'Archived')}\n </option>\n </select>\n </label>\n\n <button\n type=\"button\"\n onClick={() => {\n void refetch();\n }}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-95\"\n aria-label={tr('workspaces.list.refresh', 'Refresh workspaces')}\n title={tr('workspaces.list.refresh', 'Refresh workspaces')}\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {workspaces.length > 0 ? (\n <div className=\"mt-4 flex flex-col gap-3 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-[var(--color-text-primary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllWorkspaces}\n ariaLabel={tr('workspaces.list.selectAll', 'Select all workspaces')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedWorkspaceIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedWorkspaceIds(new Set())}\n className=\"text-sm text-[var(--color-text-secondary)] transition-colors hover:text-[var(--color-text-primary)]\"\n >\n {tr('common.clearSelection', 'Clear selection')}\n </button>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\n {selectedWorkspaceIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.WORKSPACE_DELETE) && (\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={selectedWorkspaces.length === 0 || deleteLoading}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-status-warning-border)] px-3 py-2 text-[var(--color-status-warning-text)] transition-all duration-200 hover:bg-[var(--color-status-warning-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.list.suspendSelected', 'Suspend Selected')}</span>\n {selectedWorkspaces.length > 0 ? (\n <span>({selectedWorkspaces.length})</span>\n ) : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {workspaces.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 shadow-[var(--shadow-elevation-1)]\">\n <EmptyState\n icon={<Briefcase size={40} aria-hidden=\"true\" />}\n title={tr('workspaces.list.emptyNoneTitle', 'No workspaces found')}\n description={\n hasPermission(PERMISSIONS.WORKSPACE_CREATE)\n ? tr(\n 'workspaces.list.emptyNoneDescription',\n 'Create a workspace to start mapping members, organization ownership, and collaboration settings.'\n )\n : tr(\n 'workspaces.list.emptyNoneDescriptionViewer',\n 'No workspaces have been created yet.'\n )\n }\n action={\n hasPermission(PERMISSIONS.WORKSPACE_CREATE)\n ? {\n label: tr('workspaces.create.button', 'Create Workspace'),\n onClick: () => navigate(`${basePath}/workspaces/new`),\n }\n : undefined\n }\n />\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {workspaces.map((workspace) => (\n <article\n key={workspace.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"flex items-start gap-3\">\n <SelectionCheckbox\n checked={selectedWorkspaceIds.has(workspace.id)}\n onChange={(checked) => toggleWorkspaceSelection(workspace.id, checked)}\n ariaLabel={`${tr('workspaces.list.selectOne', 'Select workspace')} ${workspace.name}`}\n />\n\n <div className=\"min-w-0 flex-1 space-y-3\">\n <div>\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {workspace.name}\n </h2>\n <WorkspaceStatusBadge status={workspace.status} />\n <span className=\"inline-flex items-center rounded-md bg-[var(--color-bg-subtle)] px-2 py-1 text-xs font-medium text-[var(--color-text-secondary)]\">\n {workspace.type}\n </span>\n </div>\n <p className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{workspace.slug}\n </p>\n </div>\n\n <dl className=\"grid grid-cols-2 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.members', 'Members')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {workspace.memberCount ?? 0}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {formatDate(workspace.createdAt)}\n </dd>\n </div>\n </dl>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-2 border-t border-[var(--color-border-default)] pt-4\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/${workspace.id}`)}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Eye size={16} aria-hidden=\"true\" />\n {tr('common.view', 'View')}\n </button>\n {hasPermission(PERMISSIONS.WORKSPACE_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/${workspace.id}/edit`)}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <SquarePen size={16} aria-hidden=\"true\" />\n {tr('common.edit', 'Edit')}\n </button>\n )}\n {hasPermission(PERMISSIONS.WORKSPACE_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(workspace.id, workspace.name)}\n disabled={deleteLoading}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-warning-border)] px-3 py-2 text-sm text-[var(--color-status-warning-text)] transition-all duration-200 hover:bg-[var(--color-status-warning-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('workspaces.suspend.confirm', 'Suspend')}\n </button>\n )}\n </div>\n </article>\n ))}\n </div>\n\n <div className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)] md:block\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"w-12 px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllWorkspaces}\n ariaLabel={tr('workspaces.list.selectAll', 'Select all workspaces')}\n />\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.column.workspace', 'Workspace')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.column.type', 'Type')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {workspaces.map((workspace) => (\n <tr\n key={workspace.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedWorkspaceIds.has(workspace.id)}\n onChange={(checked) => toggleWorkspaceSelection(workspace.id, checked)}\n ariaLabel={`${tr('workspaces.list.selectOne', 'Select workspace')} ${workspace.name}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div>\n <p className=\"font-medium text-[var(--color-text-primary)]\">\n {workspace.name}\n </p>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{workspace.slug}\n </p>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <WorkspaceStatusBadge status={workspace.status} />\n </td>\n <td className=\"px-6 py-4 align-top\">\n <span className=\"inline-flex items-center rounded-md bg-[var(--color-bg-subtle)] px-2 py-1 text-xs font-medium text-[var(--color-text-secondary)]\">\n {workspace.type}\n </span>\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(workspace.createdAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/${workspace.id}`)}\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n title={`${tr('common.view', 'View')} ${workspace.name}`}\n aria-label={`${tr('common.view', 'View')} ${workspace.name}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.WORKSPACE_UPDATE) && (\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/workspaces/${workspace.id}/edit`)\n }\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n title={`${tr('common.edit', 'Edit')} ${workspace.name}`}\n aria-label={`${tr('common.edit', 'Edit')} ${workspace.name}`}\n >\n <SquarePen size={18} aria-hidden=\"true\" />\n </button>\n )}\n {hasPermission(PERMISSIONS.WORKSPACE_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(workspace.id, workspace.name)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-warning-text)] transition-all duration-200 hover:bg-[var(--color-status-warning-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n title={`${tr('workspaces.suspend.confirm', 'Suspend')} ${workspace.name}`}\n aria-label={`${tr('workspaces.suspend.confirm', 'Suspend')} ${workspace.name}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WorkspacesListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/pages/WorkspacesListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Briefcase, Eye, Plus, RefreshCw, SquarePen, Trash2 } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useDeleteWorkspace, useWorkspaces } from '../hooks/useWorkspaces';\nimport { WorkspaceStatusBadge } from '../components/WorkspaceStatusBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport type { WorkspaceFilterInput } from '../types';\nimport { WorkspaceStatus } from '../types';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../../../utils/nativeBridge';\n\nfunction formatDate(date: string) {\n return new Date(date).toLocaleDateString();\n}\n\n/**\n * `mode` controls the scope of the listing:\n * - `'my'` — membership-scoped (`myWorkspaces`); the default for user-facing\n * app shells. A regular user only sees workspaces they belong to.\n * - `'admin'` — platform-wide (`allWorkspaces`, super-admin only); the admin app.\n */\nexport interface WorkspacesListPageProps {\n mode?: 'my' | 'admin';\n}\n\nexport function WorkspacesListPage({ mode = 'my' }: WorkspacesListPageProps = {}) {\n const tr = useTr();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);\n const [filter, setFilter] = useState<WorkspaceFilterInput>({});\n const [selectedWorkspaceIds, setSelectedWorkspaceIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n // 'my' shells call the self-scoped `myWorkspaces` (no server-side filter arg),\n // so we over-fetch a page and filter client-side below. The admin app keeps\n // the server-filtered, paginated `allWorkspaces`.\n const scope = mode === 'admin' ? 'all' : 'my';\n const {\n workspaces: fetchedWorkspaces,\n loading,\n error,\n refetch,\n } = useWorkspaces(\n { ...filter, search: debouncedSearchQuery || undefined },\n scope === 'my' ? 100 : 20,\n 0,\n scope\n );\n\n const workspaces = useMemo(() => {\n if (scope !== 'my') return fetchedWorkspaces;\n const query = debouncedSearchQuery.trim().toLowerCase();\n return fetchedWorkspaces.filter((workspace) => {\n const matchesSearch =\n !query ||\n workspace.name.toLowerCase().includes(query) ||\n (workspace.slug?.toLowerCase().includes(query) ?? false);\n const matchesStatus = !filter.status || workspace.status === filter.status;\n return matchesSearch && matchesStatus;\n });\n }, [fetchedWorkspaces, scope, debouncedSearchQuery, filter.status]);\n\n const { deleteWorkspace, loading: deleteLoading } = useDeleteWorkspace();\n\n useEffect(() => {\n setSelectedWorkspaceIds((previousSelection) => {\n const visibleIds = new Set(workspaces.map((workspace) => workspace.id));\n const nextSelection = new Set(\n [...previousSelection].filter((workspaceId) => visibleIds.has(workspaceId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [workspaces]);\n\n const selectedWorkspaces = useMemo(\n () => workspaces.filter((workspace) => selectedWorkspaceIds.has(workspace.id)),\n [selectedWorkspaceIds, workspaces]\n );\n\n const allVisibleSelected =\n workspaces.length > 0 &&\n workspaces.every((workspace) => selectedWorkspaceIds.has(workspace.id));\n const partiallySelected =\n workspaces.some((workspace) => selectedWorkspaceIds.has(workspace.id)) && !allVisibleSelected;\n\n const executeSuspend = async (workspaceIds: string[]) => {\n try {\n const results = await Promise.allSettled(\n workspaceIds.map(async (workspaceId) => {\n const result = await deleteWorkspace(workspaceId);\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr('workspaces.suspend.failed', 'Failed to suspend workspace')\n );\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedWorkspaceIds(new Set());\n await refetch();\n const successMsg =\n workspaceIds.length === 1\n ? tr('workspaces.suspend.successSingle', 'Workspace suspended successfully')\n : `${workspaceIds.length} ${tr('workspaces.suspend.successMany', 'workspaces suspended successfully')}`;\n toast.success(successMsg);\n void nativeNotify('success');\n if (isNative()) void nativeToast(successMsg);\n } catch (suspendError) {\n const errorMsg =\n suspendError instanceof Error\n ? suspendError.message\n : tr('workspaces.suspend.failed', 'Failed to suspend workspace');\n toast.error(errorMsg);\n void nativeNotify('error');\n if (isNative()) void nativeToast(errorMsg);\n }\n };\n\n const handleDelete = (workspaceId: string, workspaceName: string) => {\n nativeImpact('medium');\n const title = tr('workspaces.suspend.title', 'Suspend workspace');\n const description = `${tr('workspaces.suspend.prefix', 'Suspend')} ${workspaceName}? ${tr(\n 'workspaces.suspend.suffix',\n 'Workspace members will lose access until it is reactivated.'\n )}`;\n void nativeConfirm({\n title,\n message: description,\n okButtonTitle: tr('workspaces.suspend.confirm', 'Suspend'),\n }).then((nativeResult) => {\n if (nativeResult === null) {\n openConfirmDialog({\n title,\n description,\n confirmText: tr('workspaces.suspend.confirm', 'Suspend'),\n variant: 'warning',\n onConfirm: () => {\n void executeSuspend([workspaceId]);\n },\n });\n } else if (nativeResult) {\n void executeSuspend([workspaceId]);\n }\n });\n };\n\n const handleBulkDelete = () => {\n if (selectedWorkspaces.length === 0) return;\n\n openConfirmDialog({\n title: tr('workspaces.suspend.bulkTitle', 'Suspend selected workspaces'),\n description: `${tr('workspaces.suspend.prefix', 'Suspend')} ${selectedWorkspaces.length} ${tr(\n selectedWorkspaces.length === 1\n ? 'workspaces.suspend.bulkSuffixSingle'\n : 'workspaces.suspend.bulkSuffixMany',\n selectedWorkspaces.length === 1 ? 'selected workspace?' : 'selected workspaces?'\n )}`,\n confirmText: `${tr('workspaces.suspend.confirm', 'Suspend')} ${selectedWorkspaces.length}`,\n variant: 'warning',\n onConfirm: () => {\n void executeSuspend(selectedWorkspaces.map((workspace) => workspace.id));\n },\n });\n };\n\n const toggleWorkspaceSelection = (workspaceId: string, checked: boolean) => {\n setSelectedWorkspaceIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(workspaceId);\n } else {\n nextSelection.delete(workspaceId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllWorkspaces = (checked: boolean) => {\n setSelectedWorkspaceIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n workspaces.forEach((workspace) => {\n if (checked) {\n nextSelection.add(workspace.id);\n } else {\n nextSelection.delete(workspace.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.WORKSPACE_LIST)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.noPermission', \"You don't have permission to view workspaces.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('workspaces.list.loading', 'Loading workspaces...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('workspaces.list.title', 'Workspaces')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.list.subtitle',\n 'Manage organization workspaces, review their status, and open the workspace detail flow.'\n )}\n </p>\n </div>\n\n {hasPermission(PERMISSIONS.WORKSPACE_CREATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/new`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Plus size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('workspaces.create.button', 'Create Workspace')}\n </span>\n </button>\n )}\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('workspaces.list.searchPlaceholder', 'Search workspaces...')}\n label={tr('workspaces.list.searchLabel', 'Search workspaces')}\n inputId=\"workspaces-search\"\n className=\"flex-1\"\n />\n\n <label className=\"flex min-h-11 min-w-[180px] flex-col justify-center rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-sm text-[var(--color-text-secondary)]\">\n <span className=\"sr-only\">\n {tr('workspaces.list.filterByStatus', 'Filter by workspace status')}\n </span>\n <select\n value={filter.status || ''}\n onChange={(event) =>\n setFilter({\n ...filter,\n status: event.target.value\n ? (event.target.value as WorkspaceStatus)\n : undefined,\n })\n }\n className=\"bg-transparent text-[var(--color-text-primary)] focus:outline-none\"\n aria-label={tr('workspaces.list.filterByStatus', 'Filter by workspace status')}\n >\n <option value=\"\">{tr('workspaces.list.allStatuses', 'All statuses')}</option>\n <option value={WorkspaceStatus.ACTIVE}>\n {tr('workspaces.status.active', 'Active')}\n </option>\n <option value={WorkspaceStatus.INACTIVE}>\n {tr('workspaces.status.inactive', 'Inactive')}\n </option>\n <option value={WorkspaceStatus.ARCHIVED}>\n {tr('workspaces.status.archived', 'Archived')}\n </option>\n </select>\n </label>\n\n <button\n type=\"button\"\n onClick={() => {\n void refetch();\n }}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-95\"\n aria-label={tr('workspaces.list.refresh', 'Refresh workspaces')}\n title={tr('workspaces.list.refresh', 'Refresh workspaces')}\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {workspaces.length > 0 ? (\n <div className=\"mt-4 flex flex-col gap-3 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-[var(--color-text-primary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllWorkspaces}\n ariaLabel={tr('workspaces.list.selectAll', 'Select all workspaces')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedWorkspaceIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedWorkspaceIds(new Set())}\n className=\"text-sm text-[var(--color-text-secondary)] transition-colors hover:text-[var(--color-text-primary)]\"\n >\n {tr('common.clearSelection', 'Clear selection')}\n </button>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\n {selectedWorkspaceIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.WORKSPACE_DELETE) && (\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={selectedWorkspaces.length === 0 || deleteLoading}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-status-warning-border)] px-3 py-2 text-[var(--color-status-warning-text)] transition-all duration-200 hover:bg-[var(--color-status-warning-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.list.suspendSelected', 'Suspend Selected')}</span>\n {selectedWorkspaces.length > 0 ? (\n <span>({selectedWorkspaces.length})</span>\n ) : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {workspaces.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 shadow-[var(--shadow-elevation-1)]\">\n <EmptyState\n icon={<Briefcase size={40} aria-hidden=\"true\" />}\n title={tr('workspaces.list.emptyNoneTitle', 'No workspaces found')}\n description={\n hasPermission(PERMISSIONS.WORKSPACE_CREATE)\n ? tr(\n 'workspaces.list.emptyNoneDescription',\n 'Create a workspace to start mapping members, organization ownership, and collaboration settings.'\n )\n : tr(\n 'workspaces.list.emptyNoneDescriptionViewer',\n 'No workspaces have been created yet.'\n )\n }\n action={\n hasPermission(PERMISSIONS.WORKSPACE_CREATE)\n ? {\n label: tr('workspaces.create.button', 'Create Workspace'),\n onClick: () => navigate(`${basePath}/workspaces/new`),\n }\n : undefined\n }\n />\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {workspaces.map((workspace) => (\n <article\n key={workspace.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"flex items-start gap-3\">\n <SelectionCheckbox\n checked={selectedWorkspaceIds.has(workspace.id)}\n onChange={(checked) => toggleWorkspaceSelection(workspace.id, checked)}\n ariaLabel={`${tr('workspaces.list.selectOne', 'Select workspace')} ${workspace.name}`}\n />\n\n <div className=\"min-w-0 flex-1 space-y-3\">\n <div>\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {workspace.name}\n </h2>\n <WorkspaceStatusBadge status={workspace.status} />\n </div>\n <p className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{workspace.slug}\n </p>\n </div>\n\n <dl className=\"grid grid-cols-2 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.members', 'Members')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {workspace.memberCount ?? 0}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {formatDate(workspace.createdAt)}\n </dd>\n </div>\n </dl>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-2 border-t border-[var(--color-border-default)] pt-4\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/${workspace.id}`)}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Eye size={16} aria-hidden=\"true\" />\n {tr('common.view', 'View')}\n </button>\n {hasPermission(PERMISSIONS.WORKSPACE_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/${workspace.id}/edit`)}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <SquarePen size={16} aria-hidden=\"true\" />\n {tr('common.edit', 'Edit')}\n </button>\n )}\n {hasPermission(PERMISSIONS.WORKSPACE_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(workspace.id, workspace.name)}\n disabled={deleteLoading}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-warning-border)] px-3 py-2 text-sm text-[var(--color-status-warning-text)] transition-all duration-200 hover:bg-[var(--color-status-warning-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('workspaces.suspend.confirm', 'Suspend')}\n </button>\n )}\n </div>\n </article>\n ))}\n </div>\n\n <div className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)] md:block\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"w-12 px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllWorkspaces}\n ariaLabel={tr('workspaces.list.selectAll', 'Select all workspaces')}\n />\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('workspaces.list.column.workspace', 'Workspace')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {workspaces.map((workspace) => (\n <tr\n key={workspace.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedWorkspaceIds.has(workspace.id)}\n onChange={(checked) => toggleWorkspaceSelection(workspace.id, checked)}\n ariaLabel={`${tr('workspaces.list.selectOne', 'Select workspace')} ${workspace.name}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div>\n <p className=\"font-medium text-[var(--color-text-primary)]\">\n {workspace.name}\n </p>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{workspace.slug}\n </p>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <WorkspaceStatusBadge status={workspace.status} />\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(workspace.createdAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/${workspace.id}`)}\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n title={`${tr('common.view', 'View')} ${workspace.name}`}\n aria-label={`${tr('common.view', 'View')} ${workspace.name}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.WORKSPACE_UPDATE) && (\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/workspaces/${workspace.id}/edit`)\n }\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n title={`${tr('common.edit', 'Edit')} ${workspace.name}`}\n aria-label={`${tr('common.edit', 'Edit')} ${workspace.name}`}\n >\n <SquarePen size={18} aria-hidden=\"true\" />\n </button>\n )}\n {hasPermission(PERMISSIONS.WORKSPACE_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(workspace.id, workspace.name)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-warning-text)] transition-all duration-200 hover:bg-[var(--color-status-warning-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n title={`${tr('workspaces.suspend.confirm', 'Suspend')} ${workspace.name}`}\n aria-label={`${tr('workspaces.suspend.confirm', 'Suspend')} ${workspace.name}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
|