@checkstack/auth-frontend 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,75 @@
1
1
  # @checkstack/auth-frontend
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [4eed42d]
8
+ - @checkstack/frontend-api@0.3.0
9
+ - @checkstack/ui@0.2.2
10
+
11
+ ## 0.3.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 7a23261: ## TanStack Query Integration
16
+
17
+ Migrated all frontend components to use `usePluginClient` hook with TanStack Query integration, replacing the legacy `forPlugin()` pattern.
18
+
19
+ ### New Features
20
+
21
+ - **`usePluginClient` hook**: Provides type-safe access to plugin APIs with `.useQuery()` and `.useMutation()` methods
22
+ - **Automatic request deduplication**: Multiple components requesting the same data share a single network request
23
+ - **Built-in caching**: Configurable stale time and cache duration per query
24
+ - **Loading/error states**: TanStack Query provides `isLoading`, `error`, `isRefetching` states automatically
25
+ - **Background refetching**: Stale data is automatically refreshed when components mount
26
+
27
+ ### Contract Changes
28
+
29
+ All RPC contracts now require `operationType: "query"` or `operationType: "mutation"` metadata:
30
+
31
+ ```typescript
32
+ const getItems = proc()
33
+ .meta({ operationType: "query", access: [access.read] })
34
+ .output(z.array(itemSchema))
35
+ .query();
36
+
37
+ const createItem = proc()
38
+ .meta({ operationType: "mutation", access: [access.manage] })
39
+ .input(createItemSchema)
40
+ .output(itemSchema)
41
+ .mutation();
42
+ ```
43
+
44
+ ### Migration
45
+
46
+ ```typescript
47
+ // Before (forPlugin pattern)
48
+ const api = useApi(myPluginApiRef);
49
+ const [items, setItems] = useState<Item[]>([]);
50
+ useEffect(() => {
51
+ api.getItems().then(setItems);
52
+ }, [api]);
53
+
54
+ // After (usePluginClient pattern)
55
+ const client = usePluginClient(MyPluginApi);
56
+ const { data: items, isLoading } = client.getItems.useQuery({});
57
+ ```
58
+
59
+ ### Bug Fixes
60
+
61
+ - Fixed `rpc.test.ts` test setup for middleware type inference
62
+ - Fixed `SearchDialog` to use `setQuery` instead of deprecated `search` method
63
+ - Fixed null→undefined warnings in notification and queue frontends
64
+
65
+ ### Patch Changes
66
+
67
+ - Updated dependencies [7a23261]
68
+ - @checkstack/frontend-api@0.2.0
69
+ - @checkstack/common@0.3.0
70
+ - @checkstack/auth-common@0.3.0
71
+ - @checkstack/ui@0.2.1
72
+
3
73
  ## 0.2.0
4
74
 
