@ploykit/ui 0.0.0-stage → 0.1.0

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.
Files changed (196) hide show
  1. package/README.md +41 -3
  2. package/dist/components/ConfirmDialog.d.ts +13 -0
  3. package/dist/components/ConfirmDialog.js +29 -0
  4. package/dist/components/CronInput.d.ts +15 -0
  5. package/dist/components/CronInput.js +112 -0
  6. package/dist/components/DataTable.d.ts +22 -0
  7. package/dist/components/DataTable.js +13 -0
  8. package/dist/components/ErrorBoundary.d.ts +16 -0
  9. package/dist/components/ErrorBoundary.js +24 -0
  10. package/dist/components/FormField.d.ts +10 -0
  11. package/dist/components/FormField.js +22 -0
  12. package/dist/components/ImpersonationBanner.d.ts +3 -0
  13. package/dist/components/ImpersonationBanner.js +10 -0
  14. package/dist/components/NotificationBell.d.ts +3 -0
  15. package/dist/components/NotificationBell.js +47 -0
  16. package/dist/components/OnboardingChecklist.d.ts +12 -0
  17. package/dist/components/OnboardingChecklist.js +43 -0
  18. package/dist/components/Page.d.ts +17 -0
  19. package/dist/components/Page.js +16 -0
  20. package/dist/components/SecretModal.d.ts +16 -0
  21. package/dist/components/SecretModal.js +45 -0
  22. package/dist/components/SettingsCard.d.ts +10 -0
  23. package/dist/components/SettingsCard.js +6 -0
  24. package/dist/components/SiteBanner.d.ts +6 -0
  25. package/dist/components/SiteBanner.js +11 -0
  26. package/dist/components/SiteBannerLayer.d.ts +4 -0
  27. package/dist/components/SiteBannerLayer.js +9 -0
  28. package/dist/components/UserMenu.d.ts +13 -0
  29. package/dist/components/UserMenu.js +30 -0
  30. package/dist/components/WorkspaceSwitcher.d.ts +1 -0
  31. package/dist/components/WorkspaceSwitcher.js +53 -0
  32. package/dist/components/toast.d.ts +7 -0
  33. package/dist/components/toast.js +11 -0
  34. package/dist/components/ui/Badge.d.ts +13 -0
  35. package/dist/components/ui/Badge.js +21 -0
  36. package/dist/components/ui/Button.d.ts +10 -0
  37. package/dist/components/ui/Button.js +33 -0
  38. package/dist/components/ui/Input.d.ts +3 -0
  39. package/dist/components/ui/Input.js +10 -0
  40. package/dist/components/ui/alert-dialog.d.ts +18 -0
  41. package/dist/components/ui/alert-dialog.js +41 -0
  42. package/dist/components/ui/avatar.d.ts +11 -0
  43. package/dist/components/ui/avatar.js +22 -0
  44. package/dist/components/ui/breadcrumb.d.ts +10 -0
  45. package/dist/components/ui/breadcrumb.js +36 -0
  46. package/dist/components/ui/card.d.ts +13 -0
  47. package/dist/components/ui/card.js +24 -0
  48. package/dist/components/ui/checkbox.d.ts +3 -0
  49. package/dist/components/ui/checkbox.js +8 -0
  50. package/dist/components/ui/dialog.d.ts +15 -0
  51. package/dist/components/ui/dialog.js +26 -0
  52. package/dist/components/ui/empty.d.ts +13 -0
  53. package/dist/components/ui/empty.js +36 -0
  54. package/dist/components/ui/field.d.ts +24 -0
  55. package/dist/components/ui/field.js +67 -0
  56. package/dist/components/ui/label.d.ts +3 -0
  57. package/dist/components/ui/label.js +6 -0
  58. package/dist/components/ui/menu.d.ts +29 -0
  59. package/dist/components/ui/menu.js +50 -0
  60. package/dist/components/ui/popover.d.ts +9 -0
  61. package/dist/components/ui/popover.js +22 -0
  62. package/dist/components/ui/progress.d.ts +7 -0
  63. package/dist/components/ui/progress.js +19 -0
  64. package/dist/components/ui/select.d.ts +15 -0
  65. package/dist/components/ui/select.js +33 -0
  66. package/dist/components/ui/separator.d.ts +3 -0
  67. package/dist/components/ui/separator.js +7 -0
  68. package/dist/components/ui/sheet.d.ts +14 -0
  69. package/dist/components/ui/sheet.js +36 -0
  70. package/dist/components/ui/sidebar.d.ts +63 -0
  71. package/dist/components/ui/sidebar.js +265 -0
  72. package/dist/components/ui/skeleton.d.ts +3 -0
  73. package/dist/components/ui/skeleton.js +6 -0
  74. package/dist/components/ui/switch.d.ts +5 -0
  75. package/dist/components/ui/switch.js +7 -0
  76. package/dist/components/ui/tabs.d.ts +10 -0
  77. package/dist/components/ui/tabs.js +28 -0
  78. package/dist/components/ui/tooltip.d.ts +6 -0
  79. package/dist/components/ui/tooltip.js +16 -0
  80. package/dist/hooks/use-mobile.d.ts +1 -0
  81. package/dist/hooks/use-mobile.js +15 -0
  82. package/dist/hooks/useApi.d.ts +10 -0
  83. package/dist/hooks/useApi.js +11 -0
  84. package/dist/hooks/useAudit.d.ts +39 -0
  85. package/dist/hooks/useAudit.js +55 -0
  86. package/dist/hooks/useAuth.d.ts +1 -0
  87. package/dist/hooks/useAuth.js +4 -0
  88. package/dist/hooks/useBilling.d.ts +44 -0
  89. package/dist/hooks/useBilling.js +64 -0
  90. package/dist/hooks/useLogin.d.ts +10 -0
  91. package/dist/hooks/useLogin.js +39 -0
  92. package/dist/hooks/useMembers.d.ts +125 -0
  93. package/dist/hooks/useMembers.js +127 -0
  94. package/dist/hooks/useNotificationPrefs.d.ts +20 -0
  95. package/dist/hooks/useNotificationPrefs.js +42 -0
  96. package/dist/hooks/usePasswordReset.d.ts +5 -0
  97. package/dist/hooks/usePasswordReset.js +12 -0
  98. package/dist/hooks/useSEO.d.ts +6 -0
  99. package/dist/hooks/useSEO.js +14 -0
  100. package/dist/hooks/useSessions.d.ts +20 -0
  101. package/dist/hooks/useSessions.js +38 -0
  102. package/dist/hooks/useSiteConfig.d.ts +12 -0
  103. package/dist/hooks/useSiteConfig.js +11 -0
  104. package/dist/hooks/useTokens.d.ts +36 -0
  105. package/dist/hooks/useTokens.js +35 -0
  106. package/dist/hooks/useUsage.d.ts +20 -0
  107. package/dist/hooks/useUsage.js +21 -0
  108. package/dist/hooks/useWorkspace.d.ts +26 -0
  109. package/dist/hooks/useWorkspace.js +32 -0
  110. package/dist/hooks/useZodForm.d.ts +6 -0
  111. package/dist/hooks/useZodForm.js +6 -0
  112. package/dist/hooks/ws.d.ts +4 -0
  113. package/dist/hooks/ws.js +21 -0
  114. package/dist/index.d.ts +124 -0
  115. package/dist/index.js +94 -0
  116. package/dist/layouts/AppShell.d.ts +20 -0
  117. package/dist/layouts/AppShell.js +21 -0
  118. package/dist/layouts/AuthShell.d.ts +7 -0
  119. package/dist/layouts/AuthShell.js +4 -0
  120. package/dist/layouts/SettingsShell.d.ts +19 -0
  121. package/dist/layouts/SettingsShell.js +21 -0
  122. package/dist/lib/api-error.d.ts +1 -0
  123. package/dist/lib/api-error.js +74 -0
  124. package/dist/lib/perm.d.ts +2 -0
  125. package/dist/lib/perm.js +7 -0
  126. package/dist/lib/utils.d.ts +12 -0
  127. package/dist/lib/utils.js +84 -0
  128. package/dist/lib/ws-query-bridge.d.ts +7 -0
  129. package/dist/lib/ws-query-bridge.js +35 -0
  130. package/dist/pages/LandingPage.d.ts +22 -0
  131. package/dist/pages/LandingPage.js +5 -0
  132. package/dist/pages/LoginPage.d.ts +15 -0
  133. package/dist/pages/LoginPage.js +87 -0
  134. package/dist/pages/account/NotificationPreferencesPage.d.ts +13 -0
  135. package/dist/pages/account/NotificationPreferencesPage.js +41 -0
  136. package/dist/pages/account/ProfileSettingsPage.d.ts +4 -0
  137. package/dist/pages/account/ProfileSettingsPage.js +96 -0
  138. package/dist/pages/account/SecuritySettingsPage.d.ts +1 -0
  139. package/dist/pages/account/SecuritySettingsPage.js +134 -0
  140. package/dist/pages/account/TokensPage.d.ts +1 -0
  141. package/dist/pages/account/TokensPage.js +117 -0
  142. package/dist/pages/admin/AdminSettingsPage.d.ts +1 -0
  143. package/dist/pages/admin/AdminSettingsPage.js +210 -0
  144. package/dist/pages/admin/AdminShell.d.ts +7 -0
  145. package/dist/pages/admin/AdminShell.js +79 -0
  146. package/dist/pages/admin/AuditPage.d.ts +1 -0
  147. package/dist/pages/admin/AuditPage.js +164 -0
  148. package/dist/pages/admin/OrdersPage.d.ts +1 -0
  149. package/dist/pages/admin/OrdersPage.js +236 -0
  150. package/dist/pages/admin/OverviewPage.d.ts +1 -0
  151. package/dist/pages/admin/OverviewPage.js +200 -0
  152. package/dist/pages/admin/SSOPage.d.ts +1 -0
  153. package/dist/pages/admin/SSOPage.js +53 -0
  154. package/dist/pages/admin/UserDetailPage.d.ts +1 -0
  155. package/dist/pages/admin/UserDetailPage.js +266 -0
  156. package/dist/pages/admin/UsersPage.d.ts +4 -0
  157. package/dist/pages/admin/UsersPage.js +201 -0
  158. package/dist/pages/admin/WebhookDeliveriesPage.d.ts +1 -0
  159. package/dist/pages/admin/WebhookDeliveriesPage.js +128 -0
  160. package/dist/pages/admin/WorkspaceDetailPage.d.ts +1 -0
  161. package/dist/pages/admin/WorkspaceDetailPage.js +202 -0
  162. package/dist/pages/admin/WorkspacesPage.d.ts +1 -0
  163. package/dist/pages/admin/WorkspacesPage.js +133 -0
  164. package/dist/pages/admin/shared.d.ts +8 -0
  165. package/dist/pages/admin/shared.js +23 -0
  166. package/dist/pages/auth/ForgotPasswordPage.d.ts +6 -0
  167. package/dist/pages/auth/ForgotPasswordPage.js +32 -0
  168. package/dist/pages/auth/InviteAcceptPage.d.ts +5 -0
  169. package/dist/pages/auth/InviteAcceptPage.js +40 -0
  170. package/dist/pages/auth/RegisterPage.d.ts +15 -0
  171. package/dist/pages/auth/RegisterPage.js +80 -0
  172. package/dist/pages/auth/ResetPasswordPage.d.ts +4 -0
  173. package/dist/pages/auth/ResetPasswordPage.js +71 -0
  174. package/dist/pages/auth/VerifyEmailPage.d.ts +1 -0
  175. package/dist/pages/auth/VerifyEmailPage.js +45 -0
  176. package/dist/pages/workspace/BillingPage.d.ts +4 -0
  177. package/dist/pages/workspace/BillingPage.js +183 -0
  178. package/dist/pages/workspace/BillingSuccessPage.d.ts +4 -0
  179. package/dist/pages/workspace/BillingSuccessPage.js +72 -0
  180. package/dist/pages/workspace/MembersPage.d.ts +3 -0
  181. package/dist/pages/workspace/MembersPage.js +262 -0
  182. package/dist/pages/workspace/SchedulesPage.d.ts +4 -0
  183. package/dist/pages/workspace/SchedulesPage.js +177 -0
  184. package/dist/pages/workspace/UsagePage.d.ts +15 -0
  185. package/dist/pages/workspace/UsagePage.js +74 -0
  186. package/dist/pages/workspace/WebhooksPage.d.ts +3 -0
  187. package/dist/pages/workspace/WebhooksPage.js +242 -0
  188. package/dist/pages/workspace/WorkspaceAuditPage.d.ts +3 -0
  189. package/dist/pages/workspace/WorkspaceAuditPage.js +109 -0
  190. package/dist/pages/workspace/WorkspaceGeneralPage.d.ts +4 -0
  191. package/dist/pages/workspace/WorkspaceGeneralPage.js +101 -0
  192. package/dist/provider/PloykitProvider.d.ts +50 -0
  193. package/dist/provider/PloykitProvider.js +240 -0
  194. package/dist/realtime.d.ts +3 -0
  195. package/dist/realtime.js +11 -0
  196. package/package.json +63 -6
