@ploykit/ui 0.6.0 → 0.6.2

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 (222) hide show
  1. package/dist/AppProviders.js +2 -2
  2. package/dist/components/ConfirmDialog.js +1 -1
  3. package/dist/components/CronInput.js +3 -3
  4. package/dist/components/DataTable.js +4 -4
  5. package/dist/components/FormField.js +1 -1
  6. package/dist/components/ImpersonationBanner.js +1 -1
  7. package/dist/components/NotificationBell.js +1 -1
  8. package/dist/components/OnboardingChecklist.js +3 -3
  9. package/dist/components/Page.js +2 -2
  10. package/dist/components/ReauthDialog.js +4 -4
  11. package/dist/components/SecretModal.js +2 -2
  12. package/dist/components/SettingsCard.js +2 -2
  13. package/dist/components/SiteBanner.js +1 -1
  14. package/dist/components/SiteBannerLayer.js +1 -1
  15. package/dist/components/StatusBadge.js +1 -1
  16. package/dist/components/UserMenu.js +3 -3
  17. package/dist/components/WorkspaceSwitcher.js +7 -7
  18. package/dist/components/ui/alert-dialog.js +2 -2
  19. package/dist/components/ui/avatar.js +1 -1
  20. package/dist/components/ui/badge.js +1 -1
  21. package/dist/components/ui/breadcrumb.js +1 -1
  22. package/dist/components/ui/button.js +1 -1
  23. package/dist/components/ui/card.js +1 -1
  24. package/dist/components/ui/checkbox.js +1 -1
  25. package/dist/components/ui/dialog.js +2 -2
  26. package/dist/components/ui/dropdown-menu.js +1 -1
  27. package/dist/components/ui/empty.js +1 -1
  28. package/dist/components/ui/field.js +3 -3
  29. package/dist/components/ui/input.js +1 -1
  30. package/dist/components/ui/label.js +1 -1
  31. package/dist/components/ui/popover.js +1 -1
  32. package/dist/components/ui/progress.js +1 -1
  33. package/dist/components/ui/select.js +1 -1
  34. package/dist/components/ui/separator.js +1 -1
  35. package/dist/components/ui/sheet.js +2 -2
  36. package/dist/components/ui/sidebar.js +8 -8
  37. package/dist/components/ui/skeleton.js +1 -1
  38. package/dist/components/ui/switch.js +1 -1
  39. package/dist/components/ui/tabs.js +1 -1
  40. package/dist/components/ui/textarea.js +1 -1
  41. package/dist/components/ui/tooltip.js +1 -1
  42. package/dist/index.js +78 -78
  43. package/dist/layouts/AppShell.js +4 -4
  44. package/dist/layouts/SettingsShell.js +4 -4
  45. package/dist/pages/LoginPage.js +5 -5
  46. package/dist/pages/account/NotificationPreferencesPage.js +3 -3
  47. package/dist/pages/account/ProfileSettingsPage.js +9 -9
  48. package/dist/pages/account/SecuritySettingsPage.js +13 -13
  49. package/dist/pages/account/TokensPage.js +12 -12
  50. package/dist/pages/admin/AdminSettingsPage.js +11 -11
  51. package/dist/pages/admin/AdminShell.js +3 -3
  52. package/dist/pages/admin/AuditPage.js +8 -8
  53. package/dist/pages/admin/OrdersPage.js +12 -12
  54. package/dist/pages/admin/OverviewPage.js +13 -13
  55. package/dist/pages/admin/SSOPage.js +6 -6
  56. package/dist/pages/admin/UserDetailPage.js +13 -13
  57. package/dist/pages/admin/UsersPage.js +13 -13
  58. package/dist/pages/admin/WebhookDeliveriesPage.js +9 -9
  59. package/dist/pages/admin/WorkspaceDetailPage.js +14 -14
  60. package/dist/pages/admin/WorkspacesPage.js +9 -9
  61. package/dist/pages/admin/shared.js +1 -1
  62. package/dist/pages/auth/ForgotPasswordPage.js +4 -4
  63. package/dist/pages/auth/InviteAcceptPage.js +6 -6
  64. package/dist/pages/auth/RegisterPage.js +7 -7
  65. package/dist/pages/auth/ResetPasswordPage.js +5 -5
  66. package/dist/pages/auth/VerifyEmailPage.js +3 -3
  67. package/dist/pages/workspace/BillingPage.js +11 -11
  68. package/dist/pages/workspace/BillingSuccessPage.js +2 -2
  69. package/dist/pages/workspace/MembersPage.js +15 -15
  70. package/dist/pages/workspace/RolePermissionsPage.js +9 -9
  71. package/dist/pages/workspace/SchedulesPage.js +12 -12
  72. package/dist/pages/workspace/UsagePage.js +6 -6
  73. package/dist/pages/workspace/WebhooksPage.js +16 -16
  74. package/dist/pages/workspace/WorkspaceAuditPage.js +7 -7
  75. package/dist/pages/workspace/WorkspaceGeneralPage.js +9 -9
  76. package/package.json +6 -5
  77. package/src/AppProviders.tsx +29 -0
  78. package/src/components/ConfirmDialog.tsx +77 -0
  79. package/src/components/CronInput.tsx +195 -0
  80. package/src/components/DataTable.tsx +152 -0
  81. package/src/components/ErrorBoundary.tsx +46 -0
  82. package/src/components/FormField.tsx +56 -0
  83. package/src/components/ImpersonationBanner.tsx +23 -0
  84. package/src/components/NotificationBell.tsx +135 -0
  85. package/src/components/OnboardingChecklist.tsx +114 -0
  86. package/src/components/Page.tsx +67 -0
  87. package/src/components/ReauthDialog.tsx +94 -0
  88. package/src/components/SecretModal.tsx +147 -0
  89. package/src/components/SettingsCard.tsx +39 -0
  90. package/src/components/SiteBanner.tsx +32 -0
  91. package/src/components/SiteBannerLayer.tsx +29 -0
  92. package/src/components/StatusBadge.tsx +12 -0
  93. package/src/components/UserMenu.tsx +103 -0
  94. package/src/components/WorkspaceSwitcher.tsx +159 -0
  95. package/src/components/__tests__/AppProviders.test.tsx +111 -0
  96. package/src/components/__tests__/CronInput.test.tsx +180 -0
  97. package/src/components/__tests__/DataTable.test.tsx +208 -0
  98. package/src/components/__tests__/ErrorBoundary.test.tsx +61 -0
  99. package/src/components/__tests__/ImpersonationBanner.test.tsx +59 -0
  100. package/src/components/__tests__/NotificationBell.test.tsx +207 -0
  101. package/src/components/__tests__/OnboardingChecklist.test.tsx +97 -0
  102. package/src/components/__tests__/SecretModal.test.tsx +112 -0
  103. package/src/components/__tests__/SiteBannerLayer.test.tsx +44 -0
  104. package/src/components/__tests__/UserMenu.test.tsx +98 -0
  105. package/src/components/__tests__/WorkspaceSwitcher.test.tsx +162 -0
  106. package/src/components/__tests__/form.test.tsx +133 -0
  107. package/src/components/__tests__/semantic-tokens.test.ts +41 -0
  108. package/src/components/__tests__/smoke.test.tsx +11 -0
  109. package/src/components/__tests__/toast.test.tsx +27 -0
  110. package/src/components/toast.tsx +23 -0
  111. package/src/components/ui/__tests__/alert-dialog.test.tsx +66 -0
  112. package/src/components/ui/__tests__/avatar.test.tsx +30 -0
  113. package/src/components/ui/__tests__/checkbox.test.tsx +18 -0
  114. package/src/components/ui/__tests__/dropdown-menu.test.tsx +33 -0
  115. package/src/components/ui/__tests__/field.test.tsx +43 -0
  116. package/src/components/ui/__tests__/overlay-size.test.tsx +84 -0
  117. package/src/components/ui/__tests__/popover.test.tsx +32 -0
  118. package/src/components/ui/__tests__/progress.test.tsx +24 -0
  119. package/src/components/ui/__tests__/select-callback.test.tsx +72 -0
  120. package/src/components/ui/__tests__/select.test.tsx +48 -0
  121. package/src/components/ui/__tests__/sidebar.test.tsx +121 -0
  122. package/src/components/ui/__tests__/stock-mirror.test.ts +39 -0
  123. package/src/components/ui/__tests__/subpath.test.tsx +38 -0
  124. package/src/components/ui/__tests__/switch.test.tsx +24 -0
  125. package/src/components/ui/__tests__/tabs.test.tsx +24 -0
  126. package/src/components/ui/__tests__/textarea.test.tsx +41 -0
  127. package/src/components/ui/alert-dialog.tsx +187 -0
  128. package/src/components/ui/avatar.tsx +108 -0
  129. package/src/components/ui/badge.tsx +51 -0
  130. package/src/components/ui/breadcrumb.tsx +124 -0
  131. package/src/components/ui/button.tsx +57 -0
  132. package/src/components/ui/card.tsx +102 -0
  133. package/src/components/ui/checkbox.tsx +28 -0
  134. package/src/components/ui/dialog.tsx +166 -0
  135. package/src/components/ui/dropdown-menu.tsx +265 -0
  136. package/src/components/ui/empty.tsx +103 -0
  137. package/src/components/ui/field.tsx +238 -0
  138. package/src/components/ui/input.tsx +19 -0
  139. package/src/components/ui/label.tsx +17 -0
  140. package/src/components/ui/popover.tsx +89 -0
  141. package/src/components/ui/progress.tsx +82 -0
  142. package/src/components/ui/select.tsx +235 -0
  143. package/src/components/ui/separator.tsx +24 -0
  144. package/src/components/ui/sheet.tsx +146 -0
  145. package/src/components/ui/sidebar.tsx +723 -0
  146. package/src/components/ui/skeleton.tsx +13 -0
  147. package/src/components/ui/switch.tsx +29 -0
  148. package/src/components/ui/tabs.tsx +79 -0
  149. package/src/components/ui/textarea.tsx +26 -0
  150. package/src/components/ui/tooltip.tsx +63 -0
  151. package/src/dev.css +1 -0
  152. package/src/hooks/use-mobile.ts +19 -0
  153. package/src/index.ts +150 -0
  154. package/src/layouts/AppShell.tsx +163 -0
  155. package/src/layouts/AuthShell.tsx +24 -0
  156. package/src/layouts/SettingsShell.tsx +129 -0
  157. package/src/layouts/__tests__/AppShell.test.tsx +163 -0
  158. package/src/layouts/__tests__/SettingsShell.test.tsx +146 -0
  159. package/src/lib/__tests__/api-error.test.ts +163 -0
  160. package/src/lib/__tests__/utils.test.ts +21 -0
  161. package/src/lib/api-error.ts +98 -0
  162. package/src/lib/utils.ts +109 -0
  163. package/src/pages/LandingPage.tsx +88 -0
  164. package/src/pages/LoginPage.tsx +291 -0
  165. package/src/pages/__tests__/LandingPage.test.tsx +58 -0
  166. package/src/pages/__tests__/LoginPage.test.tsx +229 -0
  167. package/src/pages/__tests__/RegisterPage.test.tsx +138 -0
  168. package/src/pages/__tests__/ssr-smoke.test.tsx +99 -0
  169. package/src/pages/account/NotificationPreferencesPage.tsx +138 -0
  170. package/src/pages/account/ProfileSettingsPage.tsx +189 -0
  171. package/src/pages/account/SecuritySettingsPage.tsx +214 -0
  172. package/src/pages/account/TokensPage.tsx +211 -0
  173. package/src/pages/account/__tests__/NotificationPreferencesPage.test.tsx +150 -0
  174. package/src/pages/account/__tests__/ProfileSettingsPage.test.tsx +173 -0
  175. package/src/pages/account/__tests__/SecuritySettingsPage.test.tsx +212 -0
  176. package/src/pages/account/__tests__/TokensPage.test.tsx +180 -0
  177. package/src/pages/admin/AdminSettingsPage.tsx +564 -0
  178. package/src/pages/admin/AdminShell.tsx +199 -0
  179. package/src/pages/admin/AuditPage.tsx +338 -0
  180. package/src/pages/admin/OrdersPage.tsx +642 -0
  181. package/src/pages/admin/OverviewPage.tsx +559 -0
  182. package/src/pages/admin/SSOPage.tsx +166 -0
  183. package/src/pages/admin/UserDetailPage.tsx +641 -0
  184. package/src/pages/admin/UsersPage.tsx +462 -0
  185. package/src/pages/admin/WebhookDeliveriesPage.tsx +318 -0
  186. package/src/pages/admin/WorkspaceDetailPage.tsx +551 -0
  187. package/src/pages/admin/WorkspacesPage.tsx +299 -0
  188. package/src/pages/admin/__tests__/AdminSettingsPage.test.tsx +260 -0
  189. package/src/pages/admin/__tests__/OrdersPageMarkPaid.test.tsx +207 -0
  190. package/src/pages/admin/__tests__/OverviewPageActivity.test.tsx +213 -0
  191. package/src/pages/admin/__tests__/SSOPage.test.tsx +128 -0
  192. package/src/pages/admin/__tests__/UsersPageActions.test.tsx +188 -0
  193. package/src/pages/admin/__tests__/WorkspaceDetailPageTransfer.test.tsx +170 -0
  194. package/src/pages/admin/__tests__/WorkspacesPagePlans.test.tsx +138 -0
  195. package/src/pages/admin/shared.ts +42 -0
  196. package/src/pages/auth/ForgotPasswordPage.tsx +83 -0
  197. package/src/pages/auth/InviteAcceptPage.tsx +105 -0
  198. package/src/pages/auth/RegisterPage.tsx +204 -0
  199. package/src/pages/auth/ResetPasswordPage.tsx +150 -0
  200. package/src/pages/auth/VerifyEmailPage.tsx +76 -0
  201. package/src/pages/auth/__tests__/ForgotPasswordPage.test.tsx +67 -0
  202. package/src/pages/auth/__tests__/InviteAcceptPage.test.tsx +86 -0
  203. package/src/pages/auth/__tests__/ResetPasswordPage.test.tsx +115 -0
  204. package/src/pages/auth/__tests__/VerifyEmailPage.test.tsx +74 -0
  205. package/src/pages/workspace/BillingPage.tsx +361 -0
  206. package/src/pages/workspace/BillingSuccessPage.tsx +115 -0
  207. package/src/pages/workspace/MembersPage.tsx +568 -0
  208. package/src/pages/workspace/RolePermissionsPage.tsx +202 -0
  209. package/src/pages/workspace/SchedulesPage.tsx +387 -0
  210. package/src/pages/workspace/UsagePage.tsx +207 -0
  211. package/src/pages/workspace/WebhooksPage.tsx +506 -0
  212. package/src/pages/workspace/WorkspaceAuditPage.tsx +229 -0
  213. package/src/pages/workspace/WorkspaceGeneralPage.tsx +184 -0
  214. package/src/pages/workspace/__tests__/BillingPage.test.tsx +302 -0
  215. package/src/pages/workspace/__tests__/BillingSuccessPage.test.tsx +134 -0
  216. package/src/pages/workspace/__tests__/MembersPage.test.tsx +347 -0
  217. package/src/pages/workspace/__tests__/RolePermissionsPage.test.tsx +138 -0
  218. package/src/pages/workspace/__tests__/SchedulesPage.test.tsx +239 -0
  219. package/src/pages/workspace/__tests__/UsagePage.test.tsx +223 -0
  220. package/src/pages/workspace/__tests__/WebhooksPage.test.tsx +309 -0
  221. package/src/pages/workspace/__tests__/WorkspaceAuditPage.test.tsx +112 -0
  222. package/src/pages/workspace/__tests__/WorkspaceGeneralPage.test.tsx +164 -0