5
75
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/auth-frontend",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "exports": {
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from "react";
1
+ import React, { useState } from "react";
2
2
  import {
3
3
  Card,
4
4
  CardHeader,
@@ -25,8 +25,7 @@ import {
25
25
  useToast,
26
26
  } from "@checkstack/ui";
27
27
  import { Plus, Trash2, RotateCcw, Copy } from "lucide-react";
28
- import { useApi } from "@checkstack/frontend-api";
29
- import { rpcApiRef } from "@checkstack/frontend-api";
28
+ import { usePluginClient } from "@checkstack/frontend-api";
30
29
  import { AuthApi } from "@checkstack/auth-common";
31
30
  import type { Role } from "../api";
32
31
 
@@ -49,12 +48,9 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
49
48
  roles,
50
49
  canManageApplications,
51
50
  }) => {
52
- const rpcApi = useApi(rpcApiRef);
53
- const authClient = rpcApi.forPlugin(AuthApi);
51
+ const authClient = usePluginClient(AuthApi);
54
52
  const toast = useToast();
55
53
 
56
- const [applications, setApplications] = useState<Application[]>([]);
57
- const [loading, setLoading] = useState(true);
58
54
  const [applicationToDelete, setApplicationToDelete] = useState<string>();
59
55
  const [applicationToRegenerateSecret, setApplicationToRegenerateSecret] =
60
56
  useState<{ id: string; name: string }>();
@@ -67,36 +63,19 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
67
63
  const [newAppName, setNewAppName] = useState("");
68
64
  const [newAppDescription, setNewAppDescription] = useState("");
69
65
 
70
- const fetchApplications = async () => {
71
- if (!canManageApplications) {
72
- setLoading(false);
73
- return;
74
- }
75
- setLoading(true);
76
- try {
77
- const data = await authClient.getApplications();
78
- setApplications(data as Application[]);
79
- } catch (error) {
80
- console.error("Failed to fetch applications:", error);
81
- } finally {
82
- setLoading(false);
83
- }
84
- };
85
-
86
- useEffect(() => {
87
- fetchApplications();
88
- }, [canManageApplications]);
66
+ // Query: Applications list
67
+ const {
68
+ data: applications = [],
69
+ isLoading: loading,
70
+ refetch: refetchApplications,
71
+ } = authClient.getApplications.useQuery(
72
+ {},
73
+ { enabled: canManageApplications }
74
+ );
89
75
 
90
- const handleCreateApplication = async () => {
91
- if (!newAppName.trim()) {
92
- toast.error("Application name is required");
93
- return;
94
- }
95
- try {
96
- const result = await authClient.createApplication({
97
- name: newAppName.trim(),
98
- description: newAppDescription.trim() || undefined,
99
- });
76
+ // Mutations
77
+ const createApplicationMutation = authClient.createApplication.useMutation({
78
+ onSuccess: (result) => {
100
79
  setCreateAppDialogOpen(false);
101
80
  setNewAppName("");
102
81
  setNewAppDescription("");
@@ -105,15 +84,68 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
105
84
  secret: result.secret,
106
85
  applicationName: result.application.name,
107
86
  });
108
- await fetchApplications();
109
- } catch (error: unknown) {
87
+ void refetchApplications();
88
+ },
89
+ onError: (error) => {
110
90
  toast.error(
111
91
  error instanceof Error ? error.message : "Failed to create application"
112
92
  );
93
+ },
94
+ });
95
+
96
+ const updateApplicationMutation = authClient.updateApplication.useMutation({
97
+ onSuccess: () => {
98
+ void refetchApplications();
99
+ },
100
+ onError: (error) => {
101
+ toast.error(
102
+ error instanceof Error ? error.message : "Failed to update application"
103
+ );
104
+ },
105
+ });
106
+
107
+ const deleteApplicationMutation = authClient.deleteApplication.useMutation({
108
+ onSuccess: () => {
109
+ toast.success("Application deleted successfully");
110
+ setApplicationToDelete(undefined);
111
+ void refetchApplications();
112
+ },
113
+ onError: (error) => {
114
+ toast.error(
115
+ error instanceof Error ? error.message : "Failed to delete application"
116
+ );
117
+ },
118
+ });
119
+
120
+ const regenerateSecretMutation =
121
+ authClient.regenerateApplicationSecret.useMutation({
122
+ onSuccess: (result) => {
123
+ setNewSecretDialog({
124
+ open: true,
125
+ secret: result.secret,
126
+ applicationName: applicationToRegenerateSecret?.name ?? "",
127
+ });
128
+ setApplicationToRegenerateSecret(undefined);
129
+ },
130
+ onError: (error) => {
131
+ toast.error(
132
+ error instanceof Error ? error.message : "Failed to regenerate secret"
133
+ );
134
+ },
135
+ });
136
+
137
+ const handleCreateApplication = () => {
138
+ if (!newAppName.trim()) {
139
+ toast.error("Application name is required");
140
+ return;
113
141
  }
142
+ createApplicationMutation.mutate({
143
+ name: newAppName.trim(),
144
+ description: newAppDescription.trim() || undefined,
145
+ });
114
146
  };
115
147
 
116
- const handleToggleApplicationRole = async (
148
+ const handleToggleApplicationRole = (
117
149
  appId: string,
118
150
  roleId: string,
119
151
  currentRoles: string[]
@@ -122,56 +154,20 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
122
154
  ? currentRoles.filter((r) => r !== roleId)
123
155
  : [...currentRoles, roleId];
124
156
 
125
- try {
126
- await authClient.updateApplication({
127
- id: appId,
128
- roles: newRoles,
129
- });
130
- setApplications(
131
- applications.map((a) =>
132
- a.id === appId ? { ...a, roles: newRoles } : a
133
- )
134
- );
135
- } catch (error: unknown) {
136
- toast.error(
137
- error instanceof Error
138
- ? error.message
139
- : "Failed to update application roles"
140
- );
141
- }
157
+ updateApplicationMutation.mutate({
158
+ id: appId,
159
+ roles: newRoles,
160
+ });
142
161
  };
143
162
 
144
- const handleDeleteApplication = async () => {
163
+ const handleDeleteApplication = () => {
145
164
  if (!applicationToDelete) return;
146
- try {
147
- await authClient.deleteApplication(applicationToDelete);
148
- toast.success("Application deleted successfully");
149
- setApplicationToDelete(undefined);
150
- await fetchApplications();
151
- } catch (error: unknown) {
152
- toast.error(
153
- error instanceof Error ? error.message : "Failed to delete application"
154
- );
155
- }
165
+ deleteApplicationMutation.mutate(applicationToDelete);
156
166
  };
157
167
 
158
- const handleRegenerateSecret = async () => {
168
+ const handleRegenerateSecret = () => {
159
169
  if (!applicationToRegenerateSecret) return;
160
- try {
161
- const result = await authClient.regenerateApplicationSecret(
162
- applicationToRegenerateSecret.id
163
- );
164
- setApplicationToRegenerateSecret(undefined);
165
- setNewSecretDialog({
166
- open: true,
167
- secret: result.secret,
168
- applicationName: applicationToRegenerateSecret.name,
169
- });
170
- } catch (error: unknown) {
171
- toast.error(
172
- error instanceof Error ? error.message : "Failed to regenerate secret"
173
- );
174
- }
170
+ regenerateSecretMutation.mutate(applicationToRegenerateSecret.id);
175
171
  };
176
172
 
177
173
  return (
@@ -199,7 +195,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
199
195
  <div className="flex justify-center py-4">
200
196
  <LoadingSpinner />
201
197
  </div>
202
- ) : applications.length === 0 ? (
198
+ ) : (applications as Application[]).length === 0 ? (
203
199
  <p className="text-muted-foreground">
204
200
  No external applications configured yet.
205
201
  </p>
@@ -214,7 +210,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
214
210
  </TableRow>
215
211
  </TableHeader>
216
212
  <TableBody>
217
- {applications.map((app) => (
213
+ {(applications as Application[]).map((app) => (
218
214
  <TableRow key={app.id}>
219
215
  <TableCell>
220
216
  <div className="flex flex-col">
@@ -322,7 +318,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
322
318
  <ConfirmationModal
323
319
  isOpen={!!applicationToRegenerateSecret}
324
320
  onClose={() => setApplicationToRegenerateSecret(undefined)}
325
- onConfirm={() => void handleRegenerateSecret()}
321
+ onConfirm={handleRegenerateSecret}
326
322
  title="Regenerate Application Secret"
327
323
  message={`Are you sure you want to regenerate the secret for "${
328
324
  applicationToRegenerateSecret?.name ?? ""
@@ -365,7 +361,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
365
361
  variant="outline"
366
362
  size="sm"
367
363
  onClick={() => {
368
- navigator.clipboard.writeText(newSecretDialog.secret);
364
+ void navigator.clipboard.writeText(newSecretDialog.secret);
369
365
  toast.success("Secret copied to clipboard");
370
366
  }}
371
367
  >
@@ -448,8 +444,11 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
448
444
  >
449
445
  Cancel
450
446
  </Button>
451
- <Button onClick={() => void handleCreateApplication()}>
452
- Create
447
+ <Button
448
+ onClick={handleCreateApplication}
449
+ disabled={createApplicationMutation.isPending}
450
+ >
451
+ {createApplicationMutation.isPending ? "Creating..." : "Create"}
453
452
  </Button>
454
453
  </DialogFooter>
455
454
  </DialogContent>
@@ -1,7 +1,11 @@
1
1
  import React, { useEffect, useState, useMemo } from "react";
2
2
  import { useSearchParams } from "react-router-dom";
3
- import { useApi, accessApiRef, rpcApiRef } from "@checkstack/frontend-api";
4
- import { PageLayout, useToast, Tabs, TabPanel } from "@checkstack/ui";
3
+ import {
4
+ useApi,
5
+ accessApiRef,
6
+ usePluginClient,
7
+ } from "@checkstack/frontend-api";
8
+ import { PageLayout, Tabs, TabPanel } from "@checkstack/ui";
5
9
  import {
6
10
  authApiRef,
7
11
  AuthUser,
@@ -19,10 +23,8 @@ import { TeamsTab } from "./TeamsTab";
19
23
 
20
24
  export const AuthSettingsPage: React.FC = () => {
21
25
  const authApi = useApi(authApiRef);
22
- const rpcApi = useApi(rpcApiRef);
23
- const authClient = rpcApi.forPlugin(AuthApi);
26
+ const authClient = usePluginClient(AuthApi);
24
27
  const accessApi = useApi(accessApiRef);
25
- const toast = useToast();
26
28
  const [searchParams, setSearchParams] = useSearchParams();
27
29
 
28
30
  const session = authApi.useSession();
@@ -30,11 +32,6 @@ export const AuthSettingsPage: React.FC = () => {
30
32
  const [activeTab, setActiveTab] = useState<
31
33
  "users" | "roles" | "teams" | "strategies" | "applications"
32
34
  >("users");
33
- const [users, setUsers] = useState<(AuthUser & { roles: string[] })[]>([]);
34
- const [roles, setRoles] = useState<Role[]>([]);
35
- const [accessRuleEntries, setAccessRuleEntries] = useState<AccessRuleEntry[]>([]);
36
- const [strategies, setStrategies] = useState<AuthStrategy[]>([]);
37
- const [loading, setLoading] = useState(true);
38
35
 
39
36
  const canReadUsers = accessApi.useAccess(authAccess.users.read);
40
37
 
@@ -72,8 +69,39 @@ export const AuthSettingsPage: React.FC = () => {
72
69
  const canReadTeams = accessApi.useAccess(authAccess.teams.read);
73
70
  const canManageTeams = accessApi.useAccess(authAccess.teams.manage);
74
71
 
72
+ // Queries: Fetch data from API
73
+ const {
74
+ data: users = [],
75
+ isLoading: usersLoading,
76
+ refetch: refetchUsers,
77
+ } = authClient.getUsers.useQuery({}, { enabled: canReadUsers.allowed });
78
+
79
+ const {
80
+ data: roles = [],
81
+ isLoading: rolesLoading,
82
+ refetch: refetchRoles,
83
+ } = authClient.getRoles.useQuery({}, { enabled: canReadRoles.allowed });
84
+
85
+ const {
86
+ data: accessRuleEntries = [],
87
+ isLoading: rulesLoading,
88
+ refetch: refetchRules,
89
+ } = authClient.getAccessRules.useQuery({}, { enabled: canReadRoles.allowed });
90
+
91
+ const {
92
+ data: strategies = [],
93
+ isLoading: strategiesLoading,
94
+ refetch: refetchStrategies,
95
+ } = authClient.getStrategies.useQuery(
96
+ {},
97
+ { enabled: canManageStrategies.allowed }
98
+ );
99
+
100
+ const dataLoading =
101
+ usersLoading || rolesLoading || rulesLoading || strategiesLoading;
102
+
75
103
  const accessRulesLoading =
76
- loading ||
104
+ dataLoading ||
77
105
  canReadUsers.loading ||
78
106
  canReadRoles.loading ||
79
107
  canManageStrategies.loading ||
@@ -146,37 +174,19 @@ export const AuthSettingsPage: React.FC = () => {
146
174
  }
147
175
  }, [visibleTabs, activeTab]);
148
176
 
149
- const fetchData = async () => {
150
- setLoading(true);
151
- try {
152
- const usersData = (await authClient.getUsers()) as (AuthUser & {
153
- roles: string[];
154
- })[];
155
- const rolesData = await authClient.getRoles();
156
- const accessRulesData = await authClient.getAccessRules();
157
- const strategiesData = await authClient.getStrategies();
158
- setUsers(usersData);
159
- setRoles(rolesData);
160
- setAccessRuleEntries(accessRulesData);
161
- setStrategies(strategiesData);
162
- } catch (error: unknown) {
163
- const message =
164
- error instanceof Error ? error.message : "Failed to fetch data";
165
- toast.error(message);
166
- console.error(error);
167
- } finally {
168
- setLoading(false);
169
- }
177
+ const handleDataChange = async () => {
178
+ await Promise.all([
179
+ refetchUsers(),
180
+ refetchRoles(),
181
+ refetchRules(),
182
+ refetchStrategies(),
183
+ ]);
170
184
  };
171
185
 
172
- // Initial data fetch
173
- useEffect(() => {
174
- fetchData();
175
- }, []);
176
-
177
186
  // Get current user's role IDs for the RolesTab
187
+ const typedUsers = users as (AuthUser & { roles: string[] })[];
178
188
  const currentUserRoleIds =
179
- users.find((u) => u.id === session.data?.user?.id)?.roles || [];
189
+ typedUsers.find((u) => u.id === session.data?.user?.id)?.roles || [];
180
190
 
181
191
  return (
182
192
  <PageLayout
@@ -197,52 +207,52 @@ export const AuthSettingsPage: React.FC = () => {
197
207
 
198
208
  <TabPanel id="users" activeTab={activeTab}>
199
209
  <UsersTab
200
- users={users}
201
- roles={roles}
202
- strategies={strategies}
210
+ users={typedUsers}
211
+ roles={roles as Role[]}
212
+ strategies={strategies as AuthStrategy[]}
203
213
  currentUserId={session.data?.user?.id}
204
214
  canReadUsers={canReadUsers.allowed}
205
215
  canCreateUsers={canCreateUsers.allowed}
206
216
  canManageUsers={canManageUsers.allowed}
207
217
  canManageRoles={canManageRoles.allowed}
208
- onDataChange={fetchData}
218
+ onDataChange={handleDataChange}
209
219
  />
210
220
  </TabPanel>
211
221
 
212
222
  <TabPanel id="roles" activeTab={activeTab}>
213
223
  <RolesTab
214
- roles={roles}
215
- accessRulesList={accessRuleEntries}
224
+ roles={roles as Role[]}
225
+ accessRulesList={accessRuleEntries as AccessRuleEntry[]}
216
226
  userRoleIds={currentUserRoleIds}
217
227
  canReadRoles={canReadRoles.allowed}
218
228
  canCreateRoles={canCreateRoles.allowed}
219
229
  canUpdateRoles={canUpdateRoles.allowed}
220
230
  canDeleteRoles={canDeleteRoles.allowed}
221
- onDataChange={fetchData}
231
+ onDataChange={handleDataChange}
222
232
  />
223
233
  </TabPanel>
224
234
 
225
235
  <TabPanel id="teams" activeTab={activeTab}>
226
236
  <TeamsTab
227
- users={users}
237
+ users={typedUsers}
228
238
  canReadTeams={canReadTeams.allowed}
229
239
  canManageTeams={canManageTeams.allowed}
230
- onDataChange={fetchData}
240
+ onDataChange={handleDataChange}
231
241
  />
232
242
  </TabPanel>
233
243
 
234
244
  <TabPanel id="strategies" activeTab={activeTab}>
235
245
  <StrategiesTab
236
- strategies={strategies}
246
+ strategies={strategies as AuthStrategy[]}
237
247
  canManageStrategies={canManageStrategies.allowed}
238
248
  canManageRegistration={canManageRegistration.allowed}
239
- onDataChange={fetchData}
249
+ onDataChange={handleDataChange}
240
250
  />
241
251
  </TabPanel>
242
252
 
243
253
  <TabPanel id="applications" activeTab={activeTab}>
244
254
  <ApplicationsTab
245
- roles={roles}
255
+ roles={roles as Role[]}
246
256
  canManageApplications={canManageApplications.allowed}
247
257
  />
248
258
  </TabPanel>
@@ -5,7 +5,7 @@ import {
5
5
  useApi,
6
6
  ExtensionSlot,
7
7
  pluginRegistry,
8
- rpcApiRef,
8
+ usePluginClient,
9
9
  UserMenuItemsSlot,
10
10
  UserMenuItemsBottomSlot,
11
11
  UserMenuItemsContext,
@@ -49,22 +49,14 @@ export const LoginPage = () => {
49
49
  const [loading, setLoading] = useState(false);
50
50
 
51
51
  const authApi = useApi(authApiRef);
52
- const rpcApi = useApi(rpcApiRef);
53
- const authRpcClient = rpcApi.forPlugin(AuthApi);
52
+ const authClient = usePluginClient(AuthApi);
54
53
  const { strategies, loading: strategiesLoading } = useEnabledStrategies();
55
- const [registrationAllowed, setRegistrationAllowed] = useState<boolean>(true);
56
54
 
57
- useEffect(() => {
58
- authRpcClient
59
- .getRegistrationStatus()
60
- .then(({ allowRegistration }) => {
61
- setRegistrationAllowed(allowRegistration);
62
- })
63
- .catch((error: Error) => {
64
- console.error("Failed to check registration status:", error);
65
- setRegistrationAllowed(true);
66
- });
67
- }, [authRpcClient]);
55
+ // Query: Registration status
56
+ const { data: registrationData } = authClient.getRegistrationStatus.useQuery(
57
+ {}
58
+ );
59
+ const registrationAllowed = registrationData?.allowRegistration ?? true;
68
60
 
69
61
  const handleCredentialLogin = async (e: React.FormEvent) => {
70
62
  e.preventDefault();
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useEffect } from "react";
2
2
  import { Link } from "react-router-dom";
3
3
  import { AlertCircle } from "lucide-react";
4
- import { useApi, rpcApiRef } from "@checkstack/frontend-api";
4
+ import { useApi, usePluginClient } from "@checkstack/frontend-api";
5
5
  import { authApiRef } from "../api";
6
6
  import { AuthApi, authRoutes, passwordSchema } from "@checkstack/auth-common";
7
7
  import { resolveRoute } from "@checkstack/common";
@@ -33,12 +33,9 @@ export const RegisterPage = () => {
33
33
  const [validationErrors, setValidationErrors] = useState<string[]>([]);
34
34
 
35
35
  const authApi = useApi(authApiRef);
36
- const rpcApi = useApi(rpcApiRef);
37
- const authRpcClient = rpcApi.forPlugin(AuthApi);
36
+ const authClient = usePluginClient(AuthApi);
38
37
  const { strategies, loading: strategiesLoading } = useEnabledStrategies();
39
- const [registrationAllowed, setRegistrationAllowed] = useState<boolean>(true);
40
- const [checkingRegistration, setCheckingRegistration] = useState(true);
41
- const authClient = useAuthClient();
38
+ const authBetterClient = useAuthClient();
42
39
 
43
40
  // Validate password on change
44
41
  useEffect(() => {
@@ -54,19 +51,10 @@ export const RegisterPage = () => {
54
51
  }
55
52
  }, [password]);
56
53
 
57
- useEffect(() => {
58
- authRpcClient
59
- .getRegistrationStatus()
60
- .then(({ allowRegistration }) => {
61
- setRegistrationAllowed(allowRegistration);
62
- })
63
- .catch((error: Error) => {
64
- console.error("Failed to check registration status:", error);
65
- // Default to allowed on error to avoid blocking
66
- setRegistrationAllowed(true);
67
- })
68
- .finally(() => setCheckingRegistration(false));
69
- }, [authRpcClient]);
54
+ // Query: Registration status
55
+ const { data: registrationData, isLoading: checkingRegistration } =
56
+ authClient.getRegistrationStatus.useQuery({});
57
+ const registrationAllowed = registrationData?.allowRegistration ?? true;
70
58
 
71
59
  const handleCredentialRegister = async (e: React.FormEvent) => {
72
60
  e.preventDefault();
@@ -79,7 +67,11 @@ export const RegisterPage = () => {
79
67
 
80
68
  setLoading(true);
81
69
  try {
82
- const res = await authClient.signUp.email({ name, email, password });
70
+ const res = await authBetterClient.signUp.email({
71
+ name,
72
+ email,
73
+ password,
74
+ });
83
75
  if (res.error) {
84
76
  console.error("Registration failed:", res.error);
85
77
  } else {
@@ -83,21 +83,17 @@ export const RoleDialog: React.FC<RoleDialogProps> = ({
83
83
  setSelectedAccessRules(newSelected);
84
84
  };
85
85
 
86
- const handleSave = async () => {
86
+ const handleSave = () => {
87
87
  setSaving(true);
88
- try {
89
- await onSave({
90
- ...(isEditing && { id: role.id }),
91
- name,
92
- description: description || undefined,
93
- accessRules: [...selectedAccessRules],
94
- });
95
- onOpenChange(false);
96
- } catch (error) {
97
- console.error("Failed to save role:", error);
98
- } finally {
99
- setSaving(false);
100
- }
88
+ onSave({
89
+ ...(isEditing && { id: role.id }),
90
+ name,
91
+ description: description || undefined,
92
+ accessRules: [...selectedAccessRules],
93
+ });
94
+ // Dialog closing and saving state are managed by the parent via onDataChange callback
95
+ onOpenChange(false);
96
+ setSaving(false);
101
97
  };
102
98
 
103
99
  let buttonText = "Create";
@@ -149,8 +145,8 @@ export const RoleDialog: React.FC<RoleDialogProps> = ({
149
145
  {isAdminRole && (
150
146
  <Alert variant="info" className="mb-3">
151
147
  <AlertDescription>
152
- The administrator role has wildcard access to all access rules.
153
- These cannot be modified.
148
+ The administrator role has wildcard access to all access
149
+ rules. These cannot be modified.
154
150
  </AlertDescription>
155
151
  </Alert>
156
152
  )}