@ploykit/ui 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.md +40 -2
  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 +61 -4
@@ -0,0 +1,10 @@
1
+ export interface UseLoginResult {
2
+ sendCode(email: string): Promise<void>;
3
+ submit(email: string, code: string): Promise<{
4
+ ok: boolean;
5
+ error?: string;
6
+ }>;
7
+ loading: boolean;
8
+ error: string | null;
9
+ }
10
+ export declare function useLogin(): UseLoginResult;
@@ -0,0 +1,39 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { api } from '@ploykit/client';
3
+ import { useAuthCtx } from '../provider/PloykitProvider';
4
+ export function useLogin() {
5
+ const [loading, setLoading] = useState(false);
6
+ const [error, setError] = useState(null);
7
+ const { refresh } = useAuthCtx();
8
+ const sendCode = useCallback(async (email) => {
9
+ setError(null);
10
+ setLoading(true);
11
+ try {
12
+ await api.post('/auth/send-code', { email });
13
+ }
14
+ catch (e) {
15
+ setError(e instanceof Error ? e.message : '发送失败');
16
+ }
17
+ finally {
18
+ setLoading(false);
19
+ }
20
+ }, []);
21
+ const submit = useCallback(async (email, code) => {
22
+ setError(null);
23
+ setLoading(true);
24
+ try {
25
+ await api.post('/auth/verify-code', { email, code });
26
+ await refresh();
27
+ return { ok: true };
28
+ }
29
+ catch (e) {
30
+ const msg = e instanceof Error ? e.message : '登录失败';
31
+ setError(msg);
32
+ return { ok: false, error: msg };
33
+ }
34
+ finally {
35
+ setLoading(false);
36
+ }
37
+ }, [refresh]);
38
+ return { sendCode, submit, loading, error };
39
+ }
@@ -0,0 +1,125 @@
1
+ import type { components } from '@ploykit/client';
2
+ export type PKMember = components['schemas']['Member'];
3
+ export type PKInvitation = components['schemas']['Invitation'];
4
+ export type PKShareLink = components['schemas']['ShareLink'];
5
+ export type PKInvitationWithWorkspace = components['schemas']['InvitationWithWorkspace'];
6
+ export type PKMemberPage = components['schemas']['MemberPage'];
7
+ export type PKInvitationPage = components['schemas']['InvitationPage'];
8
+ export type PKShareLinkPage = components['schemas']['ShareLinkPage'];
9
+ export interface ListQuery {
10
+ page: number;
11
+ pageSize?: number;
12
+ }
13
+ export interface ListPages {
14
+ members: number;
15
+ invitations: number;
16
+ shareLinks: number;
17
+ }
18
+ export declare const LIST_PAGE_SIZE = 50;
19
+ export interface CreateShareLinkResult {
20
+ link: PKShareLink;
21
+ code: string;
22
+ }
23
+ export declare const ASSIGNABLE_ROLES: readonly ["admin", "member"];
24
+ export declare function useMembers(pages: ListPages): {
25
+ wsId: string;
26
+ myRole: "owner" | "admin" | "member" | null;
27
+ members: {
28
+ user_id: string;
29
+ email: string;
30
+ display_name: string;
31
+ role: "owner" | "admin" | "member";
32
+ created_at: string;
33
+ }[];
34
+ membersTotal: number;
35
+ invitations: {
36
+ id: string;
37
+ workspace_id: string;
38
+ email: string;
39
+ role: "owner" | "admin" | "member";
40
+ status: "pending" | "accepted" | "declined" | "revoked" | "expired";
41
+ expires_at: string;
42
+ created_at: string;
43
+ }[];
44
+ invitationsTotal: number;
45
+ shareLinks: {
46
+ id: string;
47
+ code_prefix: string;
48
+ role: "admin" | "member";
49
+ max_uses: number;
50
+ uses: number;
51
+ expires_at: string;
52
+ created_at: string;
53
+ }[];
54
+ shareLinksTotal: number;
55
+ loading: boolean;
56
+ membersError: Error | null;
57
+ invitationsError: Error | null;
58
+ shareLinksError: Error | null;
59
+ refetchAll: () => Promise<void>;
60
+ };
61
+ export declare function useMemberMutations(): {
62
+ invite: import("@tanstack/react-query").UseMutationResult<{
63
+ id: string;
64
+ workspace_id: string;
65
+ email: string;
66
+ role: "owner" | "admin" | "member";
67
+ status: "pending" | "accepted" | "declined" | "revoked" | "expired";
68
+ expires_at: string;
69
+ created_at: string;
70
+ }, Error, {
71
+ email: string;
72
+ role: string;
73
+ }, unknown>;
74
+ revokeInvitation: import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
75
+ updateRole: import("@tanstack/react-query").UseMutationResult<unknown, Error, {
76
+ userId: string;
77
+ role: string;
78
+ }, unknown>;
79
+ removeMember: import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
80
+ transferOwnership: import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
81
+ createShareLink: import("@tanstack/react-query").UseMutationResult<CreateShareLinkResult, Error, {
82
+ role: string;
83
+ max_uses: number;
84
+ ttl_hours: number;
85
+ }, unknown>;
86
+ revokeShareLink: import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
87
+ };
88
+ export declare function useMyInvitations(): {
89
+ invitations: ({
90
+ id: string;
91
+ workspace_id: string;
92
+ email: string;
93
+ role: "owner" | "admin" | "member";
94
+ status: "pending" | "accepted" | "declined" | "revoked" | "expired";
95
+ expires_at: string;
96
+ created_at: string;
97
+ } & {
98
+ workspace_name: string;
99
+ workspace_slug: string;
100
+ })[];
101
+ loading: boolean;
102
+ error: Error | null;
103
+ refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<({
104
+ id: string;
105
+ workspace_id: string;
106
+ email: string;
107
+ role: "owner" | "admin" | "member";
108
+ status: "pending" | "accepted" | "declined" | "revoked" | "expired";
109
+ expires_at: string;
110
+ created_at: string;
111
+ } & {
112
+ workspace_name: string;
113
+ workspace_slug: string;
114
+ })[], Error>>;
115
+ accept: import("@tanstack/react-query").UseMutationResult<{
116
+ id: string;
117
+ slug: string;
118
+ name: string;
119
+ plan_code: string;
120
+ created_at: string;
121
+ } & {
122
+ role: "owner" | "admin" | "member";
123
+ }, Error, string, unknown>;
124
+ decline: import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
125
+ };
@@ -0,0 +1,127 @@
1
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2
+ import { useApi } from './useApi';
3
+ import { useWorkspace } from './useWorkspace';
4
+ import { queryKeys } from '../provider/PloykitProvider';
5
+ export const LIST_PAGE_SIZE = 50;
6
+ const listQuery = (q) => `?page=${q.page}&page_size=${q.pageSize ?? LIST_PAGE_SIZE}`;
7
+ export const ASSIGNABLE_ROLES = ['admin', 'member'];
8
+ export function useMembers(pages) {
9
+ const { current } = useWorkspace();
10
+ const api = useApi();
11
+ const wsId = current?.id ?? '';
12
+ const membersQ = useQuery({
13
+ queryKey: [...queryKeys.members(wsId), pages.members],
14
+ queryFn: () => api.get(`/api/workspaces/${wsId}/members${listQuery({ page: pages.members })}`),
15
+ enabled: !!wsId,
16
+ });
17
+ const invitationsQ = useQuery({
18
+ queryKey: [...queryKeys.invitations(wsId), pages.invitations],
19
+ queryFn: () => api.get(`/api/workspaces/${wsId}/invitations${listQuery({ page: pages.invitations })}`),
20
+ enabled: !!wsId,
21
+ });
22
+ const shareLinksQ = useQuery({
23
+ queryKey: [...queryKeys.shareLinks(wsId), pages.shareLinks],
24
+ queryFn: () => api.get(`/api/workspaces/${wsId}/share-links${listQuery({ page: pages.shareLinks })}`),
25
+ enabled: !!wsId,
26
+ });
27
+ return {
28
+ wsId,
29
+ myRole: current?.role ?? null,
30
+ members: membersQ.data?.items ?? [],
31
+ membersTotal: membersQ.data?.total ?? 0,
32
+ invitations: invitationsQ.data?.items ?? [],
33
+ invitationsTotal: invitationsQ.data?.total ?? 0,
34
+ shareLinks: shareLinksQ.data?.items ?? [],
35
+ shareLinksTotal: shareLinksQ.data?.total ?? 0,
36
+ loading: !!wsId && (membersQ.isPending || invitationsQ.isPending || shareLinksQ.isPending),
37
+ membersError: membersQ.error,
38
+ invitationsError: invitationsQ.error,
39
+ shareLinksError: shareLinksQ.error,
40
+ refetchAll: async () => {
41
+ await Promise.all([
42
+ membersQ.refetch(),
43
+ invitationsQ.refetch(),
44
+ shareLinksQ.refetch(),
45
+ ]);
46
+ },
47
+ };
48
+ }
49
+ export function useMemberMutations() {
50
+ const { current } = useWorkspace();
51
+ const api = useApi();
52
+ const qc = useQueryClient();
53
+ const wsId = current?.id ?? '';
54
+ const base = `/api/workspaces/${wsId}`;
55
+ const invalidateAll = async () => {
56
+ await Promise.all([
57
+ qc.invalidateQueries({ queryKey: queryKeys.members(wsId) }),
58
+ qc.invalidateQueries({ queryKey: queryKeys.invitations(wsId) }),
59
+ qc.invalidateQueries({ queryKey: queryKeys.shareLinks(wsId) }),
60
+ ]);
61
+ };
62
+ const invite = useMutation({
63
+ mutationFn: (v) => api.post(`${base}/invitations`, v),
64
+ onSuccess: invalidateAll,
65
+ });
66
+ const revokeInvitation = useMutation({
67
+ mutationFn: (invId) => api.delete(`${base}/invitations/${invId}`),
68
+ onSuccess: invalidateAll,
69
+ });
70
+ const updateRole = useMutation({
71
+ mutationFn: (v) => api.patch(`${base}/members/${v.userId}`, { role: v.role }),
72
+ onSuccess: invalidateAll,
73
+ });
74
+ const removeMember = useMutation({
75
+ mutationFn: (userId) => api.delete(`${base}/members/${userId}`),
76
+ onSuccess: invalidateAll,
77
+ });
78
+ const transferOwnership = useMutation({
79
+ mutationFn: (userId) => api.post(`${base}/transfer-ownership`, { user_id: userId }),
80
+ onSuccess: async () => {
81
+ await invalidateAll();
82
+ await qc.invalidateQueries({ queryKey: queryKeys.workspace });
83
+ },
84
+ });
85
+ const createShareLink = useMutation({
86
+ mutationFn: (v) => api.post(`${base}/share-links`, v),
87
+ onSuccess: invalidateAll,
88
+ });
89
+ const revokeShareLink = useMutation({
90
+ mutationFn: (linkId) => api.delete(`${base}/share-links/${linkId}`),
91
+ onSuccess: invalidateAll,
92
+ });
93
+ return {
94
+ invite, revokeInvitation, updateRole, removeMember, transferOwnership,
95
+ createShareLink, revokeShareLink,
96
+ };
97
+ }
98
+ export function useMyInvitations() {
99
+ const api = useApi();
100
+ const qc = useQueryClient();
101
+ const list = useQuery({
102
+ queryKey: queryKeys.myInvitations,
103
+ queryFn: () => api.get('/api/invitations/mine'),
104
+ });
105
+ const invalidate = () => qc.invalidateQueries({ queryKey: queryKeys.myInvitations });
106
+ const accept = useMutation({
107
+ mutationFn: (id) => api.post(`/api/invitations/${id}/accept`),
108
+ onSuccess: async () => {
109
+ await Promise.all([
110
+ invalidate(),
111
+ qc.invalidateQueries({ queryKey: queryKeys.workspace }),
112
+ ]);
113
+ },
114
+ });
115
+ const decline = useMutation({
116
+ mutationFn: (id) => api.post(`/api/invitations/${id}/decline`),
117
+ onSuccess: invalidate,
118
+ });
119
+ return {
120
+ invitations: list.data ?? [],
121
+ loading: list.isPending,
122
+ error: list.error,
123
+ refetch: list.refetch,
124
+ accept,
125
+ decline,
126
+ };
127
+ }
@@ -0,0 +1,20 @@
1
+ export interface NotificationPreference {
2
+ notification_type: string;
3
+ email_enabled: boolean;
4
+ in_app_enabled: boolean;
5
+ updated_at: string;
6
+ }
7
+ export declare function useNotificationPrefs(): {
8
+ prefs: NotificationPreference[];
9
+ loading: boolean;
10
+ error: Error | null;
11
+ refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<NotificationPreference[], Error>>;
12
+ };
13
+ export declare function useSetNotificationPref(): {
14
+ setPref: (type: string, channels: {
15
+ email_enabled?: boolean;
16
+ in_app_enabled?: boolean;
17
+ }) => Promise<NotificationPreference>;
18
+ loading: boolean;
19
+ error: Error | null;
20
+ };
@@ -0,0 +1,42 @@
1
+ import { useCallback } from 'react';
2
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
3
+ import { api } from '@ploykit/client';
4
+ import { queryKeys } from '../provider/PloykitProvider';
5
+ export function useNotificationPrefs() {
6
+ const q = useQuery({
7
+ queryKey: queryKeys.notificationPrefs,
8
+ queryFn: async () => {
9
+ const data = await api.get('/api/notification-preferences');
10
+ return data?.items ?? [];
11
+ },
12
+ });
13
+ return { prefs: q.data ?? [], loading: q.isPending, error: q.error, refetch: q.refetch };
14
+ }
15
+ export function useSetNotificationPref() {
16
+ const qc = useQueryClient();
17
+ const m = useMutation({
18
+ mutationFn: (v) => api.put(`/api/notification-preferences/${encodeURIComponent(v.type)}`, { email_enabled: v.email_enabled, in_app_enabled: v.in_app_enabled }),
19
+ onMutate: async (v) => {
20
+ await qc.cancelQueries({ queryKey: queryKeys.notificationPrefs });
21
+ const prev = qc.getQueryData(queryKeys.notificationPrefs) ?? [];
22
+ const existing = prev.find((p) => p.notification_type === v.type);
23
+ const next = {
24
+ notification_type: v.type,
25
+ email_enabled: v.email_enabled ?? existing?.email_enabled ?? true,
26
+ in_app_enabled: v.in_app_enabled ?? existing?.in_app_enabled ?? true,
27
+ updated_at: new Date().toISOString(),
28
+ };
29
+ qc.setQueryData(queryKeys.notificationPrefs, existing
30
+ ? prev.map((p) => (p.notification_type === v.type ? next : p))
31
+ : [...prev, next]);
32
+ return { prev };
33
+ },
34
+ onError: (_err, _v, ctx) => {
35
+ if (ctx?.prev)
36
+ qc.setQueryData(queryKeys.notificationPrefs, ctx.prev);
37
+ },
38
+ onSettled: () => qc.invalidateQueries({ queryKey: queryKeys.notificationPrefs }),
39
+ });
40
+ const setPref = useCallback((type, channels) => m.mutateAsync({ type, ...channels }), [m]);
41
+ return { setPref, loading: m.isPending, error: m.error };
42
+ }
@@ -0,0 +1,5 @@
1
+ export interface UsePasswordResetResult {
2
+ requestReset(email: string): Promise<void>;
3
+ reset(email: string, token: string, newPassword: string): Promise<void>;
4
+ }
5
+ export declare function usePasswordReset(): UsePasswordResetResult;
@@ -0,0 +1,12 @@
1
+ import { useCallback } from 'react';
2
+ import { useApi } from './useApi';
3
+ export function usePasswordReset() {
4
+ const api = useApi();
5
+ const requestReset = useCallback(async (email) => {
6
+ await api.post('/auth/forgot-password', { email });
7
+ }, [api]);
8
+ const reset = useCallback(async (email, token, newPassword) => {
9
+ await api.post('/auth/reset-password', { email, token, new_password: newPassword });
10
+ }, [api]);
11
+ return { requestReset, reset };
12
+ }
@@ -0,0 +1,6 @@
1
+ export interface SeoMeta {
2
+ title?: string;
3
+ description?: string;
4
+ ogImage?: string;
5
+ }
6
+ export declare function useSEO(meta: SeoMeta): void;
@@ -0,0 +1,14 @@
1
+ import { emitDirective } from '@ploykit/runtime';
2
+ export function useSEO(meta) {
3
+ const entries = [];
4
+ if (meta.title)
5
+ entries.push({ tag: 'title', children: meta.title });
6
+ if (meta.description) {
7
+ entries.push({ tag: 'meta', attrs: { name: 'description', content: meta.description } });
8
+ }
9
+ if (meta.ogImage) {
10
+ entries.push({ tag: 'meta', attrs: { property: 'og:image', content: meta.ogImage } });
11
+ entries.push({ tag: 'meta', attrs: { name: 'twitter:card', content: 'summary_large_image' } });
12
+ }
13
+ emitDirective('head', entries);
14
+ }
@@ -0,0 +1,20 @@
1
+ import type { components } from '@ploykit/client';
2
+ export type SessionInfo = components['schemas']['SessionInfo'] & {
3
+ current?: boolean;
4
+ };
5
+ export declare function useSessions(): {
6
+ sessions: SessionInfo[];
7
+ loading: boolean;
8
+ error: Error | null;
9
+ refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<SessionInfo[], Error>>;
10
+ };
11
+ export declare function useRevokeSession(): {
12
+ revoke: (id: string) => Promise<void>;
13
+ loading: boolean;
14
+ error: Error | null;
15
+ };
16
+ export declare function useRevokeAllSessions(): {
17
+ revokeAll: () => Promise<void>;
18
+ loading: boolean;
19
+ error: Error | null;
20
+ };
@@ -0,0 +1,38 @@
1
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2
+ import { api } from '@ploykit/client';
3
+ import { queryKeys } from '../provider/PloykitProvider';
4
+ function normalize(data) {
5
+ const list = Array.isArray(data) ? data : data?.items;
6
+ return Array.isArray(list) ? list : [];
7
+ }
8
+ export function useSessions() {
9
+ const q = useQuery({
10
+ queryKey: queryKeys.sessions,
11
+ queryFn: async () => normalize(await api.get('/auth/sessions')),
12
+ });
13
+ return { sessions: q.data ?? [], loading: q.isPending, error: q.error, refetch: q.refetch };
14
+ }
15
+ export function useRevokeSession() {
16
+ const qc = useQueryClient();
17
+ const m = useMutation({
18
+ mutationFn: (id) => api.delete(`/auth/sessions/${id}`),
19
+ onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.sessions }),
20
+ });
21
+ return {
22
+ revoke: (id) => m.mutateAsync(id),
23
+ loading: m.isPending,
24
+ error: m.error,
25
+ };
26
+ }
27
+ export function useRevokeAllSessions() {
28
+ const qc = useQueryClient();
29
+ const m = useMutation({
30
+ mutationFn: () => api.delete('/auth/sessions'),
31
+ onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.sessions }),
32
+ });
33
+ return {
34
+ revokeAll: () => m.mutateAsync(),
35
+ loading: m.isPending,
36
+ error: m.error,
37
+ };
38
+ }
@@ -0,0 +1,12 @@
1
+ export declare const SITE_CONFIG_QUERY_KEY: readonly ["site-config"];
2
+ export interface SiteConfig {
3
+ oauth_providers?: string[];
4
+ billing_channels?: string[];
5
+ banner_text?: string;
6
+ banner_kind?: string;
7
+ maintenance_mode?: string;
8
+ allow_signup?: string;
9
+ allowed_domains?: string;
10
+ mail_mode?: string;
11
+ }
12
+ export declare function useSiteConfig(): import("@tanstack/react-query").UseQueryResult<SiteConfig, Error>;
@@ -0,0 +1,11 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { api } from '@ploykit/client';
3
+ export const SITE_CONFIG_QUERY_KEY = ['site-config'];
4
+ export function useSiteConfig() {
5
+ return useQuery({
6
+ queryKey: SITE_CONFIG_QUERY_KEY,
7
+ queryFn: () => api.get('/config'),
8
+ staleTime: 5 * 60_000,
9
+ retry: false,
10
+ });
11
+ }
@@ -0,0 +1,36 @@
1
+ import type { components } from '@ploykit/client';
2
+ export type PAT = components['schemas']['PAT'];
3
+ export type CreatePATResult = components['schemas']['CreatePATResponse'];
4
+ export declare function useTokens(): {
5
+ tokens: {
6
+ id: string;
7
+ name: string;
8
+ prefix: string;
9
+ expires_at?: string;
10
+ last_used_at?: string;
11
+ created_at: string;
12
+ }[];
13
+ loading: boolean;
14
+ error: Error | null;
15
+ refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<{
16
+ id: string;
17
+ name: string;
18
+ prefix: string;
19
+ expires_at?: string;
20
+ last_used_at?: string;
21
+ created_at: string;
22
+ }[], Error>>;
23
+ };
24
+ export declare function useCreateToken(): {
25
+ create: (name: string, ttlHours?: number) => Promise<{
26
+ pat: components["schemas"]["PAT"];
27
+ token: string;
28
+ }>;
29
+ loading: boolean;
30
+ error: Error | null;
31
+ };
32
+ export declare function useRevokeToken(): {
33
+ revoke: (id: string) => Promise<void>;
34
+ loading: boolean;
35
+ error: Error | null;
36
+ };
@@ -0,0 +1,35 @@
1
+ import { useCallback } from 'react';
2
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
3
+ import { api } from '@ploykit/client';
4
+ import { queryKeys } from '../provider/PloykitProvider';
5
+ export function useTokens() {
6
+ const q = useQuery({
7
+ queryKey: queryKeys.tokens,
8
+ queryFn: async () => {
9
+ const data = await api.get('/api/tokens');
10
+ return Array.isArray(data) ? data : [];
11
+ },
12
+ });
13
+ return { tokens: q.data ?? [], loading: q.isPending, error: q.error, refetch: q.refetch };
14
+ }
15
+ export function useCreateToken() {
16
+ const qc = useQueryClient();
17
+ const m = useMutation({
18
+ mutationFn: (v) => api.post('/api/tokens', v),
19
+ onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.tokens }),
20
+ });
21
+ const create = useCallback((name, ttlHours) => m.mutateAsync({ name, ttl_hours: ttlHours }), [m]);
22
+ return { create, loading: m.isPending, error: m.error };
23
+ }
24
+ export function useRevokeToken() {
25
+ const qc = useQueryClient();
26
+ const m = useMutation({
27
+ mutationFn: (id) => api.delete(`/api/tokens/${id}`),
28
+ onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.tokens }),
29
+ });
30
+ return {
31
+ revoke: (id) => m.mutateAsync(id),
32
+ loading: m.isPending,
33
+ error: m.error,
34
+ };
35
+ }
@@ -0,0 +1,20 @@
1
+ import type { components } from '@ploykit/client';
2
+ export type PKUsageResp = components['schemas']['UsageResp'];
3
+ export type PKUsageItem = components['schemas']['UsageItem'];
4
+ export declare function useUsage(): {
5
+ wsId: string;
6
+ usage: {
7
+ workspace_id: string;
8
+ plan_code: string;
9
+ period: string;
10
+ items: components["schemas"]["UsageItem"][];
11
+ } | null;
12
+ loading: boolean;
13
+ error: Error | null;
14
+ refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<{
15
+ workspace_id: string;
16
+ plan_code: string;
17
+ period: string;
18
+ items: components["schemas"]["UsageItem"][];
19
+ }, Error>>;
20
+ };
@@ -0,0 +1,21 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { useApi } from './useApi';
3
+ import { useWorkspace } from './useWorkspace';
4
+ import { queryKeys } from '../provider/PloykitProvider';
5
+ export function useUsage() {
6
+ const { current } = useWorkspace();
7
+ const api = useApi();
8
+ const wsId = current?.id ?? '';
9
+ const q = useQuery({
10
+ queryKey: queryKeys.usage(wsId),
11
+ queryFn: () => api.get('/api/usage'),
12
+ enabled: !!wsId,
13
+ });
14
+ return {
15
+ wsId,
16
+ usage: wsId ? (q.data ?? null) : null,
17
+ loading: !!wsId && q.isPending,
18
+ error: q.error,
19
+ refetch: q.refetch,
20
+ };
21
+ }
@@ -0,0 +1,26 @@
1
+ import { type PKWorkspace } from '../provider/PloykitProvider';
2
+ export declare function useWorkspace(): import("../provider/PloykitProvider").WorkspaceContextValue;
3
+ export declare function useWorkspaceSwitch(): {
4
+ current: ({
5
+ id: string;
6
+ slug: string;
7
+ name: string;
8
+ plan_code: string;
9
+ created_at: string;
10
+ } & {
11
+ role: "owner" | "admin" | "member";
12
+ }) | null;
13
+ list: ({
14
+ id: string;
15
+ slug: string;
16
+ name: string;
17
+ plan_code: string;
18
+ created_at: string;
19
+ } & {
20
+ role: "owner" | "admin" | "member";
21
+ })[];
22
+ switchTo: (id: string) => void;
23
+ create: (name: string, slug: string) => Promise<PKWorkspace | null>;
24
+ creating: boolean;
25
+ error: unknown;
26
+ };
@@ -0,0 +1,32 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { useQueryClient } from '@tanstack/react-query';
3
+ import { api } from '@ploykit/client';
4
+ import { useAuthCtx, useWorkspaceCtx, queryKeys, } from '../provider/PloykitProvider';
5
+ export function useWorkspace() {
6
+ return useWorkspaceCtx();
7
+ }
8
+ export function useWorkspaceSwitch() {
9
+ const { workspaces } = useAuthCtx();
10
+ const { current, switchTo } = useWorkspaceCtx();
11
+ const qc = useQueryClient();
12
+ const [creating, setCreating] = useState(false);
13
+ const [error, setError] = useState(null);
14
+ const create = useCallback(async (name, slug) => {
15
+ setCreating(true);
16
+ setError(null);
17
+ try {
18
+ const ws = await api.post('/api/workspaces', { name, slug });
19
+ await qc.invalidateQueries({ queryKey: queryKeys.workspace });
20
+ switchTo(ws.id);
21
+ return ws;
22
+ }
23
+ catch (e) {
24
+ setError(e);
25
+ return null;
26
+ }
27
+ finally {
28
+ setCreating(false);
29
+ }
30
+ }, [qc, switchTo]);
31
+ return { current, list: workspaces, switchTo, create, creating, error };
32
+ }