@@ -0,0 +1,13 @@
1
+ import { cn } from "../../lib/utils"
2
+
3
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
+ return (
5
+ <div
6
+ data-slot="skeleton"
7
+ className={cn("animate-pulse rounded-md bg-muted", className)}
8
+ {...props}
9
+ />
10
+ )
11
+ }
12
+
13
+ export { Skeleton }
@@ -0,0 +1,29 @@
1
+ import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
2
+ import { cn } from "../../lib/utils"
3
+
4
+ function Switch({
5
+ className,
6
+ size = "default",
7
+ ...props
8
+ }: SwitchPrimitive.Root.Props & {
9
+ size?: "sm" | "default"
10
+ }) {
11
+ return (
12
+ <SwitchPrimitive.Root
13
+ data-slot="switch"
14
+ data-size={size}
15
+ className={cn(
16
+ "peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none group-has-[:focus-visible]/field-label:border-transparent group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
17
+ className
18
+ )}
19
+ {...props}
20
+ >
21
+ <SwitchPrimitive.Thumb
22
+ data-slot="switch-thumb"
23
+ className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
24
+ />
25
+ </SwitchPrimitive.Root>
26
+ )
27
+ }
28
+
29
+ export { Switch }
@@ -0,0 +1,79 @@
1
+ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { cn } from "../../lib/utils"
4
+
5
+ function Tabs({
6
+ className,
7
+ orientation = "horizontal",
8
+ ...props
9
+ }: TabsPrimitive.Root.Props) {
10
+ return (
11
+ <TabsPrimitive.Root
12
+ data-slot="tabs"
13
+ data-orientation={orientation}
14
+ className={cn(
15
+ "group/tabs flex gap-2 data-horizontal:flex-col",
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+
23
+ const tabsListVariants = cva(
24
+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
25
+ {
26
+ variants: {
27
+ variant: {
28
+ default: "bg-muted",
29
+ line: "gap-1 bg-transparent",
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ variant: "default",
34
+ },
35
+ }
36
+ )
37
+
38
+ function TabsList({
39
+ className,
40
+ variant = "default",
41
+ ...props
42
+ }: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {
43
+ return (
44
+ <TabsPrimitive.List
45
+ data-slot="tabs-list"
46
+ data-variant={variant}
47
+ className={cn(tabsListVariants({ variant }), className)}
48
+ {...props}
49
+ />
50
+ )
51
+ }
52
+
53
+ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
54
+ return (
55
+ <TabsPrimitive.Tab
56
+ data-slot="tabs-trigger"
57
+ className={cn(
58
+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
59
+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
60
+ "data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
61
+ "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
62
+ className
63
+ )}
64
+ {...props}
65
+ />
66
+ )
67
+ }
68
+
69
+ function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
70
+ return (
71
+ <TabsPrimitive.Panel
72
+ data-slot="tabs-content"
73
+ className={cn("flex-1 text-sm outline-none", className)}
74
+ {...props}
75
+ />
76
+ )
77
+ }
78
+
79
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
@@ -0,0 +1,26 @@
1
+ import * as React from "react"
2
+ import { cn } from "../../lib/utils"
3
+
4
+ /**
5
+ * Textarea 多行文本域:与 Input 完全同族的 token 体系
6
+ * (border-input / bg-transparent / focus-visible ring / aria-invalid
7
+ * destructive / disabled / dark:bg-input/30),min-h 起步、支持
8
+ * ref 与 className 合并。
9
+ */
10
+ function Textarea({
11
+ className,
12
+ ...props
13
+ }: React.ComponentProps<"textarea">) {
14
+ return (
15
+ <textarea
16
+ data-slot="textarea"
17
+ className={cn(
18
+ "min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
19
+ className
20
+ )}
21
+ {...props}
22
+ />
23
+ )
24
+ }
25
+
26
+ export { Textarea }
@@ -0,0 +1,63 @@
1
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
2
+ import { cn } from "../../lib/utils"
3
+
4
+ function TooltipProvider({
5
+ delay = 0,
6
+ ...props
7
+ }: TooltipPrimitive.Provider.Props) {
8
+ return (
9
+ <TooltipPrimitive.Provider
10
+ data-slot="tooltip-provider"
11
+ delay={delay}
12
+ {...props}
13
+ />
14
+ )
15
+ }
16
+
17
+ function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
18
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
19
+ }
20
+
21
+ function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
22
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
23
+ }
24
+
25
+ function TooltipContent({
26
+ className,
27
+ side = "top",
28
+ sideOffset = 4,
29
+ align = "center",
30
+ alignOffset = 0,
31
+ children,
32
+ ...props
33
+ }: TooltipPrimitive.Popup.Props &
34
+ Pick<
35
+ TooltipPrimitive.Positioner.Props,
36
+ "align" | "alignOffset" | "side" | "sideOffset"
37
+ >) {
38
+ return (
39
+ <TooltipPrimitive.Portal>
40
+ <TooltipPrimitive.Positioner
41
+ align={align}
42
+ alignOffset={alignOffset}
43
+ side={side}
44
+ sideOffset={sideOffset}
45
+ className="isolate z-50"
46
+ >
47
+ <TooltipPrimitive.Popup
48
+ data-slot="tooltip-content"
49
+ className={cn(
50
+ "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
51
+ className
52
+ )}
53
+ {...props}
54
+ >
55
+ {children}
56
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
57
+ </TooltipPrimitive.Popup>
58
+ </TooltipPrimitive.Positioner>
59
+ </TooltipPrimitive.Portal>
60
+ )
61
+ }
62
+
63
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
package/src/dev.css ADDED
@@ -0,0 +1 @@
1
+ @import "tailwindcss";
@@ -0,0 +1,19 @@
1
+ import * as React from "react"
2
+
3
+ const MOBILE_BREAKPOINT = 768
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
7
+
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12
+ }
13
+ mql.addEventListener("change", onChange)
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15
+ return () => mql.removeEventListener("change", onChange)
16
+ }, [])
17
+
18
+ return !!isMobile
19
+ }
package/src/index.ts ADDED
@@ -0,0 +1,150 @@
1
+
2
+
3
+ export { AppShell } from './layouts/AppShell'
4
+ export type { NavItem } from './layouts/AppShell'
5
+ export {
6
+ Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent,
7
+ SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton,
8
+ SidebarMenuItem, SidebarProvider, SidebarTrigger,
9
+ } from './components/ui/sidebar'
10
+ export { Separator } from './components/ui/separator'
11
+ export { Skeleton } from './components/ui/skeleton'
12
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './components/ui/tooltip'
13
+ export { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter, SheetTrigger, SheetClose } from './components/ui/sheet'
14
+ export { AppProviders } from './AppProviders'
15
+
16
+ export { LoginPage } from './pages/LoginPage'
17
+ export type { OAuthProviderOption, LoginPageProps } from './pages/LoginPage'
18
+ export { LandingPage } from './pages/LandingPage'
19
+ export type { Feature as LandingFeature, Plan as LandingPlan } from './pages/LandingPage'
20
+
21
+ export { Button, buttonVariants } from './components/ui/button'
22
+ export { Input } from './components/ui/input'
23
+ export { Textarea } from './components/ui/textarea'
24
+ export { StatusBadge } from './components/StatusBadge'
25
+ export {
26
+ Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle,
27
+ DialogDescription, DialogFooter, DialogClose,
28
+ } from './components/ui/dialog'
29
+
30
+ export { PageHeader, PageLoading, PageEmpty, PageError } from './components/Page'
31
+
32
+ export { ConfirmProvider, useConfirm } from './components/ConfirmDialog'
33
+
34
+ export { NotificationBell } from './components/NotificationBell'
35
+
36
+ export { WorkspaceSwitcher } from './components/WorkspaceSwitcher'
37
+
38
+ export { SecretModal, ImpactConfirmation } from './components/SecretModal'
39
+
40
+ export { cn, statusTone, statusLabel, STATUS_TONE_CLASS, STATUS_LABEL, formatDate, formatDateTime, shortID } from './lib/utils'
41
+ export type { StatusTone } from './lib/utils'
42
+ export { ForgotPasswordPage } from './pages/auth/ForgotPasswordPage'
43
+ export type { ForgotPasswordPageProps } from './pages/auth/ForgotPasswordPage'
44
+ export { ResetPasswordPage } from './pages/auth/ResetPasswordPage'
45
+ export type { ResetPasswordPageProps } from './pages/auth/ResetPasswordPage'
46
+ export { VerifyEmailPage } from './pages/auth/VerifyEmailPage'
47
+
48
+ export { ErrorBoundary } from './components/ErrorBoundary'
49
+ export type { ErrorBoundaryProps } from './components/ErrorBoundary'
50
+ export { toast, Toaster } from './components/toast'
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
+
56
+ export {
57
+ Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldLegend,
58
+ FieldSeparator, FieldSet, FieldContent, FieldTitle,
59
+ } from './components/ui/field'
60
+ export { Label } from './components/ui/label'
61
+ export { Tabs, TabsList, TabsTrigger, TabsContent } from './components/ui/tabs'
62
+ export {
63
+ AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
64
+ AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay,
65
+ AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger,
66
+ } from './components/ui/alert-dialog'
67
+ export {
68
+ Avatar, AvatarImage, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarBadge,
69
+ } from './components/ui/avatar'
70
+ export {
71
+ DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent,
72
+ DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem,
73
+ DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator,
74
+ DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent,
75
+ } from './components/ui/dropdown-menu'
76
+ export {
77
+ Select, SelectContent, SelectGroup, SelectItem, SelectLabel,
78
+ SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue,
79
+ } from './components/ui/select'
80
+ export {
81
+ Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger,
82
+ } from './components/ui/popover'
83
+ export { Switch } from './components/ui/switch'
84
+ export {
85
+ Progress, ProgressTrack, ProgressIndicator, ProgressLabel, ProgressValue,
86
+ } from './components/ui/progress'
87
+ export { Checkbox } from './components/ui/checkbox'
88
+ export {
89
+ Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent,
90
+ } from './components/ui/card'
91
+ export {
92
+ Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage,
93
+ BreadcrumbSeparator, BreadcrumbEllipsis,
94
+ } from './components/ui/breadcrumb'
95
+ export {
96
+ Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia,
97
+ } from './components/ui/empty'
98
+
99
+ export { SettingsShell } from './layouts/SettingsShell'
100
+ export type { SettingsNavItem, SettingsNavGroup, SettingsShellProps } from './layouts/SettingsShell'
101
+ export { AuthShell } from './layouts/AuthShell'
102
+ export type { AuthShellProps } from './layouts/AuthShell'
103
+ export { SettingsCard } from './components/SettingsCard'
104
+ export type { SettingsCardProps } from './components/SettingsCard'
105
+ export { UserMenu } from './components/UserMenu'
106
+ export { OnboardingChecklist } from './components/OnboardingChecklist'
107
+
108
+ export { RegisterPage } from './pages/auth/RegisterPage'
109
+ export type { RegisterPageProps } from './pages/auth/RegisterPage'
110
+ export { InviteAcceptPage } from './pages/auth/InviteAcceptPage'
111
+
112
+ export { ProfileSettingsPage } from './pages/account/ProfileSettingsPage'
113
+ export type { ProfileSettingsPageProps } from './pages/account/ProfileSettingsPage'
114
+ export { SecuritySettingsPage } from './pages/account/SecuritySettingsPage'
115
+ export { TokensPage } from './pages/account/TokensPage'
116
+ export { NotificationPreferencesPage } from './pages/account/NotificationPreferencesPage'
117
+ export type { NotificationPreferencesPageProps, NotificationTypeMeta } from './pages/account/NotificationPreferencesPage'
118
+
119
+ export { MembersPage } from './pages/workspace/MembersPage'
120
+ export { RolePermissionsPage } from './pages/workspace/RolePermissionsPage'
121
+ export { ReauthDialog } from './components/ReauthDialog'
122
+ export { WorkspaceGeneralPage } from './pages/workspace/WorkspaceGeneralPage'
123
+ export { WorkspaceAuditPage } from './pages/workspace/WorkspaceAuditPage'
124
+ export { UsagePage } from './pages/workspace/UsagePage'
125
+ export { BillingPage } from './pages/workspace/BillingPage'
126
+ export { BillingSuccessPage } from './pages/workspace/BillingSuccessPage'
127
+ export { WebhooksPage } from './pages/workspace/WebhooksPage'
128
+
129
+ export { apiErrorMessage } from './lib/api-error'
130
+
131
+ export { CronInput } from './components/CronInput'
132
+ export { SchedulesPage } from './pages/workspace/SchedulesPage'
133
+
134
+ export { SiteBanner } from './components/SiteBanner'
135
+ export { SiteBannerLayer } from './components/SiteBannerLayer'
136
+ export type { SiteBannerLayerProps } from './components/SiteBannerLayer'
137
+
138
+ export { ImpersonationBanner } from './components/ImpersonationBanner'
139
+
140
+ export { AdminShell } from './pages/admin/AdminShell'
141
+ export { OverviewPage } from './pages/admin/OverviewPage'
142
+ export { UsersPage } from './pages/admin/UsersPage'
143
+ export { UserDetailPage } from './pages/admin/UserDetailPage'
144
+ export { WorkspacesPage } from './pages/admin/WorkspacesPage'
145
+ export { WorkspaceDetailPage } from './pages/admin/WorkspaceDetailPage'
146
+ export { OrdersPage } from './pages/admin/OrdersPage'
147
+ export { WebhookDeliveriesPage } from './pages/admin/WebhookDeliveriesPage'
148
+ export { AuditPage } from './pages/admin/AuditPage'
149
+ export { SSOPage } from './pages/admin/SSOPage'
150
+ export { AdminSettingsPage } from './pages/admin/AdminSettingsPage'
@@ -0,0 +1,163 @@
1
+
2
+ import { useLocation, useNavigate } from 'react-router'
3
+ import { useEffect, type ReactNode } from 'react'
4
+ import { FolderKanban } from 'lucide-react'
5
+ import {
6
+ Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent,
7
+ SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem,
8
+ SidebarProvider, SidebarTrigger,
9
+ } from '../components/ui/sidebar'
10
+ import {
11
+ Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbPage, BreadcrumbSeparator,
12
+ } from '../components/ui/breadcrumb'
13
+ import { UserMenu } from '../components/UserMenu'
14
+ import { ImpersonationBanner } from '../components/ImpersonationBanner'
15
+
16
+ // stock SidebarProvider 只写不读 sidebar_state cookie(写入供 SSR 框架回传 defaultOpen)。
17
+ // CSR 产品在组合层回读,保持跨会话折叠状态(P2-17,自 stock 镜像化后上提至此)。
18
+ function readSidebarCookieOpen(): boolean {
19
+ if (typeof document === 'undefined') return true // SSR 安全
20
+ return !document.cookie.split('; ').includes('sidebar_state=false')
21
+ }
22
+
23
+ export interface NavItem {
24
+ path: string
25
+ label: string
26
+
27
+ icon?: ReactNode
28
+ }
29
+
30
+ export function AppShell({ children, nav, brand, logo, logoDark, sidebarFooter, headerRight, breadcrumbTail = null, settingsContent }: {
31
+ children: ReactNode
32
+ nav?: NavItem[]
33
+ brand?: string
34
+
35
+ /** 品牌图形(sidebar 顶栏,方形 mark 最佳,h-7 宽自适应);与 brand 文字横排。 */
36
+ logo?: string
37
+ /** 暗色变体,经 prefers-color-scheme 切换——仅当应用主题跟随系统时才传;
38
+ * 固定亮色主题的应用不要传(否则深色系统的用户会在亮底上拿到暗色变体)。 */
39
+ logoDark?: string
40
+
41
+ sidebarFooter?: ReactNode
42
+
43
+ headerRight?: ReactNode
44
+
45
+ breadcrumbTail?: string | null
46
+
47
+ settingsContent?: ReactNode
48
+ }) {
49
+ const location = useLocation()
50
+ const navigate = useNavigate()
51
+
52
+ // 输入焦点时不劫持 Ctrl/Cmd+B(P3-1,自 stock 镜像化后上提至此):
53
+ // 捕获阶段拦截可编辑目标上的快捷键,阻断 stock SidebarProvider 的全局监听。
54
+ useEffect(() => {
55
+ const guard = (e: KeyboardEvent) => {
56
+ if (!(e.ctrlKey || e.metaKey) || e.key.toLowerCase() !== 'b') return
57
+ const t = e.target as HTMLElement | null
58
+ if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) {
59
+ e.stopImmediatePropagation()
60
+ }
61
+ }
62
+ window.addEventListener('keydown', guard, true)
63
+ return () => window.removeEventListener('keydown', guard, true)
64
+ }, [])
65
+
66
+ const items = nav ?? [
67
+ { path: '/app', label: '概览', icon: <FolderKanban size={17} aria-hidden="true" /> },
68
+ ]
69
+ const selected = items.find((item) => location.pathname.startsWith(item.path))
70
+ const isSettingsMode =
71
+ settingsContent !== undefined &&
72
+ (location.pathname.startsWith('/settings') || location.pathname.startsWith('/account'))
73
+
74
+ return (
75
+ <SidebarProvider defaultOpen={readSidebarCookieOpen()}>
76
+ <Sidebar collapsible="icon">
77
+ <SidebarHeader>
78
+ <div className="flex items-center gap-2 px-2 pb-1 pt-1 group-data-[collapsible=icon]:justify-center">
79
+ {logo && (
80
+ <picture>
81
+ <source media="(prefers-color-scheme: dark)" srcSet={logoDark ?? logo} />
82
+ <img
83
+ src={logo}
84
+ alt={`${brand ?? 'App'} logo`}
85
+ className="h-7 w-auto group-data-[collapsible=icon]:w-7 group-data-[collapsible=icon]:object-contain"
86
+ />
87
+ </picture>
88
+ )}
89
+ <div className="text-lg font-bold text-foreground group-data-[collapsible=icon]:hidden">
90
+ {brand ?? 'App'}
91
+ </div>
92
+ </div>
93
+ </SidebarHeader>
94
+ <SidebarContent>
95
+ {isSettingsMode ? (
96
+ settingsContent
97
+ ) : (
98
+ <SidebarGroup>
99
+ <SidebarGroupContent>
100
+ <SidebarMenu>
101
+ {items.map((item) => {
102
+ const active = selected?.path === item.path
103
+ return (
104
+ <SidebarMenuItem key={item.path}>
105
+ <SidebarMenuButton
106
+ isActive={active}
107
+ aria-current={active ? 'page' : undefined}
108
+ tooltip={item.label}
109
+ onClick={() => navigate(item.path)}
110
+ >
111
+ {item.icon}
112
+ <span className="truncate">{item.label}</span>
113
+ </SidebarMenuButton>
114
+ </SidebarMenuItem>
115
+ )
116
+ })}
117
+ </SidebarMenu>
118
+ </SidebarGroupContent>
119
+ </SidebarGroup>
120
+ )}
121
+ </SidebarContent>
122
+ <SidebarFooter className="group-data-[collapsible=icon]:hidden">
123
+ {sidebarFooter}
124
+ </SidebarFooter>
125
+ </Sidebar>
126
+ <SidebarInset>
127
+ {/* 模拟会话警示条(ADR 0008):impersonated_by 非空时持久显示在内容区
128
+ 最顶部;无退出按钮——退出由发起管理员控制。 */}
129
+ <ImpersonationBanner />
130
+ <header className="flex h-14 shrink-0 items-center gap-3 border-b border-border bg-background px-4">
131
+ <SidebarTrigger />
132
+ {/* 面包屑(sidebar-07 蓝本):nav 命中项为一级;SettingsShell 等二级布局通过
133
+ props.breadcrumbTail 追加层级。 */}
134
+ {breadcrumbTail !== null ? (
135
+ <Breadcrumb>
136
+ <BreadcrumbList>
137
+ {selected && (
138
+ <>
139
+ <BreadcrumbItem>
140
+ <BreadcrumbPage>{selected.label}</BreadcrumbPage>
141
+ </BreadcrumbItem>
142
+ <BreadcrumbSeparator />
143
+ </>
144
+ )}
145
+ <BreadcrumbItem>
146
+ <BreadcrumbPage>{breadcrumbTail}</BreadcrumbPage>
147
+ </BreadcrumbItem>
148
+ </BreadcrumbList>
149
+ </Breadcrumb>
150
+ ) : null}
151
+ <div className="flex-1" />
152
+ {headerRight}
153
+ <UserMenu />
154
+ </header>
155
+ {/* P3-23:不再嵌套 <main>——SidebarInset 已是 <main> 地标,文档树内
156
+ main 必须唯一(嵌套双 main 违反 landmark 唯一性)。滚动责任不变:
157
+ 本元素仍是 overflow-auto 滚动容器(SettingsShell sticky 的参照,
158
+ 见其 aside 注释),语义地标归 SidebarInset 承担。 */}
159
+ <div className="flex-1 overflow-auto p-6">{children}</div>
160
+ </SidebarInset>
161
+ </SidebarProvider>
162
+ )
163
+ }
@@ -0,0 +1,24 @@
1
+
2
+ import type { ReactNode } from 'react'
3
+
4
+ export interface AuthShellProps {
5
+ children: ReactNode
6
+
7
+ brand?: ReactNode
8
+
9
+ footer?: ReactNode
10
+ }
11
+
12
+ export function AuthShell({ children, brand, footer }: AuthShellProps) {
13
+ return (
14
+ <div className="flex min-h-svh w-full items-center justify-center bg-background p-6 md:p-10">
15
+ <div className="w-full max-w-sm">
16
+ {brand && <div className="mb-6 flex flex-col items-center gap-2 text-center">{brand}</div>}
17
+ {children}
18
+ {footer && (
19
+ <div className="mt-6 text-center text-xs text-muted-foreground">{footer}</div>
20
+ )}
21
+ </div>
22
+ </div>
23
+ )
24
+ }