@@ -0,0 +1,6 @@
1
+ import { type FieldValues, type UseFormProps, type UseFormReturn } from 'react-hook-form';
2
+ import type { ZodSchema } from 'zod';
3
+ export interface ZodFormOptions<T extends FieldValues> extends UseFormProps<T> {
4
+ schema: ZodSchema<T, T>;
5
+ }
6
+ export declare function useZodForm<T extends FieldValues>(opts: ZodFormOptions<T>): UseFormReturn<T>;
@@ -0,0 +1,6 @@
1
+ import { zodResolver } from '@hookform/resolvers/zod';
2
+ import { useForm } from 'react-hook-form';
3
+ export function useZodForm(opts) {
4
+ const { schema, ...rest } = opts;
5
+ return useForm({ ...rest, resolver: zodResolver(schema) });
6
+ }
@@ -0,0 +1,4 @@
1
+ import type { FrameHandler, ScopePrefix, WsClient, WsStatus } from '@ploykit/client';
2
+ export declare function useWsStatus(client: WsClient): WsStatus;
3
+ export declare function useWsScope(prefix: ScopePrefix, id: string | undefined, client: WsClient): void;
4
+ export declare function useWsFrame(type: string, handler: FrameHandler, client: WsClient): void;
@@ -0,0 +1,21 @@
1
+ import { useEffect, useRef, useSyncExternalStore } from 'react';
2
+ export function useWsStatus(client) {
3
+ return useSyncExternalStore((cb) => client.onStatus(cb), () => client.status, () => 'idle');
4
+ }
5
+ export function useWsScope(prefix, id, client) {
6
+ useEffect(() => {
7
+ if (!id)
8
+ return;
9
+ return client.subscribe(prefix, id);
10
+ }, [prefix, id, client]);
11
+ }
12
+ export function useWsFrame(type, handler, client) {
13
+ const ref = useRef(handler);
14
+ useEffect(() => {
15
+ ref.current = handler;
16
+ });
17
+ useEffect(() => {
18
+ const dispatch = (frame) => ref.current(frame);
19
+ return client.on(type, dispatch);
20
+ }, [type, client]);
21
+ }
@@ -0,0 +1,124 @@
1
+ export { PloykitProvider, queryKeys } from './provider/PloykitProvider';
2
+ export type { PKUser, PKWorkspace, PKSubscription, PKOrder, PKPlan } from './provider/PloykitProvider';
3
+ export { useAuth } from './hooks/useAuth';
4
+ export { useLogin } from './hooks/useLogin';
5
+ export { useWorkspace, useWorkspaceSwitch } from './hooks/useWorkspace';
6
+ export { useBilling, useUsagePreview, PlanGate } from './hooks/useBilling';
7
+ export { useApi, ApiError } from './hooks/useApi';
8
+ export type { ApiFetchOptions } from './hooks/useApi';
9
+ export { useSEO } from './hooks/useSEO';
10
+ export type { SeoMeta } from './hooks/useSEO';
11
+ export { useWsStatus, useWsScope, useWsFrame } from './hooks/ws';
12
+ export { createWsClient, defaultWsUrl } from './realtime';
13
+ export { bridgeWsToQuery } from './lib/ws-query-bridge';
14
+ export type { WsInvalidationRule } from './lib/ws-query-bridge';
15
+ export { AppShell } from './layouts/AppShell';
16
+ export type { NavItem } from './layouts/AppShell';
17
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, } from './components/ui/sidebar';
18
+ export { Separator } from './components/ui/separator';
19
+ export { Skeleton } from './components/ui/skeleton';
20
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './components/ui/tooltip';
21
+ export { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from './components/ui/sheet';
22
+ export { LoginPage } from './pages/LoginPage';
23
+ export type { OAuthProviderOption, LoginPageProps } from './pages/LoginPage';
24
+ export { LandingPage } from './pages/LandingPage';
25
+ export type { Feature as LandingFeature, Plan as LandingPlan } from './pages/LandingPage';
26
+ export { Button, buttonVariants } from './components/ui/Button';
27
+ export type { ButtonProps } from './components/ui/Button';
28
+ export { Input } from './components/ui/Input';
29
+ export { Badge, badgeVariants, StatusBadge } from './components/ui/Badge';
30
+ export { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, } from './components/ui/dialog';
31
+ export { PageHeader, PageLoading, PageEmpty, PageError } from './components/Page';
32
+ export { ConfirmProvider, useConfirm } from './components/ConfirmDialog';
33
+ export { NotificationBell } from './components/NotificationBell';
34
+ export { WorkspaceSwitcher } from './components/WorkspaceSwitcher';
35
+ export { SecretModal, ImpactConfirmation } from './components/SecretModal';
36
+ export { cn, statusTone, statusLabel, STATUS_TONE_CLASS, STATUS_LABEL, formatDate, formatDateTime, shortID } from './lib/utils';
37
+ export type { StatusTone } from './lib/utils';
38
+ export { roleSatisfies } from './lib/perm';
39
+ export type { Perm } from './lib/perm';
40
+ export { usePasswordReset } from './hooks/usePasswordReset';
41
+ export { ForgotPasswordPage } from './pages/auth/ForgotPasswordPage';
42
+ export type { ForgotPasswordPageProps } from './pages/auth/ForgotPasswordPage';
43
+ export { ResetPasswordPage } from './pages/auth/ResetPasswordPage';
44
+ export type { ResetPasswordPageProps } from './pages/auth/ResetPasswordPage';
45
+ export { VerifyEmailPage } from './pages/auth/VerifyEmailPage';
46
+ export { ErrorBoundary } from './components/ErrorBoundary';
47
+ export type { ErrorBoundaryProps } from './components/ErrorBoundary';
48
+ export { toast, Toaster } from './components/toast';
49
+ export { useZodForm } from './hooks/useZodForm';
50
+ export type { ZodFormOptions } from './hooks/useZodForm';
51
+ export { FormField } from './components/FormField';
52
+ export type { FormFieldProps } from './components/FormField';
53
+ export { DataTable } from './components/DataTable';
54
+ export type { Column, DataTableProps } from './components/DataTable';
55
+ export { Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldLegend, FieldSeparator, FieldSet, FieldContent, FieldTitle, } from './components/ui/field';
56
+ export { Label } from './components/ui/label';
57
+ export { Tabs, TabsList, TabsTrigger, TabsContent } from './components/ui/tabs';
58
+ export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, } from './components/ui/alert-dialog';
59
+ export { Avatar, AvatarImage, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarBadge, } from './components/ui/avatar';
60
+ export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from './components/ui/menu';
61
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from './components/ui/select';
62
+ export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, } from './components/ui/popover';
63
+ export { Switch } from './components/ui/switch';
64
+ export { Progress, ProgressTrack, ProgressIndicator, ProgressLabel, ProgressValue, } from './components/ui/progress';
65
+ export { Checkbox } from './components/ui/checkbox';
66
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, } from './components/ui/card';
67
+ export { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis, } from './components/ui/breadcrumb';
68
+ export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia, EmptyFooter, } from './components/ui/empty';
69
+ export { SettingsShell } from './layouts/SettingsShell';
70
+ export type { SettingsNavItem, SettingsNavGroup, SettingsShellProps } from './layouts/SettingsShell';
71
+ export { AuthShell } from './layouts/AuthShell';
72
+ export type { AuthShellProps } from './layouts/AuthShell';
73
+ export { SettingsCard } from './components/SettingsCard';
74
+ export type { SettingsCardProps } from './components/SettingsCard';
75
+ export { UserMenu } from './components/UserMenu';
76
+ export { OnboardingChecklist } from './components/OnboardingChecklist';
77
+ export { RegisterPage } from './pages/auth/RegisterPage';
78
+ export type { RegisterPageProps } from './pages/auth/RegisterPage';
79
+ export { InviteAcceptPage } from './pages/auth/InviteAcceptPage';
80
+ export { ProfileSettingsPage } from './pages/account/ProfileSettingsPage';
81
+ export type { ProfileSettingsPageProps } from './pages/account/ProfileSettingsPage';
82
+ export { SecuritySettingsPage } from './pages/account/SecuritySettingsPage';
83
+ export { TokensPage } from './pages/account/TokensPage';
84
+ export { NotificationPreferencesPage } from './pages/account/NotificationPreferencesPage';
85
+ export type { NotificationPreferencesPageProps, NotificationTypeMeta } from './pages/account/NotificationPreferencesPage';
86
+ export { MembersPage } from './pages/workspace/MembersPage';
87
+ export { WorkspaceGeneralPage } from './pages/workspace/WorkspaceGeneralPage';
88
+ export { WorkspaceAuditPage } from './pages/workspace/WorkspaceAuditPage';
89
+ export { UsagePage } from './pages/workspace/UsagePage';
90
+ export { BillingPage } from './pages/workspace/BillingPage';
91
+ export { BillingSuccessPage } from './pages/workspace/BillingSuccessPage';
92
+ export { WebhooksPage } from './pages/workspace/WebhooksPage';
93
+ export { useSessions, useRevokeSession, useRevokeAllSessions } from './hooks/useSessions';
94
+ export type { SessionInfo } from './hooks/useSessions';
95
+ export { useTokens, useCreateToken, useRevokeToken } from './hooks/useTokens';
96
+ export type { PAT, CreatePATResult } from './hooks/useTokens';
97
+ export { useNotificationPrefs, useSetNotificationPref } from './hooks/useNotificationPrefs';
98
+ export type { NotificationPreference } from './hooks/useNotificationPrefs';
99
+ export { useMembers, useMemberMutations, useMyInvitations, ASSIGNABLE_ROLES } from './hooks/useMembers';
100
+ export type { PKMember, PKInvitation, PKShareLink, PKInvitationWithWorkspace, CreateShareLinkResult, } from './hooks/useMembers';
101
+ export { useAudit, exportAuditCsv, auditQueryString, EMPTY_AUDIT_FILTERS } from './hooks/useAudit';
102
+ export type { AuditFilters, PKAuditEvent } from './hooks/useAudit';
103
+ export { useUsage } from './hooks/useUsage';
104
+ export type { PKUsageResp, PKUsageItem } from './hooks/useUsage';
105
+ export { apiErrorMessage } from './lib/api-error';
106
+ export { CronInput } from './components/CronInput';
107
+ export { SchedulesPage } from './pages/workspace/SchedulesPage';
108
+ export { SiteBanner } from './components/SiteBanner';
109
+ export { SiteBannerLayer } from './components/SiteBannerLayer';
110
+ export type { SiteBannerLayerProps } from './components/SiteBannerLayer';
111
+ export { useSiteConfig, SITE_CONFIG_QUERY_KEY } from './hooks/useSiteConfig';
112
+ export type { SiteConfig } from './hooks/useSiteConfig';
113
+ export { ImpersonationBanner } from './components/ImpersonationBanner';
114
+ export { AdminShell } from './pages/admin/AdminShell';
115
+ export { OverviewPage } from './pages/admin/OverviewPage';
116
+ export { UsersPage } from './pages/admin/UsersPage';
117
+ export { UserDetailPage } from './pages/admin/UserDetailPage';
118
+ export { WorkspacesPage } from './pages/admin/WorkspacesPage';
119
+ export { WorkspaceDetailPage } from './pages/admin/WorkspaceDetailPage';
120
+ export { OrdersPage } from './pages/admin/OrdersPage';
121
+ export { WebhookDeliveriesPage } from './pages/admin/WebhookDeliveriesPage';
122
+ export { AuditPage } from './pages/admin/AuditPage';
123
+ export { SSOPage } from './pages/admin/SSOPage';
124
+ export { AdminSettingsPage } from './pages/admin/AdminSettingsPage';
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
1
+ export { PloykitProvider, queryKeys } from './provider/PloykitProvider';
2
+ export { useAuth } from './hooks/useAuth';
3
+ export { useLogin } from './hooks/useLogin';
4
+ export { useWorkspace, useWorkspaceSwitch } from './hooks/useWorkspace';
5
+ export { useBilling, useUsagePreview, PlanGate } from './hooks/useBilling';
6
+ export { useApi, ApiError } from './hooks/useApi';
7
+ export { useSEO } from './hooks/useSEO';
8
+ export { useWsStatus, useWsScope, useWsFrame } from './hooks/ws';
9
+ export { createWsClient, defaultWsUrl } from './realtime';
10
+ export { bridgeWsToQuery } from './lib/ws-query-bridge';
11
+ export { AppShell } from './layouts/AppShell';
12
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, } from './components/ui/sidebar';
13
+ export { Separator } from './components/ui/separator';
14
+ export { Skeleton } from './components/ui/skeleton';
15
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './components/ui/tooltip';
16
+ export { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from './components/ui/sheet';
17
+ export { LoginPage } from './pages/LoginPage';
18
+ export { LandingPage } from './pages/LandingPage';
19
+ export { Button, buttonVariants } from './components/ui/Button';
20
+ export { Input } from './components/ui/Input';
21
+ export { Badge, badgeVariants, StatusBadge } from './components/ui/Badge';
22
+ export { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, } from './components/ui/dialog';
23
+ export { PageHeader, PageLoading, PageEmpty, PageError } from './components/Page';
24
+ export { ConfirmProvider, useConfirm } from './components/ConfirmDialog';
25
+ export { NotificationBell } from './components/NotificationBell';
26
+ export { WorkspaceSwitcher } from './components/WorkspaceSwitcher';
27
+ export { SecretModal, ImpactConfirmation } from './components/SecretModal';
28
+ export { cn, statusTone, statusLabel, STATUS_TONE_CLASS, STATUS_LABEL, formatDate, formatDateTime, shortID } from './lib/utils';
29
+ export { roleSatisfies } from './lib/perm';
30
+ export { usePasswordReset } from './hooks/usePasswordReset';
31
+ export { ForgotPasswordPage } from './pages/auth/ForgotPasswordPage';
32
+ export { ResetPasswordPage } from './pages/auth/ResetPasswordPage';
33
+ export { VerifyEmailPage } from './pages/auth/VerifyEmailPage';
34
+ export { ErrorBoundary } from './components/ErrorBoundary';
35
+ export { toast, Toaster } from './components/toast';
36
+ export { useZodForm } from './hooks/useZodForm';
37
+ export { FormField } from './components/FormField';
38
+ export { DataTable } from './components/DataTable';
39
+ export { Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldLegend, FieldSeparator, FieldSet, FieldContent, FieldTitle, } from './components/ui/field';
40
+ export { Label } from './components/ui/label';
41
+ export { Tabs, TabsList, TabsTrigger, TabsContent } from './components/ui/tabs';
42
+ export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, } from './components/ui/alert-dialog';
43
+ export { Avatar, AvatarImage, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarBadge, } from './components/ui/avatar';
44
+ export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from './components/ui/menu';
45
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from './components/ui/select';
46
+ export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, } from './components/ui/popover';
47
+ export { Switch } from './components/ui/switch';
48
+ export { Progress, ProgressTrack, ProgressIndicator, ProgressLabel, ProgressValue, } from './components/ui/progress';
49
+ export { Checkbox } from './components/ui/checkbox';
50
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, } from './components/ui/card';
51
+ export { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis, } from './components/ui/breadcrumb';
52
+ export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia, EmptyFooter, } from './components/ui/empty';
53
+ export { SettingsShell } from './layouts/SettingsShell';
54
+ export { AuthShell } from './layouts/AuthShell';
55
+ export { SettingsCard } from './components/SettingsCard';
56
+ export { UserMenu } from './components/UserMenu';
57
+ export { OnboardingChecklist } from './components/OnboardingChecklist';
58
+ export { RegisterPage } from './pages/auth/RegisterPage';
59
+ export { InviteAcceptPage } from './pages/auth/InviteAcceptPage';
60
+ export { ProfileSettingsPage } from './pages/account/ProfileSettingsPage';
61
+ export { SecuritySettingsPage } from './pages/account/SecuritySettingsPage';
62
+ export { TokensPage } from './pages/account/TokensPage';
63
+ export { NotificationPreferencesPage } from './pages/account/NotificationPreferencesPage';
64
+ export { MembersPage } from './pages/workspace/MembersPage';
65
+ export { WorkspaceGeneralPage } from './pages/workspace/WorkspaceGeneralPage';
66
+ export { WorkspaceAuditPage } from './pages/workspace/WorkspaceAuditPage';
67
+ export { UsagePage } from './pages/workspace/UsagePage';
68
+ export { BillingPage } from './pages/workspace/BillingPage';
69
+ export { BillingSuccessPage } from './pages/workspace/BillingSuccessPage';
70
+ export { WebhooksPage } from './pages/workspace/WebhooksPage';
71
+ export { useSessions, useRevokeSession, useRevokeAllSessions } from './hooks/useSessions';
72
+ export { useTokens, useCreateToken, useRevokeToken } from './hooks/useTokens';
73
+ export { useNotificationPrefs, useSetNotificationPref } from './hooks/useNotificationPrefs';
74
+ export { useMembers, useMemberMutations, useMyInvitations, ASSIGNABLE_ROLES } from './hooks/useMembers';
75
+ export { useAudit, exportAuditCsv, auditQueryString, EMPTY_AUDIT_FILTERS } from './hooks/useAudit';
76
+ export { useUsage } from './hooks/useUsage';
77
+ export { apiErrorMessage } from './lib/api-error';
78
+ export { CronInput } from './components/CronInput';
79
+ export { SchedulesPage } from './pages/workspace/SchedulesPage';
80
+ export { SiteBanner } from './components/SiteBanner';
81
+ export { SiteBannerLayer } from './components/SiteBannerLayer';
82
+ export { useSiteConfig, SITE_CONFIG_QUERY_KEY } from './hooks/useSiteConfig';
83
+ export { ImpersonationBanner } from './components/ImpersonationBanner';
84
+ export { AdminShell } from './pages/admin/AdminShell';
85
+ export { OverviewPage } from './pages/admin/OverviewPage';
86
+ export { UsersPage } from './pages/admin/UsersPage';
87
+ export { UserDetailPage } from './pages/admin/UserDetailPage';
88
+ export { WorkspacesPage } from './pages/admin/WorkspacesPage';
89
+ export { WorkspaceDetailPage } from './pages/admin/WorkspaceDetailPage';
90
+ export { OrdersPage } from './pages/admin/OrdersPage';
91
+ export { WebhookDeliveriesPage } from './pages/admin/WebhookDeliveriesPage';
92
+ export { AuditPage } from './pages/admin/AuditPage';
93
+ export { SSOPage } from './pages/admin/SSOPage';
94
+ export { AdminSettingsPage } from './pages/admin/AdminSettingsPage';
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface NavItem {
3
+ path: string;
4
+ label: string;
5
+ icon?: ReactNode;
6
+ }
7
+ export declare function AppShell({ children, nav, brand, logo, logoDark, sidebarFooter, headerRight, breadcrumbTail, settingsContent }: {
8
+ children: ReactNode;
9
+ nav?: NavItem[];
10
+ brand?: string;
11
+ /** 品牌图形(sidebar 顶栏,方形 mark 最佳,h-7 宽自适应);与 brand 文字横排。 */
12
+ logo?: string;
13
+ /** 暗色变体,经 prefers-color-scheme 切换——仅当应用主题跟随系统时才传;
14
+ * 固定亮色主题的应用不要传(否则深色系统的用户会在亮底上拿到暗色变体)。 */
15
+ logoDark?: string;
16
+ sidebarFooter?: ReactNode;
17
+ headerRight?: ReactNode;
18
+ breadcrumbTail?: string | null;
19
+ settingsContent?: ReactNode;
20
+ }): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useLocation, useNavigate } from 'react-router';
3
+ import { FolderKanban } from 'lucide-react';
4
+ import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, } from '../components/ui/sidebar';
5
+ import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbPage, BreadcrumbSeparator, } from '../components/ui/breadcrumb';
6
+ import { UserMenu } from '../components/UserMenu';
7
+ import { ImpersonationBanner } from '../components/ImpersonationBanner';
8
+ export function AppShell({ children, nav, brand, logo, logoDark, sidebarFooter, headerRight, breadcrumbTail = null, settingsContent }) {
9
+ const location = useLocation();
10
+ const navigate = useNavigate();
11
+ const items = nav ?? [
12
+ { path: '/app', label: '概览', icon: _jsx(FolderKanban, { size: 17, "aria-hidden": "true" }) },
13
+ ];
14
+ const selected = items.find((item) => location.pathname.startsWith(item.path));
15
+ const isSettingsMode = settingsContent !== undefined &&
16
+ (location.pathname.startsWith('/settings') || location.pathname.startsWith('/account'));
17
+ return (_jsxs(SidebarProvider, { children: [_jsxs(Sidebar, { collapsible: "icon", children: [_jsx(SidebarHeader, { children: _jsxs("div", { className: "flex items-center gap-2 px-2 pb-1 pt-1 group-data-[collapsible=icon]:justify-center", children: [logo && (_jsxs("picture", { children: [_jsx("source", { media: "(prefers-color-scheme: dark)", srcSet: logoDark ?? logo }), _jsx("img", { src: logo, alt: `${brand ?? 'App'} logo`, className: "h-7 w-auto group-data-[collapsible=icon]:w-7 group-data-[collapsible=icon]:object-contain" })] })), _jsx("div", { className: "text-lg font-bold text-foreground group-data-[collapsible=icon]:hidden", children: brand ?? 'App' })] }) }), _jsx(SidebarContent, { children: isSettingsMode ? (settingsContent) : (_jsx(SidebarGroup, { children: _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: items.map((item) => {
18
+ const active = selected?.path === item.path;
19
+ return (_jsx(SidebarMenuItem, { children: _jsxs(SidebarMenuButton, { isActive: active, "aria-current": active ? 'page' : undefined, tooltip: item.label, onClick: () => navigate(item.path), children: [item.icon, _jsx("span", { className: "truncate", children: item.label })] }) }, item.path));
20
+ }) }) }) })) }), _jsx(SidebarFooter, { className: "group-data-[collapsible=icon]:hidden", children: sidebarFooter })] }), _jsxs(SidebarInset, { children: [_jsx(ImpersonationBanner, {}), _jsxs("header", { className: "flex h-14 shrink-0 items-center gap-3 border-b border-border bg-background px-4", children: [_jsx(SidebarTrigger, {}), breadcrumbTail !== null ? (_jsx(Breadcrumb, { children: _jsxs(BreadcrumbList, { children: [selected && (_jsxs(_Fragment, { children: [_jsx(BreadcrumbItem, { children: _jsx(BreadcrumbPage, { children: selected.label }) }), _jsx(BreadcrumbSeparator, {})] })), _jsx(BreadcrumbItem, { children: _jsx(BreadcrumbPage, { children: breadcrumbTail }) })] }) })) : null, _jsx("div", { className: "flex-1" }), headerRight, _jsx(UserMenu, {})] }), _jsx("div", { className: "flex-1 overflow-auto p-6", children: children })] })] }));
21
+ }
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface AuthShellProps {
3
+ children: ReactNode;
4
+ brand?: ReactNode;
5
+ footer?: ReactNode;
6
+ }
7
+ export declare function AuthShell({ children, brand, footer }: AuthShellProps): import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function AuthShell({ children, brand, footer }) {
3
+ return (_jsx("div", { className: "flex min-h-svh w-full items-center justify-center bg-background p-6 md:p-10", children: _jsxs("div", { className: "w-full max-w-sm", children: [brand && _jsx("div", { className: "mb-6 flex flex-col items-center gap-2 text-center", children: brand }), children, footer && (_jsx("div", { className: "mt-6 text-center text-xs text-muted-foreground", children: footer }))] }) }));
4
+ }
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type Perm } from '../lib/perm';
3
+ export interface SettingsNavItem {
4
+ path: string;
5
+ label: string;
6
+ icon?: ReactNode;
7
+ perm?: Perm;
8
+ }
9
+ export interface SettingsNavGroup {
10
+ group: string;
11
+ items: SettingsNavItem[];
12
+ }
13
+ export interface SettingsShellProps {
14
+ nav?: SettingsNavGroup[];
15
+ children: ReactNode;
16
+ title?: string;
17
+ description?: string;
18
+ }
19
+ export declare function SettingsShell({ nav, children, title, description }: SettingsShellProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Link, useLocation, useNavigate } from 'react-router';
3
+ import { cn } from '../lib/utils';
4
+ import { roleSatisfies } from '../lib/perm';
5
+ import { useWorkspace } from '../hooks/useWorkspace';
6
+ import { buttonVariants } from '../components/ui/Button';
7
+ import { Separator } from '../components/ui/separator';
8
+ import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from '../components/ui/select';
9
+ export function SettingsShell({ nav, children, title = '设置', description }) {
10
+ const location = useLocation();
11
+ const navigate = useNavigate();
12
+ const role = useWorkspace().current?.role;
13
+ const isActive = (path) => location.pathname === path || location.pathname.startsWith(path + '/');
14
+ const visibleNav = (nav ?? [])
15
+ .map((g) => ({ ...g, items: g.items.filter((i) => roleSatisfies(role, i.perm)) }))
16
+ .filter((g) => g.items.length > 0);
17
+ const flatItems = visibleNav.flatMap((g) => g.items);
18
+ const activeItem = flatItems.find((i) => isActive(i.path));
19
+ const hasNav = visibleNav.length > 0;
20
+ return (_jsxs("div", { className: "mx-auto w-full max-w-6xl", children: [_jsxs("div", { children: [_jsx("div", { className: "text-2xl md:text-3xl font-bold tracking-tight", children: title }), description && _jsx("p", { className: "mt-1 text-base text-muted-foreground", children: description })] }), _jsx(Separator, { className: "my-4 lg:my-6" }), _jsxs("div", { className: cn('flex flex-col gap-6', hasNav && 'lg:flex-row lg:gap-0 lg:space-x-12'), children: [hasNav && (_jsx("div", { className: "lg:hidden", children: _jsxs(Select, { value: activeItem?.path ?? null, onValueChange: (v) => typeof v === 'string' && v && navigate(v), children: [_jsx(SelectTrigger, { className: "w-full", "aria-label": "\u8BBE\u7F6E\u5BFC\u822A", children: _jsx(SelectValue, { children: activeItem?.label ?? '跳转设置页' }) }), _jsx(SelectContent, { align: "start", children: visibleNav.map((group) => (_jsxs(SelectGroup, { children: [_jsx(SelectLabel, { children: group.group }), group.items.map((item) => (_jsx(SelectItem, { value: item.path, children: item.label }, item.path)))] }, group.group))) })] }) })), hasNav && (_jsx("aside", { className: "hidden lg:block lg:w-1/5 lg:self-start lg:sticky lg:top-0", children: _jsx("nav", { "aria-label": "\u8BBE\u7F6E\u5BFC\u822A", children: visibleNav.map((group) => (_jsxs("div", { className: "mb-6", children: [_jsx("p", { className: "mb-2 px-2 text-xs uppercase tracking-wider text-muted-foreground", children: group.group }), _jsx("ul", { className: "space-y-1", children: group.items.map((item) => (_jsx("li", { children: _jsxs(Link, { to: item.path, "aria-current": isActive(item.path) ? 'page' : undefined, className: cn(buttonVariants({ variant: 'ghost' }), 'w-full justify-start', isActive(item.path) && 'bg-muted'), children: [item.icon, item.label] }) }, item.path))) })] }, group.group))) }) })), _jsx("div", { className: "min-w-0 flex-1", children: children })] })] }));
21
+ }
@@ -0,0 +1 @@
1
+ export declare function apiErrorMessage(err: unknown, fallback?: string): string;
@@ -0,0 +1,74 @@
1
+ import { ApiError } from '@ploykit/client';
2
+ const KNOWN_MESSAGES = {
3
+ 'invalid credentials': '邮箱或密码不正确,请检查后重试',
4
+ 'current password incorrect': '当前密码不正确,请确认后重新输入',
5
+ '当前密码不正确,请确认后重新输入': '当前密码不正确,请确认后重新输入',
6
+ '新密码不能与当前密码相同': '新密码不能与当前密码相同,请换一个新密码',
7
+ 'email already registered': '该邮箱已注册,可直接登录或换个邮箱重试',
8
+ 'slug already taken': '该标识已被占用,请换个工作区名称重试',
9
+ 'already a member': '该用户已是本工作区成员,无需重复邀请',
10
+ 'invitation already pending': '该邮箱已有一封待处理的邀请,可先撤销旧邀请再重发',
11
+ 'target is already an owner': '对方已是所有者,无需重复转让所有权',
12
+ 'name required (<=64 chars)': '名称不能为空,且不超过 64 字节(约 21 个汉字)',
13
+ 'name required': '名称不能为空',
14
+ };
15
+ const KNOWN_MESSAGE_PREFIXES = [
16
+ ['slug must be', '标识格式有误:需 4-40 位小写字母、数字或连字符(-),且不能以连字符开头或结尾'],
17
+ ['last owner cannot leave', '最后一个所有者不能离开工作区,请先转移所有权或删除工作区'],
18
+ ['avatar_url must be', '头像链接格式有误:需以 http(s) 开头且不超过 2048 字符'],
19
+ ['workspace cannot be deleted', '工作区暂时无法删除:存在未满足的前置条件(如未完结订单或活跃资源),请先处理后再试'],
20
+ ['egress:', '回调地址不可达私网/被安全策略拦截,请改用公网 https 地址'],
21
+ ['egressx:', '回调地址不可达私网/被安全策略拦截,请改用公网 https 地址'],
22
+ ];
23
+ const KNOWN_CODES = {
24
+ E_QUOTA_EXCEEDED: '配额已用尽,可在 设置→计费 升级套餐后重试',
25
+ E_TIMEOUT: '请求超时,网络可能较慢,请稍后重试',
26
+ E_ABORTED: '请求已取消',
27
+ E_CHANNEL_TEST_UNSUPPORTED: '该支付渠道不支持测试连接(manual 渠道无外部端点可探测)',
28
+ E_MALFORMED_RESPONSE: '服务返回了无法识别的响应,请稍后重试;若持续出现请联系平台管理员',
29
+ E_VALIDATION: '请求参数有误,请检查填写内容后重试',
30
+ E_INVALID_CRON: 'Cron 表达式格式有误(应为 5 段:分 时 日 月 周),请检查后重试',
31
+ E_INVALID_TZ: '时区无效,请填写 IANA 时区名(如 Asia/Shanghai)',
32
+ E_CONFLICT: '操作与当前状态冲突,请刷新页面查看最新状态后再试',
33
+ };
34
+ function detailsText(details) {
35
+ return typeof details === 'string' && details.trim() !== '' ? details.trim() : undefined;
36
+ }
37
+ function knownMessage(message) {
38
+ const normalized = message.trim().toLowerCase();
39
+ const exact = KNOWN_MESSAGES[normalized];
40
+ if (exact)
41
+ return exact;
42
+ return KNOWN_MESSAGE_PREFIXES.find(([prefix]) => normalized.startsWith(prefix))?.[1];
43
+ }
44
+ export function apiErrorMessage(err, fallback = '操作失败') {
45
+ if (err instanceof ApiError) {
46
+ const byMessage = knownMessage(err.message);
47
+ if (byMessage)
48
+ return byMessage;
49
+ const details = detailsText(err.details);
50
+ if (details)
51
+ return details;
52
+ if (err.code && KNOWN_CODES[err.code])
53
+ return KNOWN_CODES[err.code];
54
+ if (err.status === 402) {
55
+ return '当前套餐不支持此操作。可在 设置→计费 升级套餐';
56
+ }
57
+ if (err.status === 401)
58
+ return '登录已过期或未登录,请重新登录后再试';
59
+ if (err.status === 429) {
60
+ const wait = err.retryAfter !== undefined && err.retryAfter > 0 ? `(约 ${err.retryAfter} 秒后可重试)` : '';
61
+ return `请求过于频繁,请稍后再试${wait}`;
62
+ }
63
+ if (err.status >= 500)
64
+ return '服务暂时不可用,请稍后重试;若持续出现请联系平台管理员';
65
+ if (err.status === 404)
66
+ return '不存在或无权访问';
67
+ if (err.status === 403)
68
+ return '权限不足' + (err.message && err.message !== 'forbidden' ? `:${err.message}` : '');
69
+ return err.message || fallback;
70
+ }
71
+ if (err instanceof TypeError)
72
+ return '网络连接失败,请检查网络后重试';
73
+ return err instanceof Error && err.message ? err.message : fallback;
74
+ }
@@ -0,0 +1,2 @@
1
+ export type Perm = 'admin' | 'owner';
2
+ export declare function roleSatisfies(role: string | undefined, perm?: Perm): boolean;
@@ -0,0 +1,7 @@
1
+ export function roleSatisfies(role, perm) {
2
+ if (perm === undefined)
3
+ return true;
4
+ if (perm === 'owner')
5
+ return role === 'owner';
6
+ return role === 'owner' || role === 'admin';
7
+ }
@@ -0,0 +1,12 @@
1
+ import { type ClassValue } from 'clsx';
2
+ export declare function cn(...inputs: ClassValue[]): string;
3
+ export type StatusTone = 'positive' | 'critical' | 'warning' | 'neutral' | 'brand';
4
+ export declare const STATUS_TONE_CLASS: Record<StatusTone, string>;
5
+ export declare function statusTone(status: string): StatusTone;
6
+ export declare const STATUS_LABEL: Record<string, string>;
7
+ export declare function statusLabel(status: string): string;
8
+ export declare function formatDate(iso: string): string;
9
+ export declare function formatDateTime(iso: string): string;
10
+ export declare function browserTzLabel(): string;
11
+ export declare function formatDateTimeTz(iso: string): string;
12
+ export declare function shortID(id: string, len?: number): string;
@@ -0,0 +1,84 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
6
+ export const STATUS_TONE_CLASS = {
7
+ positive: 'bg-(--success)/10 text-(--success) border-(--success)/30',
8
+ critical: 'bg-(--destructive)/10 text-(--destructive) border-(--destructive)/30',
9
+ warning: 'bg-(--warning)/15 text-(--warning) border-(--warning)/40',
10
+ neutral: 'bg-muted text-muted-foreground border-border',
11
+ brand: 'bg-primary/10 text-primary border-primary/20',
12
+ };
13
+ export function statusTone(status) {
14
+ const s = status.toLowerCase();
15
+ if (['active', 'paid', 'delivered', 'completed', 'success', 'running', 'entitled', 'done'].includes(s))
16
+ return 'positive';
17
+ if (['failed', 'dead', 'error', 'disabled', 'canceled', 'expired', 'removed'].includes(s))
18
+ return 'critical';
19
+ if (['pending', 'warned', 'suspended', 'retrying'].includes(s))
20
+ return 'warning';
21
+ if (['owner', 'admin', 'pro', 'recommended'].includes(s))
22
+ return 'brand';
23
+ return 'neutral';
24
+ }
25
+ export const STATUS_LABEL = {
26
+ pending: '待处理',
27
+ accepted: '已接受',
28
+ declined: '已拒绝',
29
+ revoked: '已撤销',
30
+ expired: '已过期',
31
+ redeemed: '已兑换',
32
+ paid: '已支付',
33
+ canceled: '已取消',
34
+ active: '生效中',
35
+ inactive: '未启用',
36
+ enabled: '已启用',
37
+ disabled: '已禁用',
38
+ delivered: '已投递',
39
+ failed: '失败',
40
+ retrying: '重试中',
41
+ completed: '已完成',
42
+ done: '已完成',
43
+ running: '运行中',
44
+ success: '成功',
45
+ error: '错误',
46
+ dead: '死信/投递失败',
47
+ warned: '已警告',
48
+ suspended: '已暂停',
49
+ removed: '已移除',
50
+ entitled: '已生效',
51
+ refunded: '已退款',
52
+ };
53
+ export function statusLabel(status) {
54
+ return STATUS_LABEL[status.toLowerCase()] ?? status;
55
+ }
56
+ export function formatDate(iso) {
57
+ return new Date(iso).toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' });
58
+ }
59
+ export function formatDateTime(iso) {
60
+ return new Date(iso).toLocaleString('zh-CN', {
61
+ year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false,
62
+ });
63
+ }
64
+ export function browserTzLabel() {
65
+ for (const nameType of ['shortOffset', 'short']) {
66
+ try {
67
+ const opts = { timeZoneName: nameType };
68
+ const parts = new Intl.DateTimeFormat('en-US', opts).formatToParts(new Date());
69
+ const label = parts.find((p) => p.type === 'timeZoneName')?.value;
70
+ if (label)
71
+ return label;
72
+ }
73
+ catch {
74
+ // 试下一档兜底
75
+ }
76
+ }
77
+ return '本地时区';
78
+ }
79
+ export function formatDateTimeTz(iso) {
80
+ return `${formatDateTime(iso)} (${browserTzLabel()})`;
81
+ }
82
+ export function shortID(id, len = 8) {
83
+ return id.slice(0, len);
84
+ }
@@ -0,0 +1,7 @@
1
+ import type { QueryClient, QueryKey } from '@tanstack/react-query';
2
+ import type { WsClient } from '@ploykit/client';
3
+ export interface WsInvalidationRule {
4
+ match: string | RegExp;
5
+ invalidate: readonly QueryKey[];
6
+ }
7
+ export declare function bridgeWsToQuery(ws: WsClient, queryClient: QueryClient, rules: readonly WsInvalidationRule[]): () => void;
@@ -0,0 +1,35 @@
1
+ function ruleMatches(rule, type) {
2
+ if (typeof rule.match === 'string')
3
+ return rule.match === type;
4
+ rule.match.lastIndex = 0;
5
+ return rule.match.test(type);
6
+ }
7
+ export function bridgeWsToQuery(ws, queryClient, rules) {
8
+ let sawOpen = false;
9
+ const offStatus = ws.onStatus((status) => {
10
+ if (status !== 'open')
11
+ return;
12
+ if (!sawOpen) {
13
+ sawOpen = true;
14
+ return;
15
+ }
16
+ for (const rule of rules) {
17
+ for (const key of rule.invalidate) {
18
+ queryClient.invalidateQueries({ queryKey: key });
19
+ }
20
+ }
21
+ });
22
+ const offFrame = ws.on('*', (frame) => {
23
+ for (const rule of rules) {
24
+ if (!ruleMatches(rule, frame.type))
25
+ continue;
26
+ for (const key of rule.invalidate) {
27
+ queryClient.invalidateQueries({ queryKey: key });
28
+ }
29
+ }
30
+ });
31
+ return () => {
32
+ offStatus();
33
+ offFrame();
34
+ };
35
+ }