@withpotter/pages-admin 0.13.0-next.115 → 0.13.0-next.116
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/admin/admins/access-dialog.js +7 -2
- package/dist/admin/admins/access-dialog.js.map +1 -1
- package/dist/admin/admins/invite-dialog.js +6 -0
- package/dist/admin/admins/invite-dialog.js.map +1 -1
- package/dist/admin/hooks.js +7 -0
- package/dist/admin/hooks.js.map +1 -1
- package/dist/admin/roles/page.js +14 -4
- package/dist/admin/roles/page.js.map +1 -1
- package/dist/admin/roles/role-dialog.js +2 -1
- package/dist/admin/roles/role-dialog.js.map +1 -1
- package/dist/admin/roles/role-requests.js +80 -0
- package/dist/admin/roles/role-requests.js.map +1 -0
- package/package.json +4 -4
|
@@ -53,9 +53,14 @@ function AccessForm({
|
|
|
53
53
|
() => kind === "roles" ? (a.roleIds ?? []).filter((id) => roles.some((r) => r.id === id)) : a.permissions ?? []
|
|
54
54
|
);
|
|
55
55
|
const save = useMutation({
|
|
56
|
-
mutationFn: () => kind === "roles" ? platformAdminApi.setAdminRoles(a.id, draft) : platformAdminApi.setAdminPermissions(a.id, draft),
|
|
56
|
+
mutationFn: async () => kind === "roles" ? platformAdminApi.setAdminRoles(a.id, draft) : platformAdminApi.setAdminPermissions(a.id, draft),
|
|
57
57
|
onSuccess: () => {
|
|
58
|
-
toast(
|
|
58
|
+
toast(
|
|
59
|
+
kind === "roles" ? {
|
|
60
|
+
title: "Sent for approval",
|
|
61
|
+
description: `Another super admin has to approve the roles for ${a.name ?? a.email}.`
|
|
62
|
+
} : { title: `Access updated for ${a.name ?? a.email}` }
|
|
63
|
+
);
|
|
59
64
|
onSaved();
|
|
60
65
|
onClose();
|
|
61
66
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/admins/access-dialog.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useMutation } from '@tanstack/react-query';\nimport { Button, FormDialog, useToast } from '@withpotter/ui-kit';\nimport {\n platformAdminApi,\n type AdminRow as Admin,\n type RoleRow,\n} from '@withpotter/lib/admin-api';\nimport { PermissionPicker, toggleIn } from '../roles/permission-picker';\nimport { RoleChoices } from './role-choices';\nimport styles from './admins.module.css';\n\nexport type AccessEdit = { admin: Admin; kind: 'roles' | 'permissions' };\nconst FORM_ID = 'admin-access-form';\n\n/** Change one admin's roles or direct permissions. */\nexport function AccessDialog({\n edit,\n catalog,\n roles,\n onClose,\n onSaved,\n}: {\n edit: AccessEdit | null;\n catalog: string[];\n roles: RoleRow[];\n onClose: () => void;\n onSaved: () => void;\n}) {\n // Count each opening so a cancelled edit never reappears on the next open.\n const [last, setLast] = useState(edit);\n const [opens, setOpens] = useState(0);\n const [wasOpen, setWasOpen] = useState(false);\n if (edit && edit !== last) setLast(edit);\n if (Boolean(edit) !== wasOpen) {\n setWasOpen(Boolean(edit));\n if (edit) setOpens((n) => n + 1);\n }\n const shown = edit ?? last;\n if (!shown) return null;\n return (\n <AccessForm\n key={`${opens}-${shown.admin.id}-${shown.kind}`}\n open={edit !== null}\n edit={shown}\n catalog={catalog}\n roles={roles}\n onClose={onClose}\n onSaved={onSaved}\n />\n );\n}\n\nfunction AccessForm({\n open,\n edit: { admin: a, kind },\n catalog,\n roles,\n onClose,\n onSaved,\n}: {\n open: boolean;\n edit: AccessEdit;\n catalog: string[];\n roles: RoleRow[];\n onClose: () => void;\n onSaved: () => void;\n}) {\n const { toast } = useToast();\n // Deleted roles linger in an admin's roleIds (the API keeps them); drop\n // them so the count is honest and Save does not send dead ids back.\n const [draft, setDraft] = useState<string[]>(() =>\n kind === 'roles'\n ? (a.roleIds ?? []).filter((id) => roles.some((r) => r.id === id))\n : (a.permissions ?? []),\n );\n const save = useMutation({\n mutationFn: () =>\n kind === 'roles'\n ? platformAdminApi.setAdminRoles(a.id, draft)\n : platformAdminApi.setAdminPermissions(a.id, draft),\n onSuccess: () => {\n toast({ title: `Access updated for ${a.name ?? a.email}` });\n onSaved();\n onClose();\n },\n onError: (error) =>\n toast({\n title: 'Access was not updated',\n description: error.message,\n variant: 'destructive',\n }),\n });\n\n return (\n <FormDialog\n open={open}\n size={kind === 'roles' ? 'md' : 'lg'}\n title={kind === 'roles' ? 'Roles' : 'Direct permissions'}\n description={a.email}\n onClose={onClose}\n footer={\n <>\n <span className={styles.footNote}>{draft.length} selected</span>\n <div className={styles.footActions}>\n <Button type=\"button\" variant=\"secondary\" onClick={onClose}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n form={FORM_ID}\n disabled={save.isPending}\n loading={save.isPending}\n >\n Save\n </Button>\n </div>\n </>\n }\n >\n <form\n id={FORM_ID}\n onSubmit={(e) => {\n e.preventDefault();\n if (!save.isPending) save.mutate();\n }}\n >\n {kind === 'roles' ? (\n <RoleChoices\n legend=\"Their access is the union of every role ticked here.\"\n roles={roles}\n value={draft}\n onToggle={(id) => setDraft(toggleIn(id))}\n emptyLabel=\"No roles available.\"\n />\n ) : (\n <PermissionPicker\n legend=\"Granted on top of their roles\"\n keys={catalog}\n value={draft}\n onToggle={(key) => setDraft(toggleIn(key))}\n onSet={setDraft}\n />\n )}\n </form>\n </FormDialog>\n );\n}\n"],"mappings":";AA2CI,
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/admins/access-dialog.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useMutation } from '@tanstack/react-query';\nimport { Button, FormDialog, useToast } from '@withpotter/ui-kit';\nimport {\n platformAdminApi,\n type AdminRow as Admin,\n type RoleRow,\n} from '@withpotter/lib/admin-api';\nimport { PermissionPicker, toggleIn } from '../roles/permission-picker';\nimport { RoleChoices } from './role-choices';\nimport styles from './admins.module.css';\n\nexport type AccessEdit = { admin: Admin; kind: 'roles' | 'permissions' };\nconst FORM_ID = 'admin-access-form';\n\n/** Change one admin's roles or direct permissions. */\nexport function AccessDialog({\n edit,\n catalog,\n roles,\n onClose,\n onSaved,\n}: {\n edit: AccessEdit | null;\n catalog: string[];\n roles: RoleRow[];\n onClose: () => void;\n onSaved: () => void;\n}) {\n // Count each opening so a cancelled edit never reappears on the next open.\n const [last, setLast] = useState(edit);\n const [opens, setOpens] = useState(0);\n const [wasOpen, setWasOpen] = useState(false);\n if (edit && edit !== last) setLast(edit);\n if (Boolean(edit) !== wasOpen) {\n setWasOpen(Boolean(edit));\n if (edit) setOpens((n) => n + 1);\n }\n const shown = edit ?? last;\n if (!shown) return null;\n return (\n <AccessForm\n key={`${opens}-${shown.admin.id}-${shown.kind}`}\n open={edit !== null}\n edit={shown}\n catalog={catalog}\n roles={roles}\n onClose={onClose}\n onSaved={onSaved}\n />\n );\n}\n\nfunction AccessForm({\n open,\n edit: { admin: a, kind },\n catalog,\n roles,\n onClose,\n onSaved,\n}: {\n open: boolean;\n edit: AccessEdit;\n catalog: string[];\n roles: RoleRow[];\n onClose: () => void;\n onSaved: () => void;\n}) {\n const { toast } = useToast();\n // Deleted roles linger in an admin's roleIds (the API keeps them); drop\n // them so the count is honest and Save does not send dead ids back.\n const [draft, setDraft] = useState<string[]>(() =>\n kind === 'roles'\n ? (a.roleIds ?? []).filter((id) => roles.some((r) => r.id === id))\n : (a.permissions ?? []),\n );\n const save = useMutation({\n mutationFn: async (): Promise<unknown> =>\n kind === 'roles'\n ? platformAdminApi.setAdminRoles(a.id, draft)\n : platformAdminApi.setAdminPermissions(a.id, draft),\n onSuccess: () => {\n toast(\n kind === 'roles'\n ? {\n title: 'Sent for approval',\n description: `Another super admin has to approve the roles for ${a.name ?? a.email}.`,\n }\n : { title: `Access updated for ${a.name ?? a.email}` },\n );\n onSaved();\n onClose();\n },\n onError: (error) =>\n toast({\n title: 'Access was not updated',\n description: error.message,\n variant: 'destructive',\n }),\n });\n\n return (\n <FormDialog\n open={open}\n size={kind === 'roles' ? 'md' : 'lg'}\n title={kind === 'roles' ? 'Roles' : 'Direct permissions'}\n description={a.email}\n onClose={onClose}\n footer={\n <>\n <span className={styles.footNote}>{draft.length} selected</span>\n <div className={styles.footActions}>\n <Button type=\"button\" variant=\"secondary\" onClick={onClose}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n form={FORM_ID}\n disabled={save.isPending}\n loading={save.isPending}\n >\n Save\n </Button>\n </div>\n </>\n }\n >\n <form\n id={FORM_ID}\n onSubmit={(e) => {\n e.preventDefault();\n if (!save.isPending) save.mutate();\n }}\n >\n {kind === 'roles' ? (\n <RoleChoices\n legend=\"Their access is the union of every role ticked here.\"\n roles={roles}\n value={draft}\n onToggle={(id) => setDraft(toggleIn(id))}\n emptyLabel=\"No roles available.\"\n />\n ) : (\n <PermissionPicker\n legend=\"Granted on top of their roles\"\n keys={catalog}\n value={draft}\n onToggle={(key) => setDraft(toggleIn(key))}\n onSet={setDraft}\n />\n )}\n </form>\n </FormDialog>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -25,6 +25,12 @@ function InviteDialog({
|
|
|
25
25
|
const invite = useMutation({
|
|
26
26
|
mutationFn: () => platformAdminApi.inviteAdmin(email, role, perms, roleIds),
|
|
27
27
|
onSuccess: (r) => {
|
|
28
|
+
if (r.pendingRoles) {
|
|
29
|
+
toast({
|
|
30
|
+
title: "Roles sent for approval",
|
|
31
|
+
description: "Another super admin has to approve them before this admin holds them."
|
|
32
|
+
});
|
|
33
|
+
}
|
|
28
34
|
onInvited(r);
|
|
29
35
|
setEmail("");
|
|
30
36
|
setRole("admin");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/admins/invite-dialog.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState } from 'react';\nimport { useMutation } from '@tanstack/react-query';\nimport { Button, FormDialog, Input, useToast } from '@withpotter/ui-kit';\nimport { Select } from '@withpotter/ui-kit/select';\nimport { platformAdminApi, type RoleRow } from '@withpotter/lib/admin-api';\nimport { PermissionPicker, toggleIn } from '../roles/permission-picker';\nimport { RoleChoices } from './role-choices';\nimport styles from './admins.module.css';\n\ntype AdminRole = 'admin' | 'superadmin';\nconst FORM_ID = 'invite-admin-form';\n\n/** Email and access level, then roles and any direct grants. */\nexport function InviteDialog({\n open,\n catalog,\n roles,\n onClose,\n onInvited,\n}: {\n open: boolean;\n catalog: string[];\n roles: RoleRow[];\n onClose: () => void;\n onInvited: (temp: { email: string; tempPassword: string }) => void;\n}) {\n const { toast } = useToast();\n const roleFieldId = useId();\n const [email, setEmail] = useState('');\n const [role, setRole] = useState<AdminRole>('admin');\n const [perms, setPerms] = useState<string[]>([]);\n const [roleIds, setRoleIds] = useState<string[]>([]);\n\n const invite = useMutation({\n mutationFn: () => platformAdminApi.inviteAdmin(email, role, perms, roleIds),\n onSuccess: (r) => {\n onInvited(r);\n setEmail('');\n setRole('admin');\n setPerms([]);\n setRoleIds([]);\n },\n onError: (error) =>\n toast({\n title: 'The invite was not sent',\n description: error.message,\n variant: 'destructive',\n }),\n });\n const canSend = email.trim().length > 0 && !invite.isPending;\n\n return (\n <FormDialog\n open={open}\n size=\"lg\"\n title=\"Invite admin\"\n description=\"They get a temporary password and choose their own on first sign-in.\"\n onClose={onClose}\n footer={\n <>\n <span className={styles.footNote}>\n {role === 'superadmin'\n ? 'Full access'\n : `${roleIds.length} role${roleIds.length === 1 ? '' : 's'} · ${perms.length} direct`}\n </span>\n <div className={styles.footActions}>\n <Button type=\"button\" variant=\"secondary\" onClick={onClose}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n form={FORM_ID}\n disabled={!canSend}\n loading={invite.isPending}\n >\n Send invite\n </Button>\n </div>\n </>\n }\n >\n <form\n id={FORM_ID}\n className={styles.form}\n onSubmit={(e) => {\n e.preventDefault();\n if (canSend) invite.mutate();\n }}\n >\n <div className={styles.formRow}>\n <Input\n label=\"Email\"\n type=\"email\"\n placeholder=\"name@company.com\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoFocus\n />\n <div className={styles.field}>\n <label htmlFor={roleFieldId} className={styles.fieldLabel}>\n Access level\n </label>\n <Select\n id={roleFieldId}\n value={role}\n onChange={(v) => setRole(v as AdminRole)}\n options={[\n { value: 'admin', label: 'Admin' },\n { value: 'superadmin', label: 'Superadmin' },\n ]}\n />\n </div>\n </div>\n\n {role === 'admin' ? (\n <>\n <RoleChoices\n legend=\"Roles (permissions stack as a union)\"\n roles={roles}\n value={roleIds}\n onToggle={(id) => setRoleIds(toggleIn(id))}\n emptyLabel=\"No roles yet — create one on the Roles page.\"\n />\n <PermissionPicker\n legend=\"Extra permissions (optional)\"\n keys={catalog}\n value={perms}\n onToggle={(key) => setPerms(toggleIn(key))}\n onSet={setPerms}\n />\n </>\n ) : (\n <p className={styles.note}>\n Superadmins have full access, so no roles or permissions are needed.\n </p>\n )}\n </form>\n </FormDialog>\n );\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/admins/invite-dialog.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState } from 'react';\nimport { useMutation } from '@tanstack/react-query';\nimport { Button, FormDialog, Input, useToast } from '@withpotter/ui-kit';\nimport { Select } from '@withpotter/ui-kit/select';\nimport { platformAdminApi, type RoleRow } from '@withpotter/lib/admin-api';\nimport { PermissionPicker, toggleIn } from '../roles/permission-picker';\nimport { RoleChoices } from './role-choices';\nimport styles from './admins.module.css';\n\ntype AdminRole = 'admin' | 'superadmin';\nconst FORM_ID = 'invite-admin-form';\n\n/** Email and access level, then roles and any direct grants. */\nexport function InviteDialog({\n open,\n catalog,\n roles,\n onClose,\n onInvited,\n}: {\n open: boolean;\n catalog: string[];\n roles: RoleRow[];\n onClose: () => void;\n onInvited: (temp: { email: string; tempPassword: string }) => void;\n}) {\n const { toast } = useToast();\n const roleFieldId = useId();\n const [email, setEmail] = useState('');\n const [role, setRole] = useState<AdminRole>('admin');\n const [perms, setPerms] = useState<string[]>([]);\n const [roleIds, setRoleIds] = useState<string[]>([]);\n\n const invite = useMutation({\n mutationFn: () => platformAdminApi.inviteAdmin(email, role, perms, roleIds),\n onSuccess: (r) => {\n if (r.pendingRoles) {\n toast({\n title: 'Roles sent for approval',\n description:\n 'Another super admin has to approve them before this admin holds them.',\n });\n }\n onInvited(r);\n setEmail('');\n setRole('admin');\n setPerms([]);\n setRoleIds([]);\n },\n onError: (error) =>\n toast({\n title: 'The invite was not sent',\n description: error.message,\n variant: 'destructive',\n }),\n });\n const canSend = email.trim().length > 0 && !invite.isPending;\n\n return (\n <FormDialog\n open={open}\n size=\"lg\"\n title=\"Invite admin\"\n description=\"They get a temporary password and choose their own on first sign-in.\"\n onClose={onClose}\n footer={\n <>\n <span className={styles.footNote}>\n {role === 'superadmin'\n ? 'Full access'\n : `${roleIds.length} role${roleIds.length === 1 ? '' : 's'} · ${perms.length} direct`}\n </span>\n <div className={styles.footActions}>\n <Button type=\"button\" variant=\"secondary\" onClick={onClose}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n form={FORM_ID}\n disabled={!canSend}\n loading={invite.isPending}\n >\n Send invite\n </Button>\n </div>\n </>\n }\n >\n <form\n id={FORM_ID}\n className={styles.form}\n onSubmit={(e) => {\n e.preventDefault();\n if (canSend) invite.mutate();\n }}\n >\n <div className={styles.formRow}>\n <Input\n label=\"Email\"\n type=\"email\"\n placeholder=\"name@company.com\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoFocus\n />\n <div className={styles.field}>\n <label htmlFor={roleFieldId} className={styles.fieldLabel}>\n Access level\n </label>\n <Select\n id={roleFieldId}\n value={role}\n onChange={(v) => setRole(v as AdminRole)}\n options={[\n { value: 'admin', label: 'Admin' },\n { value: 'superadmin', label: 'Superadmin' },\n ]}\n />\n </div>\n </div>\n\n {role === 'admin' ? (\n <>\n <RoleChoices\n legend=\"Roles (permissions stack as a union)\"\n roles={roles}\n value={roleIds}\n onToggle={(id) => setRoleIds(toggleIn(id))}\n emptyLabel=\"No roles yet — create one on the Roles page.\"\n />\n <PermissionPicker\n legend=\"Extra permissions (optional)\"\n keys={catalog}\n value={perms}\n onToggle={(key) => setPerms(toggleIn(key))}\n onSet={setPerms}\n />\n </>\n ) : (\n <p className={styles.note}>\n Superadmins have full access, so no roles or permissions are needed.\n </p>\n )}\n </form>\n </FormDialog>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/admin/hooks.js
CHANGED
|
@@ -211,6 +211,12 @@ function useAdminRoles() {
|
|
|
211
211
|
queryFn: () => platformAdminApi.roles()
|
|
212
212
|
});
|
|
213
213
|
}
|
|
214
|
+
function useRoleChangeRequests() {
|
|
215
|
+
return useQuery({
|
|
216
|
+
queryKey: ["platform-admin", "roles", "requests"],
|
|
217
|
+
queryFn: () => platformAdminApi.roleRequests()
|
|
218
|
+
});
|
|
219
|
+
}
|
|
214
220
|
function useAccountSessions(id) {
|
|
215
221
|
return useQuery({
|
|
216
222
|
queryKey: ["platform-admin", "account-sessions", id],
|
|
@@ -259,6 +265,7 @@ export {
|
|
|
259
265
|
usePlatformTenant,
|
|
260
266
|
usePlatformTenants,
|
|
261
267
|
usePlatformTenantsAnalytics,
|
|
268
|
+
useRoleChangeRequests,
|
|
262
269
|
useSubscriptionPayments,
|
|
263
270
|
useTenantAnalytics,
|
|
264
271
|
useTenantCustomerInsights,
|
package/dist/admin/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/admin/hooks.ts"],"sourcesContent":["'use client';\n\nimport { keepPreviousData, useQuery } from '@tanstack/react-query';\nimport { platformAdminApi, type LedgerFilters } from '@withpotter/lib/admin-api';\nimport type { TenantListParams } from '@withpotter/lib/admin-tenants-query';\nimport type { AccountStage } from '@withpotter/lib/admin-account-types';\n\nexport function useAdminSession(enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'session'],\n queryFn: () => platformAdminApi.me(),\n enabled,\n retry: false,\n staleTime: 60_000,\n });\n}\n\nexport function usePlatformOverview() {\n return useQuery({\n queryKey: ['platform-admin', 'overview'],\n queryFn: () => platformAdminApi.overview(),\n });\n}\n\nexport function usePlatformCockpit(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'cockpit', range],\n queryFn: () => platformAdminApi.cockpit(range),\n });\n}\n\nexport function usePlatformRevenue(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', range],\n queryFn: () => platformAdminApi.revenue(range),\n });\n}\n\n/**\n * How customers paid, per channel. Not every box can say: where the route is\n * absent the query fails once (no retry) and `data` stays undefined, which the\n * panels read as \"nothing to show\".\n */\nexport function useGatewayChannels(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'gateway-channels', range],\n queryFn: () => platformAdminApi.gatewayChannels(range),\n retry: false,\n staleTime: 60_000,\n });\n}\n\n/**\n * Revenue's Merchant payments view. A box without the route answers 404; the\n * view reads that as \"not available here\", never as a failure to retry.\n */\nexport function useMerchantPayments(filters: LedgerFilters) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'merchant-payments', filters],\n queryFn: () => platformAdminApi.merchantPayments(filters),\n retry: false,\n staleTime: 30_000,\n placeholderData: keepPreviousData,\n });\n}\n\n/** Revenue's Subscriptions view: every plan payment merchants made to Olive. */\nexport function useSubscriptionPayments(filters: LedgerFilters) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'subscription-payments', filters],\n queryFn: () => platformAdminApi.subscriptionPayments(filters),\n retry: false,\n staleTime: 30_000,\n placeholderData: keepPreviousData,\n });\n}\n\n/** Revenue's Merchants view: what each merchant received and how customers paid them. */\nexport function useMerchantRevenue(filters: LedgerFilters) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'merchants', filters],\n queryFn: () => platformAdminApi.merchantRevenue(filters),\n retry: false,\n staleTime: 30_000,\n placeholderData: keepPreviousData,\n });\n}\n\n/** The merchants that took payments in the window, for the merchant picker. */\nexport function useMerchantOptions(range: { startDate?: string; endDate?: string }) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'merchant-options', range],\n queryFn: () => platformAdminApi.merchantOptions(range),\n retry: false,\n staleTime: 60_000,\n });\n}\n\nexport function usePlatformTenantsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants-analytics', range],\n queryFn: () => platformAdminApi.tenantsAnalytics(range),\n });\n}\n\nexport function usePlatformTenants(params: TenantListParams) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants', params],\n queryFn: () => platformAdminApi.tenants(params),\n });\n}\n\nexport function usePlatformTenant(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant', id, range],\n queryFn: () => platformAdminApi.tenant(id, range),\n enabled: !!id,\n });\n}\n\nexport function useTenantAnalytics(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-analytics', id, range],\n queryFn: () => platformAdminApi.tenantAnalytics(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantFinance(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-finance', id, range],\n queryFn: () => platformAdminApi.tenantFinance(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantTransactions(\n id: string,\n params: {\n status?: string;\n type?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-transactions', id, params],\n queryFn: () => platformAdminApi.tenantTransactions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomers(\n id: string,\n params: {\n search?: string;\n type?: string;\n status?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customers', id, params],\n queryFn: () => platformAdminApi.tenantCustomers(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProducts(\n id: string,\n params: { type?: string; search?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-products', id, params],\n queryFn: () => platformAdminApi.tenantProducts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProductInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-product-insights', id, range],\n queryFn: () => platformAdminApi.tenantProductInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomerInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customer-insights', id, range],\n queryFn: () => platformAdminApi.tenantCustomerInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantInvoices(\n id: string,\n params: {\n status?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-invoices', id, params],\n queryFn: () => platformAdminApi.tenantInvoices(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantLoyalty(id: string, enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-loyalty', id],\n queryFn: () => platformAdminApi.tenantLoyalty(id),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantDiscounts(\n id: string,\n params: {\n status?: string;\n discountType?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-discounts', id, params],\n queryFn: () => platformAdminApi.tenantDiscounts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSettlements(\n id: string,\n params: {\n page?: number;\n limit?: number;\n startDate?: string;\n endDate?: string;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-settlements', id, params],\n queryFn: () => platformAdminApi.tenantSettlements(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSubscriptions(\n id: string,\n params: {\n status?: string;\n planId?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-subscriptions', id, params],\n queryFn: () => platformAdminApi.tenantSubscriptions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function usePlatformAccounts(params: {\n page?: number;\n pageSize?: number;\n search?: string;\n joinedFrom?: string;\n joinedTo?: string;\n stage?: AccountStage;\n}) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts', params],\n queryFn: () => platformAdminApi.accounts(params),\n });\n}\n\nexport function usePlatformAccountsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts-analytics', range],\n queryFn: () => platformAdminApi.accountsAnalytics(range),\n });\n}\n\nexport function usePlatformAdmins() {\n return useQuery({\n queryKey: ['platform-admin', 'admins'],\n queryFn: () => platformAdminApi.admins(),\n });\n}\n\nexport function usePlatformAdminsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'admins-analytics', range],\n queryFn: () => platformAdminApi.adminsAnalytics(range),\n });\n}\n\nexport function useAdminPermissions() {\n return useQuery({\n queryKey: ['platform-admin', 'admin-permissions'],\n queryFn: () => platformAdminApi.adminPermissions(),\n staleTime: 5 * 60_000,\n });\n}\n\nexport function useAdminRoles() {\n return useQuery({\n queryKey: ['platform-admin', 'roles'],\n queryFn: () => platformAdminApi.roles(),\n });\n}\n\nexport function useAccountSessions(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-sessions', id],\n queryFn: () => platformAdminApi.accountSessions(id!),\n enabled: !!id,\n });\n}\n\nexport function useAccountTenants(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-tenants', id],\n queryFn: () => platformAdminApi.accountTenants(id!),\n enabled: !!id,\n });\n}\n\nexport function usePlatformAudit(\n params: {\n page?: number;\n pageSize?: number;\n search?: string;\n action?: string;\n adminEmail?: string;\n from?: string;\n to?: string;\n } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit', params],\n queryFn: () => platformAdminApi.audit(params),\n });\n}\n\nexport function usePlatformAuditAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit-analytics', range],\n queryFn: () => platformAdminApi.auditAnalytics(range),\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/admin/hooks.ts"],"sourcesContent":["'use client';\n\nimport { keepPreviousData, useQuery } from '@tanstack/react-query';\nimport { platformAdminApi, type LedgerFilters } from '@withpotter/lib/admin-api';\nimport type { TenantListParams } from '@withpotter/lib/admin-tenants-query';\nimport type { AccountStage } from '@withpotter/lib/admin-account-types';\n\nexport function useAdminSession(enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'session'],\n queryFn: () => platformAdminApi.me(),\n enabled,\n retry: false,\n staleTime: 60_000,\n });\n}\n\nexport function usePlatformOverview() {\n return useQuery({\n queryKey: ['platform-admin', 'overview'],\n queryFn: () => platformAdminApi.overview(),\n });\n}\n\nexport function usePlatformCockpit(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'cockpit', range],\n queryFn: () => platformAdminApi.cockpit(range),\n });\n}\n\nexport function usePlatformRevenue(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', range],\n queryFn: () => platformAdminApi.revenue(range),\n });\n}\n\n/**\n * How customers paid, per channel. Not every box can say: where the route is\n * absent the query fails once (no retry) and `data` stays undefined, which the\n * panels read as \"nothing to show\".\n */\nexport function useGatewayChannels(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'gateway-channels', range],\n queryFn: () => platformAdminApi.gatewayChannels(range),\n retry: false,\n staleTime: 60_000,\n });\n}\n\n/**\n * Revenue's Merchant payments view. A box without the route answers 404; the\n * view reads that as \"not available here\", never as a failure to retry.\n */\nexport function useMerchantPayments(filters: LedgerFilters) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'merchant-payments', filters],\n queryFn: () => platformAdminApi.merchantPayments(filters),\n retry: false,\n staleTime: 30_000,\n placeholderData: keepPreviousData,\n });\n}\n\n/** Revenue's Subscriptions view: every plan payment merchants made to Olive. */\nexport function useSubscriptionPayments(filters: LedgerFilters) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'subscription-payments', filters],\n queryFn: () => platformAdminApi.subscriptionPayments(filters),\n retry: false,\n staleTime: 30_000,\n placeholderData: keepPreviousData,\n });\n}\n\n/** Revenue's Merchants view: what each merchant received and how customers paid them. */\nexport function useMerchantRevenue(filters: LedgerFilters) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'merchants', filters],\n queryFn: () => platformAdminApi.merchantRevenue(filters),\n retry: false,\n staleTime: 30_000,\n placeholderData: keepPreviousData,\n });\n}\n\n/** The merchants that took payments in the window, for the merchant picker. */\nexport function useMerchantOptions(range: { startDate?: string; endDate?: string }) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'merchant-options', range],\n queryFn: () => platformAdminApi.merchantOptions(range),\n retry: false,\n staleTime: 60_000,\n });\n}\n\nexport function usePlatformTenantsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants-analytics', range],\n queryFn: () => platformAdminApi.tenantsAnalytics(range),\n });\n}\n\nexport function usePlatformTenants(params: TenantListParams) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants', params],\n queryFn: () => platformAdminApi.tenants(params),\n });\n}\n\nexport function usePlatformTenant(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant', id, range],\n queryFn: () => platformAdminApi.tenant(id, range),\n enabled: !!id,\n });\n}\n\nexport function useTenantAnalytics(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-analytics', id, range],\n queryFn: () => platformAdminApi.tenantAnalytics(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantFinance(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-finance', id, range],\n queryFn: () => platformAdminApi.tenantFinance(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantTransactions(\n id: string,\n params: {\n status?: string;\n type?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-transactions', id, params],\n queryFn: () => platformAdminApi.tenantTransactions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomers(\n id: string,\n params: {\n search?: string;\n type?: string;\n status?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customers', id, params],\n queryFn: () => platformAdminApi.tenantCustomers(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProducts(\n id: string,\n params: { type?: string; search?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-products', id, params],\n queryFn: () => platformAdminApi.tenantProducts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProductInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-product-insights', id, range],\n queryFn: () => platformAdminApi.tenantProductInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomerInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customer-insights', id, range],\n queryFn: () => platformAdminApi.tenantCustomerInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantInvoices(\n id: string,\n params: {\n status?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-invoices', id, params],\n queryFn: () => platformAdminApi.tenantInvoices(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantLoyalty(id: string, enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-loyalty', id],\n queryFn: () => platformAdminApi.tenantLoyalty(id),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantDiscounts(\n id: string,\n params: {\n status?: string;\n discountType?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-discounts', id, params],\n queryFn: () => platformAdminApi.tenantDiscounts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSettlements(\n id: string,\n params: {\n page?: number;\n limit?: number;\n startDate?: string;\n endDate?: string;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-settlements', id, params],\n queryFn: () => platformAdminApi.tenantSettlements(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSubscriptions(\n id: string,\n params: {\n status?: string;\n planId?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-subscriptions', id, params],\n queryFn: () => platformAdminApi.tenantSubscriptions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function usePlatformAccounts(params: {\n page?: number;\n pageSize?: number;\n search?: string;\n joinedFrom?: string;\n joinedTo?: string;\n stage?: AccountStage;\n}) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts', params],\n queryFn: () => platformAdminApi.accounts(params),\n });\n}\n\nexport function usePlatformAccountsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts-analytics', range],\n queryFn: () => platformAdminApi.accountsAnalytics(range),\n });\n}\n\nexport function usePlatformAdmins() {\n return useQuery({\n queryKey: ['platform-admin', 'admins'],\n queryFn: () => platformAdminApi.admins(),\n });\n}\n\nexport function usePlatformAdminsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'admins-analytics', range],\n queryFn: () => platformAdminApi.adminsAnalytics(range),\n });\n}\n\nexport function useAdminPermissions() {\n return useQuery({\n queryKey: ['platform-admin', 'admin-permissions'],\n queryFn: () => platformAdminApi.adminPermissions(),\n staleTime: 5 * 60_000,\n });\n}\n\nexport function useAdminRoles() {\n return useQuery({\n queryKey: ['platform-admin', 'roles'],\n queryFn: () => platformAdminApi.roles(),\n });\n}\n\nexport function useRoleChangeRequests() {\n return useQuery({\n queryKey: ['platform-admin', 'roles', 'requests'],\n queryFn: () => platformAdminApi.roleRequests(),\n });\n}\n\nexport function useAccountSessions(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-sessions', id],\n queryFn: () => platformAdminApi.accountSessions(id!),\n enabled: !!id,\n });\n}\n\nexport function useAccountTenants(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-tenants', id],\n queryFn: () => platformAdminApi.accountTenants(id!),\n enabled: !!id,\n });\n}\n\nexport function usePlatformAudit(\n params: {\n page?: number;\n pageSize?: number;\n search?: string;\n action?: string;\n adminEmail?: string;\n from?: string;\n to?: string;\n } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit', params],\n queryFn: () => platformAdminApi.audit(params),\n });\n}\n\nexport function usePlatformAuditAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit-analytics', range],\n queryFn: () => platformAdminApi.auditAnalytics(range),\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/admin/roles/page.js
CHANGED
|
@@ -16,6 +16,7 @@ import { EmptyState, Panel } from "../_ui";
|
|
|
16
16
|
import { EmptyBlock } from "../audit/empty-block";
|
|
17
17
|
import { SectionHeader } from "../audit/section-header";
|
|
18
18
|
import { RolesKpis } from "./roles-kpis";
|
|
19
|
+
import { RoleRequests } from "./role-requests";
|
|
19
20
|
import { RoleDialog } from "./role-dialog";
|
|
20
21
|
import { RoleRow, ROLE_COLUMNS, adminCount } from "./role-row";
|
|
21
22
|
import section from "../audit/page.module.css";
|
|
@@ -47,11 +48,14 @@ function RolesPage() {
|
|
|
47
48
|
const remove = useMutation({
|
|
48
49
|
mutationFn: (r) => platformAdminApi.deleteRole(r.id),
|
|
49
50
|
onSuccess: (_, r) => {
|
|
50
|
-
toast({
|
|
51
|
+
toast({
|
|
52
|
+
title: "Sent for approval",
|
|
53
|
+
description: `Another super admin has to approve deleting ${r.name}.`
|
|
54
|
+
});
|
|
51
55
|
void invalidate();
|
|
52
56
|
},
|
|
53
57
|
onError: (error) => toast({
|
|
54
|
-
title: "The
|
|
58
|
+
title: "The deletion was not sent",
|
|
55
59
|
description: error.message,
|
|
56
60
|
variant: "destructive"
|
|
57
61
|
})
|
|
@@ -60,7 +64,7 @@ function RolesPage() {
|
|
|
60
64
|
const ok = await confirm({
|
|
61
65
|
title: `Delete ${r.name}?`,
|
|
62
66
|
description: r.assignedCount > 0 ? `${adminCount(r.assignedCount)} hold this role and will lose the access it grants. This can\u2019t be undone.` : "No admin holds this role. This can\u2019t be undone.",
|
|
63
|
-
confirmLabel: "
|
|
67
|
+
confirmLabel: "Ask to delete",
|
|
64
68
|
tone: "danger"
|
|
65
69
|
});
|
|
66
70
|
if (ok) remove.mutate(r);
|
|
@@ -142,13 +146,19 @@ function RolesPage() {
|
|
|
142
146
|
children: table
|
|
143
147
|
}
|
|
144
148
|
) }),
|
|
149
|
+
/* @__PURE__ */ jsx(RoleRequests, {}),
|
|
145
150
|
/* @__PURE__ */ jsx(
|
|
146
151
|
RoleDialog,
|
|
147
152
|
{
|
|
148
153
|
mode: dialog,
|
|
149
154
|
catalog: availablePermissions,
|
|
150
155
|
onClose: () => setDialog(null),
|
|
151
|
-
onSaved: () =>
|
|
156
|
+
onSaved: () => {
|
|
157
|
+
void invalidate();
|
|
158
|
+
void queryClient.invalidateQueries({
|
|
159
|
+
queryKey: ["platform-admin", "roles", "requests"]
|
|
160
|
+
});
|
|
161
|
+
}
|
|
152
162
|
}
|
|
153
163
|
)
|
|
154
164
|
] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/roles/page.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo, useState } from 'react';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport {\n Button,\n TableShell,\n TableSkeleton,\n Th,\n useConfirm,\n useToast,\n} from '@withpotter/ui-kit';\nimport { platformAdminApi, type RoleRow as Role } from '@withpotter/lib/admin-api';\nimport { useAdminRoles, useAdminPermissions } from '../hooks';\nimport { EmptyState, Panel } from '../_ui';\nimport { EmptyBlock } from '../audit/empty-block';\nimport { SectionHeader } from '../audit/section-header';\nimport { RolesKpis } from './roles-kpis';\nimport { RoleDialog, type RoleDialogMode } from './role-dialog';\nimport { RoleRow, ROLE_COLUMNS, adminCount } from './role-row';\nimport section from '../audit/page.module.css';\nimport kpi from '../audit/kpi-brand.module.css';\nimport styles from './roles.module.css';\n\nconst CONTROLLED_PERMISSIONS = [\n 'money.cash.propose',\n 'money.cash.approve',\n 'money.payout_tiers.read',\n 'money.payout_tiers.manage',\n 'pricing.read',\n 'pricing.propose',\n 'pricing.approve',\n 'sales.read',\n 'sales.manage',\n] as const;\n\nexport default function RolesPage() {\n const queryClient = useQueryClient();\n const confirm = useConfirm();\n const { toast } = useToast();\n const [dialog, setDialog] = useState<RoleDialogMode | null>(null);\n const { data: roles = [], isLoading, isError, refetch } = useAdminRoles();\n const { data: catalog = [] } = useAdminPermissions();\n const availablePermissions = useMemo(\n () => Array.from(new Set([...catalog, ...CONTROLLED_PERMISSIONS])),\n [catalog],\n );\n\n const invalidate = () =>\n queryClient.invalidateQueries({ queryKey: ['platform-admin', 'roles'] });\n\n const remove = useMutation({\n mutationFn: (r: Role) => platformAdminApi.deleteRole(r.id),\n onSuccess: (_, r) => {\n toast({
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/roles/page.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo, useState } from 'react';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport {\n Button,\n TableShell,\n TableSkeleton,\n Th,\n useConfirm,\n useToast,\n} from '@withpotter/ui-kit';\nimport { platformAdminApi, type RoleRow as Role } from '@withpotter/lib/admin-api';\nimport { useAdminRoles, useAdminPermissions } from '../hooks';\nimport { EmptyState, Panel } from '../_ui';\nimport { EmptyBlock } from '../audit/empty-block';\nimport { SectionHeader } from '../audit/section-header';\nimport { RolesKpis } from './roles-kpis';\nimport { RoleRequests } from './role-requests';\nimport { RoleDialog, type RoleDialogMode } from './role-dialog';\nimport { RoleRow, ROLE_COLUMNS, adminCount } from './role-row';\nimport section from '../audit/page.module.css';\nimport kpi from '../audit/kpi-brand.module.css';\nimport styles from './roles.module.css';\n\nconst CONTROLLED_PERMISSIONS = [\n 'money.cash.propose',\n 'money.cash.approve',\n 'money.payout_tiers.read',\n 'money.payout_tiers.manage',\n 'pricing.read',\n 'pricing.propose',\n 'pricing.approve',\n 'sales.read',\n 'sales.manage',\n] as const;\n\nexport default function RolesPage() {\n const queryClient = useQueryClient();\n const confirm = useConfirm();\n const { toast } = useToast();\n const [dialog, setDialog] = useState<RoleDialogMode | null>(null);\n const { data: roles = [], isLoading, isError, refetch } = useAdminRoles();\n const { data: catalog = [] } = useAdminPermissions();\n const availablePermissions = useMemo(\n () => Array.from(new Set([...catalog, ...CONTROLLED_PERMISSIONS])),\n [catalog],\n );\n\n const invalidate = () =>\n queryClient.invalidateQueries({ queryKey: ['platform-admin', 'roles'] });\n\n const remove = useMutation({\n mutationFn: (r: Role) => platformAdminApi.deleteRole(r.id),\n onSuccess: (_, r) => {\n toast({\n title: 'Sent for approval',\n description: `Another super admin has to approve deleting ${r.name}.`,\n });\n void invalidate();\n },\n onError: (error) =>\n toast({\n title: 'The deletion was not sent',\n description: error.message,\n variant: 'destructive',\n }),\n });\n\n const confirmDelete = async (r: Role) => {\n const ok = await confirm({\n title: `Delete ${r.name}?`,\n description:\n r.assignedCount > 0\n ? `${adminCount(r.assignedCount)} hold this role and will lose the access it grants. This can’t be undone.`\n : 'No admin holds this role. This can’t be undone.',\n confirmLabel: 'Ask to delete',\n tone: 'danger',\n });\n if (ok) remove.mutate(r);\n };\n\n const create = (\n <Button onClick={() => setDialog({ kind: 'create' })}>Create role</Button>\n );\n\n let table;\n if (isLoading) {\n table = (\n <div aria-hidden>\n <TableSkeleton bare rows={5} columns={ROLE_COLUMNS} />\n </div>\n );\n } else if (isError) {\n table = (\n <EmptyState\n tone=\"error\"\n title=\"Roles could not be loaded\"\n description=\"Try again. If this keeps happening, contact engineering.\"\n action={\n <Button size=\"sm\" variant=\"secondary\" onClick={() => void refetch()}>\n Try again\n </Button>\n }\n />\n );\n } else if (roles.length === 0) {\n table = (\n <EmptyBlock\n message=\"No roles yet. A role bundles permissions so you can grant them in one step.\"\n action={create}\n />\n );\n } else {\n table = (\n <TableShell\n bare\n fixed\n minWidth={760}\n density=\"compact\"\n head={\n <tr>\n <Th className={styles.colRole}>Role</Th>\n <Th>Permissions</Th>\n <Th className={styles.colType}>Type</Th>\n <Th className={styles.colHeld} align=\"right\">\n Held by\n </Th>\n <Th className={styles.colMenu} align=\"right\">\n <span className=\"sr-only\">Actions</span>\n </Th>\n </tr>\n }\n >\n {roles.map((r) => (\n <RoleRow\n key={r.id}\n r={r}\n onEdit={() => setDialog({ kind: 'edit', role: r })}\n onDuplicate={() => setDialog({ kind: 'duplicate', role: r })}\n onDelete={() => void confirmDelete(r)}\n />\n ))}\n </TableShell>\n );\n }\n\n return (\n <div className={styles.page}>\n <SectionHeader\n title=\"Roles\"\n description=\"Reusable permission bundles. An admin’s access is the union of every role they hold.\"\n actions={create}\n />\n\n <div className={kpi.kpis}>\n <RolesKpis\n roles={roles}\n permissions={availablePermissions.length}\n isLoading={isLoading}\n />\n </div>\n\n <div className={section.section} aria-busy={isLoading || undefined}>\n <Panel\n title=\"All roles\"\n count={isLoading ? undefined : roles.length}\n height=\"auto\"\n flush\n >\n {table}\n </Panel>\n </div>\n\n <RoleRequests />\n\n <RoleDialog\n mode={dialog}\n catalog={availablePermissions}\n onClose={() => setDialog(null)}\n onSaved={() => {\n void invalidate();\n void queryClient.invalidateQueries({\n queryKey: ['platform-admin', 'roles', 'requests'],\n });\n }}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -66,7 +66,8 @@ function RoleForm({
|
|
|
66
66
|
),
|
|
67
67
|
onSuccess: () => {
|
|
68
68
|
toast({
|
|
69
|
-
title:
|
|
69
|
+
title: "Sent for approval",
|
|
70
|
+
description: "Another super admin has to approve this before the role changes."
|
|
70
71
|
});
|
|
71
72
|
onSaved();
|
|
72
73
|
onClose();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/roles/role-dialog.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useMutation } from '@tanstack/react-query';\nimport { Button, FormDialog, Input, useToast } from '@withpotter/ui-kit';\nimport { platformAdminApi, type RoleRow as Role } from '@withpotter/lib/admin-api';\nimport { PermissionPicker, toggleIn } from './permission-picker';\nimport styles from './roles.module.css';\n\nexport type RoleDialogMode =\n | { kind: 'create' }\n | { kind: 'edit'; role: Role }\n | { kind: 'duplicate'; role: Role };\n\nconst COPY = {\n create: { title: 'Create role', action: 'Create role' },\n edit: { title: 'Edit role', action: 'Save changes' },\n duplicate: { title: 'Duplicate role', action: 'Create copy' },\n} as const;\n\nconst FORM_ID = 'role-dialog-form';\n\n/** Create, edit or duplicate a role: name, purpose and grouped permissions. */\nexport function RoleDialog({\n mode,\n catalog,\n onClose,\n onSaved,\n}: {\n mode: RoleDialogMode | null;\n catalog: readonly string[];\n onClose: () => void;\n onSaved: () => void;\n}) {\n // Keep the last mode while the dialog animates out, and count each opening\n // so the form remounts fresh — a reopened Create must not show the last\n // role's name, nor a cancelled edit come back.\n const [shown, setShown] = useState(mode);\n const [opens, setOpens] = useState(0);\n const [wasOpen, setWasOpen] = useState(false);\n if (mode && mode !== shown) setShown(mode);\n if (Boolean(mode) !== wasOpen) {\n setWasOpen(Boolean(mode));\n if (mode) setOpens((n) => n + 1);\n }\n const active = mode ?? shown;\n\n if (!active) return null;\n return (\n <RoleForm\n key={`${opens}-${active.kind}-${'role' in active ? active.role.id : ''}`}\n open={mode !== null}\n mode={active}\n catalog={catalog}\n onClose={onClose}\n onSaved={onSaved}\n />\n );\n}\n\nfunction RoleForm({\n open,\n mode,\n catalog,\n onClose,\n onSaved,\n}: {\n open: boolean;\n mode: RoleDialogMode;\n catalog: readonly string[];\n onClose: () => void;\n onSaved: () => void;\n}) {\n const { toast } = useToast();\n const source = mode.kind === 'create' ? null : mode.role;\n const [name, setName] = useState(\n !source\n ? ''\n : mode.kind === 'duplicate'\n ? `${source.name} (copy)`\n : source.name,\n );\n const [description, setDescription] = useState(source?.description ?? '');\n const [perms, setPerms] = useState<string[]>(source?.permissions ?? []);\n const lockedName = mode.kind === 'edit' && mode.role.isSystem;\n\n const save = useMutation({\n mutationFn: async (): Promise<unknown> =>\n mode.kind === 'edit'\n ? platformAdminApi.updateRole(mode.role.id, {\n ...(lockedName\n ? {}\n : { name: name.trim(), description: description.trim() }),\n permissions: perms,\n })\n : platformAdminApi.createRole(\n name.trim(),\n perms,\n description.trim() || undefined,\n ),\n onSuccess: () => {\n toast({\n title:\n
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/roles/role-dialog.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useMutation } from '@tanstack/react-query';\nimport { Button, FormDialog, Input, useToast } from '@withpotter/ui-kit';\nimport { platformAdminApi, type RoleRow as Role } from '@withpotter/lib/admin-api';\nimport { PermissionPicker, toggleIn } from './permission-picker';\nimport styles from './roles.module.css';\n\nexport type RoleDialogMode =\n | { kind: 'create' }\n | { kind: 'edit'; role: Role }\n | { kind: 'duplicate'; role: Role };\n\nconst COPY = {\n create: { title: 'Create role', action: 'Create role' },\n edit: { title: 'Edit role', action: 'Save changes' },\n duplicate: { title: 'Duplicate role', action: 'Create copy' },\n} as const;\n\nconst FORM_ID = 'role-dialog-form';\n\n/** Create, edit or duplicate a role: name, purpose and grouped permissions. */\nexport function RoleDialog({\n mode,\n catalog,\n onClose,\n onSaved,\n}: {\n mode: RoleDialogMode | null;\n catalog: readonly string[];\n onClose: () => void;\n onSaved: () => void;\n}) {\n // Keep the last mode while the dialog animates out, and count each opening\n // so the form remounts fresh — a reopened Create must not show the last\n // role's name, nor a cancelled edit come back.\n const [shown, setShown] = useState(mode);\n const [opens, setOpens] = useState(0);\n const [wasOpen, setWasOpen] = useState(false);\n if (mode && mode !== shown) setShown(mode);\n if (Boolean(mode) !== wasOpen) {\n setWasOpen(Boolean(mode));\n if (mode) setOpens((n) => n + 1);\n }\n const active = mode ?? shown;\n\n if (!active) return null;\n return (\n <RoleForm\n key={`${opens}-${active.kind}-${'role' in active ? active.role.id : ''}`}\n open={mode !== null}\n mode={active}\n catalog={catalog}\n onClose={onClose}\n onSaved={onSaved}\n />\n );\n}\n\nfunction RoleForm({\n open,\n mode,\n catalog,\n onClose,\n onSaved,\n}: {\n open: boolean;\n mode: RoleDialogMode;\n catalog: readonly string[];\n onClose: () => void;\n onSaved: () => void;\n}) {\n const { toast } = useToast();\n const source = mode.kind === 'create' ? null : mode.role;\n const [name, setName] = useState(\n !source\n ? ''\n : mode.kind === 'duplicate'\n ? `${source.name} (copy)`\n : source.name,\n );\n const [description, setDescription] = useState(source?.description ?? '');\n const [perms, setPerms] = useState<string[]>(source?.permissions ?? []);\n const lockedName = mode.kind === 'edit' && mode.role.isSystem;\n\n const save = useMutation({\n mutationFn: async (): Promise<unknown> =>\n mode.kind === 'edit'\n ? platformAdminApi.updateRole(mode.role.id, {\n ...(lockedName\n ? {}\n : { name: name.trim(), description: description.trim() }),\n permissions: perms,\n })\n : platformAdminApi.createRole(\n name.trim(),\n perms,\n description.trim() || undefined,\n ),\n onSuccess: () => {\n toast({\n title: 'Sent for approval',\n description:\n 'Another super admin has to approve this before the role changes.',\n });\n onSaved();\n onClose();\n },\n onError: (error) =>\n toast({\n title: 'The role was not saved',\n description: error.message,\n variant: 'destructive',\n }),\n });\n const canSave = name.trim().length > 0 && !save.isPending;\n\n return (\n <FormDialog\n open={open}\n size=\"lg\"\n title={COPY[mode.kind].title}\n description={\n mode.kind === 'edit'\n ? `Changes apply to every admin holding ${mode.role.name}.`\n : 'Bundle permissions you can grant in one step.'\n }\n onClose={onClose}\n footer={\n <>\n <span className={styles.footNote}>\n {perms.length} permission{perms.length === 1 ? '' : 's'} selected\n </span>\n <div className={styles.footActions}>\n <Button type=\"button\" variant=\"secondary\" onClick={onClose}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n form={FORM_ID}\n disabled={!canSave}\n loading={save.isPending}\n >\n {COPY[mode.kind].action}\n </Button>\n </div>\n </>\n }\n >\n <form\n id={FORM_ID}\n className={styles.dialogBody}\n onSubmit={(e) => {\n e.preventDefault();\n if (canSave) save.mutate();\n }}\n >\n <div className={styles.dialogFields}>\n <Input\n label=\"Role name\"\n placeholder=\"e.g. Support\"\n value={name}\n disabled={lockedName}\n onChange={(e) => setName(e.target.value)}\n autoFocus={!lockedName}\n />\n <Input\n label=\"Description (optional)\"\n placeholder=\"What this role is for\"\n value={description}\n disabled={lockedName}\n onChange={(e) => setDescription(e.target.value)}\n />\n </div>\n {lockedName && (\n <p className={styles.note}>\n Built-in roles keep their name. You can still change what they\n grant.\n </p>\n )}\n <PermissionPicker\n keys={catalog}\n value={perms}\n onToggle={(key) => setPerms(toggleIn(key))}\n onSet={setPerms}\n />\n </form>\n </FormDialog>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import { Button, useToast } from "@withpotter/ui-kit";
|
|
5
|
+
import { platformAdminApi } from "@withpotter/lib/admin-api";
|
|
6
|
+
import { useAdminSession, useRoleChangeRequests } from "../hooks";
|
|
7
|
+
import { Panel } from "../_ui";
|
|
8
|
+
import section from "../audit/page.module.css";
|
|
9
|
+
function RoleRequests() {
|
|
10
|
+
const queryClient = useQueryClient();
|
|
11
|
+
const { toast } = useToast();
|
|
12
|
+
const { data: me } = useAdminSession();
|
|
13
|
+
const { data: requests = [] } = useRoleChangeRequests();
|
|
14
|
+
const pending = requests.filter((r) => r.status === "pending");
|
|
15
|
+
const decided = requests.filter((r) => r.status !== "pending").slice(0, 5);
|
|
16
|
+
const refresh = () => {
|
|
17
|
+
void queryClient.invalidateQueries({ queryKey: ["platform-admin", "roles"] });
|
|
18
|
+
void queryClient.invalidateQueries({ queryKey: ["platform-admin", "admins"] });
|
|
19
|
+
};
|
|
20
|
+
const decide = useMutation({
|
|
21
|
+
mutationFn: ({ id, approve }) => approve ? platformAdminApi.approveRoleChange(id) : platformAdminApi.rejectRoleChange(id),
|
|
22
|
+
onSuccess: (_, { approve }) => {
|
|
23
|
+
toast({ title: approve ? "Approved and applied" : "Rejected" });
|
|
24
|
+
refresh();
|
|
25
|
+
},
|
|
26
|
+
onError: (error) => toast({ title: "That was not saved", description: error.message, variant: "destructive" })
|
|
27
|
+
});
|
|
28
|
+
if (requests.length === 0) return null;
|
|
29
|
+
return /* @__PURE__ */ jsx("div", { className: section.section, children: /* @__PURE__ */ jsxs(Panel, { title: "Waiting for approval", count: pending.length, height: "auto", flush: true, children: [
|
|
30
|
+
/* @__PURE__ */ jsxs("ul", { children: [
|
|
31
|
+
pending.map((r) => {
|
|
32
|
+
const mine = me?.email === r.requestedByEmail;
|
|
33
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-3 px-4 py-3 text-sm", children: [
|
|
34
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
35
|
+
r.summary,
|
|
36
|
+
/* @__PURE__ */ jsxs("span", { className: "block text-xs text-[var(--text-tertiary)]", children: [
|
|
37
|
+
"Asked by ",
|
|
38
|
+
mine ? "you" : r.requestedByEmail,
|
|
39
|
+
" \xB7 ",
|
|
40
|
+
new Date(r.createdAt).toLocaleString()
|
|
41
|
+
] })
|
|
42
|
+
] }),
|
|
43
|
+
/* @__PURE__ */ jsxs("span", { className: "flex shrink-0 gap-2", children: [
|
|
44
|
+
!mine && /* @__PURE__ */ jsx(
|
|
45
|
+
Button,
|
|
46
|
+
{
|
|
47
|
+
size: "sm",
|
|
48
|
+
disabled: decide.isPending,
|
|
49
|
+
onClick: () => decide.mutate({ id: r.id, approve: true }),
|
|
50
|
+
children: "Approve"
|
|
51
|
+
}
|
|
52
|
+
),
|
|
53
|
+
/* @__PURE__ */ jsx(
|
|
54
|
+
Button,
|
|
55
|
+
{
|
|
56
|
+
size: "sm",
|
|
57
|
+
variant: "secondary",
|
|
58
|
+
disabled: decide.isPending,
|
|
59
|
+
onClick: () => decide.mutate({ id: r.id, approve: false }),
|
|
60
|
+
children: mine ? "Withdraw" : "Reject"
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
] })
|
|
64
|
+
] }, r.id);
|
|
65
|
+
}),
|
|
66
|
+
pending.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-4 py-3 text-sm text-[var(--text-secondary)]", children: "Nothing is waiting." })
|
|
67
|
+
] }),
|
|
68
|
+
decided.length > 0 && /* @__PURE__ */ jsx("ul", { className: "border-t border-[var(--border-subtle)]", children: decided.map((r) => /* @__PURE__ */ jsxs("li", { className: "px-4 py-2 text-xs text-[var(--text-secondary)]", children: [
|
|
69
|
+
r.summary,
|
|
70
|
+
" \xB7 ",
|
|
71
|
+
r.status === "approved" ? "approved" : "rejected",
|
|
72
|
+
" by ",
|
|
73
|
+
r.decidedByEmail
|
|
74
|
+
] }, r.id)) })
|
|
75
|
+
] }) });
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
RoleRequests
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=role-requests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/roles/role-requests.tsx"],"sourcesContent":["'use client';\n\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { Button, useToast } from '@withpotter/ui-kit';\nimport { platformAdminApi } from '@withpotter/lib/admin-api';\nimport { useAdminSession, useRoleChangeRequests } from '../hooks';\nimport { Panel } from '../_ui';\nimport section from '../audit/page.module.css';\n\n/**\n * Role changes wait here for a second super admin. You can approve or reject what others\n * asked for, and withdraw your own, but never approve your own.\n */\nexport function RoleRequests() {\n const queryClient = useQueryClient();\n const { toast } = useToast();\n const { data: me } = useAdminSession();\n const { data: requests = [] } = useRoleChangeRequests();\n const pending = requests.filter((r) => r.status === 'pending');\n const decided = requests.filter((r) => r.status !== 'pending').slice(0, 5);\n\n const refresh = () => {\n void queryClient.invalidateQueries({ queryKey: ['platform-admin', 'roles'] });\n void queryClient.invalidateQueries({ queryKey: ['platform-admin', 'admins'] });\n };\n const decide = useMutation({\n mutationFn: ({ id, approve }: { id: string; approve: boolean }) =>\n approve ? platformAdminApi.approveRoleChange(id) : platformAdminApi.rejectRoleChange(id),\n onSuccess: (_, { approve }) => {\n toast({ title: approve ? 'Approved and applied' : 'Rejected' });\n refresh();\n },\n onError: (error) =>\n toast({ title: 'That was not saved', description: error.message, variant: 'destructive' }),\n });\n\n if (requests.length === 0) return null;\n\n return (\n <div className={section.section}>\n <Panel title=\"Waiting for approval\" count={pending.length} height=\"auto\" flush>\n <ul>\n {pending.map((r) => {\n const mine = me?.email === r.requestedByEmail;\n return (\n <li key={r.id} className=\"flex items-center justify-between gap-3 px-4 py-3 text-sm\">\n <span className=\"min-w-0\">\n {r.summary}\n <span className=\"block text-xs text-[var(--text-tertiary)]\">\n Asked by {mine ? 'you' : r.requestedByEmail} · {new Date(r.createdAt).toLocaleString()}\n </span>\n </span>\n <span className=\"flex shrink-0 gap-2\">\n {!mine && (\n <Button\n size=\"sm\"\n disabled={decide.isPending}\n onClick={() => decide.mutate({ id: r.id, approve: true })}\n >\n Approve\n </Button>\n )}\n <Button\n size=\"sm\"\n variant=\"secondary\"\n disabled={decide.isPending}\n onClick={() => decide.mutate({ id: r.id, approve: false })}\n >\n {mine ? 'Withdraw' : 'Reject'}\n </Button>\n </span>\n </li>\n );\n })}\n {pending.length === 0 && (\n <li className=\"px-4 py-3 text-sm text-[var(--text-secondary)]\">Nothing is waiting.</li>\n )}\n </ul>\n {decided.length > 0 && (\n <ul className=\"border-t border-[var(--border-subtle)]\">\n {decided.map((r) => (\n <li key={r.id} className=\"px-4 py-2 text-xs text-[var(--text-secondary)]\">\n {r.summary} · {r.status === 'approved' ? 'approved' : 'rejected'} by {r.decidedByEmail}\n </li>\n ))}\n </ul>\n )}\n </Panel>\n </div>\n );\n}\n"],"mappings":";AAgDkB,SAME,KANF;AA9ClB,SAAS,aAAa,sBAAsB;AAC5C,SAAS,QAAQ,gBAAgB;AACjC,SAAS,wBAAwB;AACjC,SAAS,iBAAiB,6BAA6B;AACvD,SAAS,aAAa;AACtB,OAAO,aAAa;AAMb,SAAS,eAAe;AAC7B,QAAM,cAAc,eAAe;AACnC,QAAM,EAAE,MAAM,IAAI,SAAS;AAC3B,QAAM,EAAE,MAAM,GAAG,IAAI,gBAAgB;AACrC,QAAM,EAAE,MAAM,WAAW,CAAC,EAAE,IAAI,sBAAsB;AACtD,QAAM,UAAU,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS;AAC7D,QAAM,UAAU,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,MAAM,GAAG,CAAC;AAEzE,QAAM,UAAU,MAAM;AACpB,SAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,kBAAkB,OAAO,EAAE,CAAC;AAC5E,SAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,kBAAkB,QAAQ,EAAE,CAAC;AAAA,EAC/E;AACA,QAAM,SAAS,YAAY;AAAA,IACzB,YAAY,CAAC,EAAE,IAAI,QAAQ,MACzB,UAAU,iBAAiB,kBAAkB,EAAE,IAAI,iBAAiB,iBAAiB,EAAE;AAAA,IACzF,WAAW,CAAC,GAAG,EAAE,QAAQ,MAAM;AAC7B,YAAM,EAAE,OAAO,UAAU,yBAAyB,WAAW,CAAC;AAC9D,cAAQ;AAAA,IACV;AAAA,IACA,SAAS,CAAC,UACR,MAAM,EAAE,OAAO,sBAAsB,aAAa,MAAM,SAAS,SAAS,cAAc,CAAC;AAAA,EAC7F,CAAC;AAED,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,SACE,oBAAC,SAAI,WAAW,QAAQ,SACtB,+BAAC,SAAM,OAAM,wBAAuB,OAAO,QAAQ,QAAQ,QAAO,QAAO,OAAK,MAC5E;AAAA,yBAAC,QACE;AAAA,cAAQ,IAAI,CAAC,MAAM;AAClB,cAAM,OAAO,IAAI,UAAU,EAAE;AAC7B,eACE,qBAAC,QAAc,WAAU,6DACvB;AAAA,+BAAC,UAAK,WAAU,WACb;AAAA,cAAE;AAAA,YACH,qBAAC,UAAK,WAAU,6CAA4C;AAAA;AAAA,cAChD,OAAO,QAAQ,EAAE;AAAA,cAAiB;AAAA,cAAI,IAAI,KAAK,EAAE,SAAS,EAAE,eAAe;AAAA,eACvF;AAAA,aACF;AAAA,UACA,qBAAC,UAAK,WAAU,uBACb;AAAA,aAAC,QACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,UAAU,OAAO;AAAA,gBACjB,SAAS,MAAM,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,SAAS,KAAK,CAAC;AAAA,gBACzD;AAAA;AAAA,YAED;AAAA,YAEF;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,UAAU,OAAO;AAAA,gBACjB,SAAS,MAAM,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,SAAS,MAAM,CAAC;AAAA,gBAExD,iBAAO,aAAa;AAAA;AAAA,YACvB;AAAA,aACF;AAAA,aAzBO,EAAE,EA0BX;AAAA,MAEJ,CAAC;AAAA,MACA,QAAQ,WAAW,KAClB,oBAAC,QAAG,WAAU,kDAAiD,iCAAmB;AAAA,OAEtF;AAAA,IACC,QAAQ,SAAS,KAChB,oBAAC,QAAG,WAAU,0CACX,kBAAQ,IAAI,CAAC,MACZ,qBAAC,QAAc,WAAU,kDACtB;AAAA,QAAE;AAAA,MAAQ;AAAA,MAAI,EAAE,WAAW,aAAa,aAAa;AAAA,MAAW;AAAA,MAAK,EAAE;AAAA,SADjE,EAAE,EAEX,CACD,GACH;AAAA,KAEJ,GACF;AAEJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@withpotter/pages-admin",
|
|
3
|
-
"version": "0.13.0-next.
|
|
3
|
+
"version": "0.13.0-next.116",
|
|
4
4
|
"description": "Box console platform-admin routes: overview, revenue, accounts, tenants, admins, roles, audit.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@radix-ui/react-popover": "^1.1.23",
|
|
33
33
|
"@tanstack/react-query": "^5.90.20",
|
|
34
|
-
"@withpotter/lib": "^0.13.0-next.
|
|
35
|
-
"@withpotter/platform-admin": "^0.13.0-next.
|
|
36
|
-
"@withpotter/ui-kit": "^0.13.0-next.
|
|
34
|
+
"@withpotter/lib": "^0.13.0-next.116",
|
|
35
|
+
"@withpotter/platform-admin": "^0.13.0-next.116",
|
|
36
|
+
"@withpotter/ui-kit": "^0.13.0-next.116",
|
|
37
37
|
"lucide-react": "^0.562.0",
|
|
38
38
|
"motion": "^12.42.2"
|
|
39
39
|
},
|