@rebasepro/auth 0.0.1-canary.2 → 0.0.1-canary.4d4fb3e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js CHANGED
@@ -1,7 +1,7 @@
1
- import { useState, useRef, useEffect, useCallback } from "react";
1
+ import { useState, useRef, useEffect, useCallback, useLayoutEffect } from "react";
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
- import { MailIcon, IconButton, ArrowBackIcon, Typography, TextField, CircularProgress, Button, CenteredView, Container, AddIcon, Table, TableHeader, TableCell, TableBody, TableRow, Tooltip, DeleteIcon, Checkbox, getColorSchemeForSeed, Chip, Dialog, DialogTitle, DialogContent, DialogActions, LoadingButton, MultiSelect, MultiSelectItem } from "@rebasepro/ui";
4
- import { useModeController, ErrorView, RebaseLogo, useSnackbarController, ConfirmationDialog, useAuthController, FieldCaption, useCollectionRegistryController } from "@rebasepro/core";
3
+ import { Paper, MailIcon, Typography, cls, IconButton, ArrowBackIcon, TextField, LoadingButton, Button, CenteredView, CircularProgress, Container, AddIcon, Table, TableHeader, TableCell, TableBody, TableRow, Tooltip, DeleteIcon, Checkbox, getColorSchemeForSeed, Chip, Dialog, DialogTitle, DialogContent, DialogActions, MultiSelect, MultiSelectItem } from "@rebasepro/ui";
4
+ import { useModeController, ErrorView, RebaseLogo, useRebaseRegistryDispatch, useSnackbarController, ConfirmationDialog, useAuthController } from "@rebasepro/core";
5
5
  let baseApiUrl = "";
6
6
  function setApiUrl(url) {
7
7
  baseApiUrl = url;
@@ -223,7 +223,7 @@ function isTokenExpiredOrNearExpiry(expiresAt, bufferMs = TOKEN_REFRESH_BUFFER_M
223
223
  return Date.now() + bufferMs >= expiresAt;
224
224
  }
225
225
  function useRebaseAuthController(props = {}) {
226
- const { apiUrl, onSignOut, defineRolesFor } = props;
226
+ const { client, apiUrl, onSignOut, defineRolesFor } = props;
227
227
  const [user, setUser] = useState(null);
228
228
  const [authLoading, setAuthLoading] = useState(false);
229
229
  const [initialLoading, setInitialLoading] = useState(true);
@@ -237,10 +237,12 @@ function useRebaseAuthController(props = {}) {
237
237
  const refreshPromiseRef = useRef(null);
238
238
  const isMountedRef = useRef(true);
239
239
  useEffect(() => {
240
- if (apiUrl) {
240
+ if (client) {
241
+ setApiUrl(client.baseUrl);
242
+ } else if (apiUrl) {
241
243
  setApiUrl(apiUrl);
242
244
  }
243
- }, [apiUrl]);
245
+ }, [client, apiUrl]);
244
246
  const clearSessionAndSignOut = useCallback(() => {
245
247
  tokensRef.current = null;
246
248
  clearAuthFromStorage();
@@ -352,6 +354,35 @@ function useRebaseAuthController(props = {}) {
352
354
  }
353
355
  return currentTokens.accessToken;
354
356
  }, [initialLoading, refreshAccessToken$1, clearSessionAndSignOut]);
357
+ useEffect(() => {
358
+ if (client) {
359
+ client.setAuthTokenGetter(async () => {
360
+ try {
361
+ return await getAuthToken();
362
+ } catch {
363
+ return null;
364
+ }
365
+ });
366
+ if (client.setOnUnauthorized) {
367
+ client.setOnUnauthorized(async () => {
368
+ try {
369
+ const newTokens = await refreshAccessToken$1();
370
+ if (newTokens) return true;
371
+ clearSessionAndSignOut();
372
+ return false;
373
+ } catch (e) {
374
+ clearSessionAndSignOut();
375
+ return false;
376
+ }
377
+ });
378
+ }
379
+ if (client.ws) {
380
+ client.ws.setAuthTokenGetter(async () => {
381
+ return await getAuthToken();
382
+ });
383
+ }
384
+ }
385
+ }, [client, getAuthToken, refreshAccessToken$1, clearSessionAndSignOut]);
355
386
  const handleAuthSuccess = useCallback(async (userInfo, tokens) => {
356
387
  tokensRef.current = tokens;
357
388
  let convertedUser = convertToUser(userInfo);
@@ -670,7 +701,16 @@ function useRebaseAuthController(props = {}) {
670
701
  revokeSession: revokeSession$1,
671
702
  revokeAllSessions: revokeAllSessions$1,
672
703
  extra,
673
- setExtra
704
+ setExtra,
705
+ capabilities: {
706
+ emailPasswordLogin: true,
707
+ googleLogin: !!props.googleClientId,
708
+ registration: authConfig?.registrationEnabled ?? false,
709
+ passwordReset: true,
710
+ sessionManagement: true,
711
+ profileUpdate: true,
712
+ emailVerification: false
713
+ }
674
714
  };
675
715
  }
676
716
  function convertUser(apiUser) {
@@ -681,7 +721,8 @@ function convertUser(apiUser) {
681
721
  photoURL: apiUser.photoURL || null,
682
722
  providerId: "custom",
683
723
  isAnonymous: false,
684
- roles: apiUser.roles
724
+ roles: apiUser.roles,
725
+ createdAt: apiUser.createdAt ? new Date(apiUser.createdAt) : null
685
726
  };
686
727
  }
687
728
  function convertRole(apiRole) {
@@ -693,7 +734,7 @@ function convertRole(apiRole) {
693
734
  };
694
735
  }
695
736
  function useBackendUserManagement(config) {
696
- const { apiUrl, getAuthToken, currentUser } = config;
737
+ const { client, apiUrl, getAuthToken, currentUser } = config;
697
738
  const [users, setUsers] = useState([]);
698
739
  const [roles, setRoles] = useState([]);
699
740
  const [loading, setLoading] = useState(true);
@@ -708,12 +749,13 @@ function useBackendUserManagement(config) {
708
749
  throw error;
709
750
  }
710
751
  try {
711
- const token = await getAuthToken();
712
- const response = await fetch(`${apiUrl}/api/admin${endpoint}`, {
752
+ const token = getAuthToken ? await getAuthToken() : client ? await client.resolveToken() : null;
753
+ const baseUrl = apiUrl || (client?.baseUrl ? client.baseUrl : "");
754
+ const response = await fetch(`${baseUrl}/api/admin${endpoint}`, {
713
755
  method,
714
756
  headers: {
715
757
  "Content-Type": "application/json",
716
- "Authorization": `Bearer ${token}`
758
+ ...token ? { "Authorization": `Bearer ${token}` } : {}
717
759
  },
718
760
  body: body ? JSON.stringify(body) : void 0,
719
761
  signal
@@ -765,17 +807,6 @@ function useBackendUserManagement(config) {
765
807
  }
766
808
  throw lastError;
767
809
  }, [apiUrl, getAuthToken]);
768
- const loadUsers = useCallback(async (signal) => {
769
- try {
770
- const data = await apiRequest("/users", "GET", void 0, 6, signal);
771
- setUsers(data.users.map((u) => convertUser(u)));
772
- setUsersError(void 0);
773
- } catch (error) {
774
- if (error instanceof Error && error.name === "AbortError") return;
775
- console.error("Failed to load users:", error);
776
- setUsersError(error instanceof Error ? error : new Error(String(error)));
777
- }
778
- }, [apiRequest]);
779
810
  useCallback(async (signal) => {
780
811
  try {
781
812
  const data = await apiRequest("/roles", "GET", void 0, 6, signal);
@@ -787,6 +818,19 @@ function useBackendUserManagement(config) {
787
818
  setRolesError(error instanceof Error ? error : new Error(String(error)));
788
819
  }
789
820
  }, [apiRequest]);
821
+ const loadAdminUsers = useCallback(async (signal) => {
822
+ try {
823
+ const data = await apiRequest("/users", "GET", void 0, 6, signal);
824
+ const allUsers = data.users.map((u) => convertUser(u));
825
+ const adminUsers = allUsers.filter((u) => u.roles && u.roles.length > 0);
826
+ setUsers(adminUsers);
827
+ setUsersError(void 0);
828
+ } catch (error) {
829
+ if (error instanceof Error && error.name === "AbortError") return;
830
+ console.error("Failed to load admin users:", error);
831
+ setUsersError(error instanceof Error ? error : new Error(String(error)));
832
+ }
833
+ }, [apiRequest]);
790
834
  useEffect(() => {
791
835
  if (!currentUser) {
792
836
  setLoading(false);
@@ -795,11 +839,9 @@ function useBackendUserManagement(config) {
795
839
  const abortController = new AbortController();
796
840
  const load = async () => {
797
841
  setLoading(true);
798
- let loadedRoles = [];
799
842
  try {
800
843
  const data = await apiRequest("/roles", "GET", void 0, 6, abortController.signal);
801
- loadedRoles = data.roles.map(convertRole);
802
- setRoles(loadedRoles);
844
+ setRoles(data.roles.map(convertRole));
803
845
  setRolesError(void 0);
804
846
  } catch (error) {
805
847
  if (error instanceof Error && error.name !== "AbortError") {
@@ -808,7 +850,7 @@ function useBackendUserManagement(config) {
808
850
  }
809
851
  }
810
852
  if (!abortController.signal.aborted) {
811
- await loadUsers(abortController.signal);
853
+ await loadAdminUsers(abortController.signal);
812
854
  }
813
855
  if (!abortController.signal.aborted) {
814
856
  setLoading(false);
@@ -818,7 +860,21 @@ function useBackendUserManagement(config) {
818
860
  return () => {
819
861
  abortController.abort();
820
862
  };
821
- }, [currentUser, apiRequest, loadUsers]);
863
+ }, [currentUser, apiRequest, loadAdminUsers]);
864
+ const searchUsers = useCallback(async (options) => {
865
+ const params = new URLSearchParams();
866
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
867
+ if (options.offset !== void 0) params.set("offset", String(options.offset));
868
+ if (options.search) params.set("search", options.search);
869
+ if (options.orderBy) params.set("orderBy", options.orderBy);
870
+ if (options.orderDir) params.set("orderDir", options.orderDir);
871
+ const qs = params.toString();
872
+ const data = await apiRequest("/users" + (qs ? "?" + qs : ""), "GET");
873
+ return {
874
+ users: data.users.map((u) => convertUser(u)),
875
+ total: data.total
876
+ };
877
+ }, [apiRequest]);
822
878
  const saveUser = useCallback(async (user) => {
823
879
  const roleIds = user.roles ?? [];
824
880
  const existingUser = users.find((u) => u.uid === user.uid);
@@ -842,6 +898,33 @@ function useBackendUserManagement(config) {
842
898
  return created;
843
899
  }
844
900
  }, [apiRequest, users, roles]);
901
+ const createUser = useCallback(async (user) => {
902
+ const roleIds = user.roles ?? [];
903
+ const data = await apiRequest("/users", "POST", {
904
+ email: user.email,
905
+ displayName: user.displayName,
906
+ roles: roleIds
907
+ });
908
+ const created = convertUser(data.user);
909
+ if (created.roles && created.roles.length > 0) {
910
+ setUsers((prev) => [...prev, created]);
911
+ }
912
+ return {
913
+ user: created,
914
+ invitationSent: data.invitationSent ?? false,
915
+ temporaryPassword: data.temporaryPassword
916
+ };
917
+ }, [apiRequest, roles]);
918
+ const resetPassword2 = useCallback(async (user) => {
919
+ const data = await apiRequest(`/users/${user.uid}/reset-password`, "POST");
920
+ const updatedUser = convertUser(data.user);
921
+ setUsers((prev) => prev.map((u) => u.uid === updatedUser.uid ? updatedUser : u));
922
+ return {
923
+ user: updatedUser,
924
+ invitationSent: data.invitationSent ?? false,
925
+ temporaryPassword: data.temporaryPassword
926
+ };
927
+ }, [apiRequest]);
845
928
  const deleteUser = useCallback(async (user) => {
846
929
  await apiRequest(`/users/${user.uid}`, "DELETE");
847
930
  setUsers((prev) => prev.filter((u) => u.uid !== user.uid));
@@ -887,16 +970,18 @@ function useBackendUserManagement(config) {
887
970
  const data = await apiRequest("/roles");
888
971
  const loadedRoles = data.roles.map(convertRole);
889
972
  setRoles(loadedRoles);
890
- await loadUsers();
973
+ await loadAdminUsers();
891
974
  } catch (error) {
892
975
  console.error("Failed to bootstrap admin:", error);
893
976
  throw error;
894
977
  }
895
- }, [apiRequest, loadUsers]);
978
+ }, [apiRequest, loadAdminUsers]);
896
979
  return {
897
980
  loading,
898
981
  users,
899
982
  saveUser,
983
+ createUser,
984
+ resetPassword: resetPassword2,
900
985
  deleteUser,
901
986
  roles,
902
987
  saveRole,
@@ -906,6 +991,7 @@ function useBackendUserManagement(config) {
906
991
  includeCollectionConfigPermissions: true,
907
992
  defineRolesFor,
908
993
  getUser,
994
+ searchUsers,
909
995
  usersError,
910
996
  rolesError,
911
997
  bootstrapAdmin
@@ -922,14 +1008,26 @@ function RebaseLoginView({
922
1008
  googleClientId
923
1009
  }) {
924
1010
  const modeState = useModeController();
925
- const [registrationSelected, setRegistrationSelected] = useState(false);
926
- const [passwordLoginSelected, setPasswordLoginSelected] = useState(false);
927
- const [forgotPasswordSelected, setForgotPasswordSelected] = useState(false);
1011
+ const isDark = modeState.mode === "dark";
1012
+ const [mode, setMode] = useState("buttons");
1013
+ const [fadeIn, setFadeIn] = useState(false);
1014
+ const [viewVisible, setViewVisible] = useState(true);
1015
+ const switchMode = (newMode) => {
1016
+ setViewVisible(false);
1017
+ setTimeout(() => {
1018
+ setMode(newMode);
1019
+ setViewVisible(true);
1020
+ }, 150);
1021
+ };
928
1022
  const isBootstrapMode = authController.needsSetup;
1023
+ useEffect(() => {
1024
+ const timer = setTimeout(() => setFadeIn(true), 50);
1025
+ return () => clearTimeout(timer);
1026
+ }, []);
929
1027
  function buildErrorView() {
930
1028
  if (!authController.authProviderError) return null;
931
1029
  if (authController.user != null) return null;
932
- return /* @__PURE__ */ jsx(ErrorView, { error: authController.authProviderError.message ?? authController.authProviderError });
1030
+ return /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(ErrorView, { error: authController.authProviderError.message ?? authController.authProviderError }) });
933
1031
  }
934
1032
  let logoComponent;
935
1033
  if (logo) {
@@ -958,90 +1056,111 @@ function RebaseLoginView({
958
1056
  notAllowedMessage = "It looks like you don't have access to the CMS, based on the specified Authenticator configuration";
959
1057
  }
960
1058
  }
961
- return /* @__PURE__ */ jsx("div", { className: "flex flex-col justify-center items-center min-h-screen min-w-full p-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center w-full max-w-md", children: [
962
- /* @__PURE__ */ jsx("div", { className: `m-4 p-4 w-64 h-64`, children: logoComponent }),
963
- notAllowedMessage && /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsx(ErrorView, { error: notAllowedMessage }) }),
964
- !forgotPasswordSelected && buildErrorView(),
965
- isBootstrapMode && !authController.user && /* @__PURE__ */ jsx(
966
- LoginForm,
967
- {
968
- authController,
969
- registrationMode: true,
970
- onClose: () => {
971
- },
972
- onForgotPassword: () => {
973
- },
974
- mode: modeState.mode,
975
- noUserComponent,
976
- disableSignupScreen: false,
977
- bootstrapMode: true
978
- }
979
- ),
980
- !isBootstrapMode && !passwordLoginSelected && !registrationSelected && !forgotPasswordSelected && /* @__PURE__ */ jsxs(Fragment, { children: [
981
- /* @__PURE__ */ jsx(
982
- LoginButton,
983
- {
984
- disabled,
985
- text: "Email/password",
986
- icon: /* @__PURE__ */ jsx(MailIcon, {}),
987
- onClick: () => {
988
- setRegistrationSelected(false);
989
- setPasswordLoginSelected(true);
990
- setForgotPasswordSelected(false);
991
- }
992
- }
993
- ),
994
- googleEnabled && googleClientId && /* @__PURE__ */ jsx(
995
- GoogleLoginButton,
996
- {
997
- disabled,
998
- googleClientId,
999
- authController
1000
- }
1059
+ const showRegistration = !disableSignupScreen && authController.registrationEnabled;
1060
+ return /* @__PURE__ */ jsx(
1061
+ "div",
1062
+ {
1063
+ className: cls(
1064
+ "flex items-center justify-center min-h-screen w-full p-4 transition-opacity duration-500",
1065
+ fadeIn ? "opacity-100" : "opacity-0"
1001
1066
  ),
1002
- !disableSignupScreen && authController.registrationEnabled && /* @__PURE__ */ jsx(
1003
- LoginButton,
1004
- {
1005
- disabled,
1006
- text: "Create account",
1007
- icon: /* @__PURE__ */ jsx(MailIcon, {}),
1008
- onClick: () => {
1009
- setRegistrationSelected(true);
1010
- setPasswordLoginSelected(false);
1011
- setForgotPasswordSelected(false);
1012
- }
1013
- }
1014
- )
1015
- ] }),
1016
- !isBootstrapMode && (passwordLoginSelected || registrationSelected) && !forgotPasswordSelected && /* @__PURE__ */ jsx(
1017
- LoginForm,
1018
- {
1019
- authController,
1020
- registrationMode: registrationSelected,
1021
- onClose: () => {
1022
- setRegistrationSelected(false);
1023
- setPasswordLoginSelected(false);
1024
- },
1025
- onForgotPassword: () => {
1026
- setForgotPasswordSelected(true);
1027
- setPasswordLoginSelected(false);
1028
- },
1029
- mode: modeState.mode,
1030
- noUserComponent,
1031
- disableSignupScreen
1032
- }
1033
- ),
1034
- forgotPasswordSelected && /* @__PURE__ */ jsx(
1035
- ForgotPasswordForm,
1036
- {
1037
- authController,
1038
- onClose: () => {
1039
- setForgotPasswordSelected(false);
1040
- setPasswordLoginSelected(true);
1041
- }
1042
- }
1043
- )
1044
- ] }) });
1067
+ children: /* @__PURE__ */ jsxs(Paper, { className: "flex flex-col items-center w-[480px] max-w-full p-8 sm:p-10", children: [
1068
+ /* @__PURE__ */ jsx("div", { className: "w-32 h-32 m-2 mb-6", children: logoComponent }),
1069
+ notAllowedMessage && /* @__PURE__ */ jsx("div", { className: "p-4 w-full", children: /* @__PURE__ */ jsx(ErrorView, { error: notAllowedMessage }) }),
1070
+ mode !== "forgot" && buildErrorView(),
1071
+ /* @__PURE__ */ jsxs("div", { className: cls(
1072
+ "w-full transition-opacity duration-150",
1073
+ viewVisible ? "opacity-100" : "opacity-0"
1074
+ ), children: [
1075
+ isBootstrapMode && !authController.user && /* @__PURE__ */ jsx(
1076
+ LoginForm,
1077
+ {
1078
+ authController,
1079
+ registrationMode: true,
1080
+ onClose: () => {
1081
+ },
1082
+ onForgotPassword: () => {
1083
+ },
1084
+ mode: modeState.mode,
1085
+ noUserComponent,
1086
+ disableSignupScreen: false,
1087
+ bootstrapMode: true
1088
+ }
1089
+ ),
1090
+ !isBootstrapMode && /* @__PURE__ */ jsxs(Fragment, { children: [
1091
+ mode === "buttons" && /* @__PURE__ */ jsxs("div", { className: "w-full flex flex-col gap-3 mt-2", children: [
1092
+ /* @__PURE__ */ jsx(
1093
+ LoginButton,
1094
+ {
1095
+ disabled,
1096
+ text: "Sign in with email",
1097
+ icon: /* @__PURE__ */ jsx(MailIcon, {}),
1098
+ onClick: () => switchMode("login")
1099
+ }
1100
+ ),
1101
+ googleEnabled && googleClientId && /* @__PURE__ */ jsx(
1102
+ GoogleLoginButton,
1103
+ {
1104
+ disabled,
1105
+ googleClientId,
1106
+ authController
1107
+ }
1108
+ ),
1109
+ showRegistration && /* @__PURE__ */ jsx("div", { className: "mt-2 text-center", children: /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "secondary", children: [
1110
+ "Don't have an account?",
1111
+ " ",
1112
+ /* @__PURE__ */ jsx(
1113
+ "button",
1114
+ {
1115
+ type: "button",
1116
+ className: cls(
1117
+ "font-semibold hover:underline cursor-pointer",
1118
+ isDark ? "text-primary-400" : "text-primary-600"
1119
+ ),
1120
+ onClick: () => switchMode("register"),
1121
+ children: "Create one"
1122
+ }
1123
+ )
1124
+ ] }) })
1125
+ ] }),
1126
+ mode === "login" && /* @__PURE__ */ jsx(
1127
+ LoginForm,
1128
+ {
1129
+ authController,
1130
+ registrationMode: false,
1131
+ onClose: () => switchMode("buttons"),
1132
+ onForgotPassword: () => switchMode("forgot"),
1133
+ mode: modeState.mode,
1134
+ noUserComponent,
1135
+ disableSignupScreen,
1136
+ switchToRegister: showRegistration ? () => switchMode("register") : void 0
1137
+ }
1138
+ ),
1139
+ mode === "register" && /* @__PURE__ */ jsx(
1140
+ LoginForm,
1141
+ {
1142
+ authController,
1143
+ registrationMode: true,
1144
+ onClose: () => switchMode("buttons"),
1145
+ onForgotPassword: () => switchMode("forgot"),
1146
+ mode: modeState.mode,
1147
+ noUserComponent,
1148
+ disableSignupScreen,
1149
+ switchToLogin: () => switchMode("login")
1150
+ }
1151
+ ),
1152
+ mode === "forgot" && /* @__PURE__ */ jsx(
1153
+ ForgotPasswordForm,
1154
+ {
1155
+ authController,
1156
+ onClose: () => switchMode("login")
1157
+ }
1158
+ )
1159
+ ] })
1160
+ ] })
1161
+ ] })
1162
+ }
1163
+ );
1045
1164
  }
1046
1165
  function LoginButton({
1047
1166
  icon,
@@ -1049,18 +1168,20 @@ function LoginButton({
1049
1168
  text,
1050
1169
  disabled
1051
1170
  }) {
1052
- return /* @__PURE__ */ jsx("div", { className: "m-2 w-full", children: /* @__PURE__ */ jsx(
1171
+ return /* @__PURE__ */ jsx(
1053
1172
  Button,
1054
1173
  {
1055
1174
  disabled,
1056
- className: `w-full`,
1175
+ className: "w-full",
1176
+ variant: "outlined",
1177
+ size: "large",
1057
1178
  onClick,
1058
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center p-2 w-full h-8", children: [
1059
- /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center justify-center w-8", children: icon }),
1060
- /* @__PURE__ */ jsx("div", { className: "grow pl-2 text-center", children: text })
1179
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center w-full gap-3 py-1", children: [
1180
+ /* @__PURE__ */ jsx("span", { className: "flex items-center justify-center w-5 h-5", children: icon }),
1181
+ /* @__PURE__ */ jsx(Typography, { variant: "button", children: text })
1061
1182
  ] })
1062
1183
  }
1063
- ) });
1184
+ );
1064
1185
  }
1065
1186
  function GoogleLoginButton({
1066
1187
  disabled,
@@ -1089,47 +1210,49 @@ function GoogleLoginButton({
1089
1210
  console.error("Google login error:", err);
1090
1211
  }
1091
1212
  };
1092
- return /* @__PURE__ */ jsx("div", { className: "m-2 w-full", children: /* @__PURE__ */ jsx(
1213
+ return /* @__PURE__ */ jsx(
1093
1214
  Button,
1094
1215
  {
1095
1216
  disabled,
1096
- className: `w-full`,
1217
+ className: "w-full",
1218
+ variant: "outlined",
1219
+ size: "large",
1097
1220
  onClick: handleGoogleLogin,
1098
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center p-2 w-full h-8", children: [
1099
- /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center justify-center w-8", children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "24", height: "24", children: [
1221
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center w-full gap-3 py-1", children: [
1222
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "20", height: "20", children: [
1100
1223
  /* @__PURE__ */ jsx(
1101
1224
  "path",
1102
1225
  {
1103
- fill: "currentColor",
1226
+ fill: "#4285F4",
1104
1227
  d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
1105
1228
  }
1106
1229
  ),
1107
1230
  /* @__PURE__ */ jsx(
1108
1231
  "path",
1109
1232
  {
1110
- fill: "currentColor",
1233
+ fill: "#34A853",
1111
1234
  d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
1112
1235
  }
1113
1236
  ),
1114
1237
  /* @__PURE__ */ jsx(
1115
1238
  "path",
1116
1239
  {
1117
- fill: "currentColor",
1240
+ fill: "#FBBC05",
1118
1241
  d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
1119
1242
  }
1120
1243
  ),
1121
1244
  /* @__PURE__ */ jsx(
1122
1245
  "path",
1123
1246
  {
1124
- fill: "currentColor",
1247
+ fill: "#EA4335",
1125
1248
  d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
1126
1249
  }
1127
1250
  )
1128
- ] }) }),
1129
- /* @__PURE__ */ jsx("div", { className: "grow pl-2 text-center", children: "Continue with Google" })
1251
+ ] }),
1252
+ /* @__PURE__ */ jsx(Typography, { variant: "button", children: "Continue with Google" })
1130
1253
  ] })
1131
1254
  }
1132
- ) });
1255
+ );
1133
1256
  }
1134
1257
  function LoginForm({
1135
1258
  onClose,
@@ -1139,8 +1262,11 @@ function LoginForm({
1139
1262
  registrationMode,
1140
1263
  noUserComponent,
1141
1264
  disableSignupScreen,
1142
- bootstrapMode = false
1265
+ bootstrapMode = false,
1266
+ switchToRegister,
1267
+ switchToLogin
1143
1268
  }) {
1269
+ const isDark = mode === "dark";
1144
1270
  const passwordRef = useRef(null);
1145
1271
  const [email, setEmail] = useState();
1146
1272
  const [password, setPassword] = useState();
@@ -1167,9 +1293,6 @@ function LoginForm({
1167
1293
  authController.register(email, password, displayName);
1168
1294
  }
1169
1295
  }
1170
- const onBackPressed = () => {
1171
- onClose();
1172
- };
1173
1296
  const handleSubmit = (event) => {
1174
1297
  event.preventDefault();
1175
1298
  if (registrationMode)
@@ -1177,68 +1300,126 @@ function LoginForm({
1177
1300
  else
1178
1301
  handleEnterPassword();
1179
1302
  };
1180
- const label = bootstrapMode ? "Welcome! Create your admin account" : registrationMode ? "Create a new account" : "Enter your email and password";
1181
- const button = registrationMode ? "Create account" : "Login";
1182
- return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col items-center w-full max-w-[500px] gap-2", children: [
1183
- !bootstrapMode && /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(IconButton, { onClick: onBackPressed, children: /* @__PURE__ */ jsx(ArrowBackIcon, {}) }) }),
1184
- /* @__PURE__ */ jsx("div", { className: "flex justify-center w-full py-2", children: /* @__PURE__ */ jsx(Typography, { align: "center", variant: bootstrapMode ? "subtitle1" : "subtitle2", children: label }) }),
1185
- bootstrapMode && /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-gray-500 text-center mb-2", children: "No users found. Create the first account to get started. This account will have admin privileges." }),
1186
- registrationMode && /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(
1187
- TextField,
1188
- {
1189
- placeholder: "Display Name (optional)",
1190
- className: "w-full",
1191
- value: displayName ?? "",
1192
- disabled: authController.initialLoading,
1193
- type: "text",
1194
- onChange: (event) => setDisplayName(event.target.value)
1195
- }
1196
- ) }),
1197
- /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(
1198
- TextField,
1199
- {
1200
- placeholder: "Email",
1201
- className: "w-full",
1202
- autoFocus: true,
1203
- value: email ?? "",
1204
- disabled: authController.initialLoading,
1205
- type: "email",
1206
- onChange: (event) => setEmail(event.target.value)
1207
- }
1208
- ) }),
1209
- /* @__PURE__ */ jsx("div", { className: "w-full", children: registrationMode && noUserComponent }),
1210
- /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(
1211
- TextField,
1212
- {
1213
- placeholder: "Password",
1214
- className: "w-full",
1215
- value: password ?? "",
1216
- disabled: authController.initialLoading,
1217
- inputRef: passwordRef,
1218
- type: "password",
1219
- onChange: (event) => setPassword(event.target.value)
1220
- }
1221
- ) }),
1222
- registrationMode && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-gray-500 text-sm", children: "Password: 8+ chars, uppercase, lowercase, number" }),
1223
- !registrationMode && /* @__PURE__ */ jsx("div", { className: "w-full text-right", children: /* @__PURE__ */ jsx(
1303
+ const title = bootstrapMode ? "Welcome!" : registrationMode ? "Create account" : "Sign in";
1304
+ const subtitle = bootstrapMode ? "Create your admin account to get started. This account will have admin privileges." : registrationMode ? "Fill in your details to create a new account" : "Enter your credentials to continue";
1305
+ const buttonLabel = registrationMode ? "Create account" : "Sign in";
1306
+ return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col w-full gap-1 mt-2", children: [
1307
+ !bootstrapMode && /* @__PURE__ */ jsx("div", { className: "w-full mb-2", children: /* @__PURE__ */ jsx(IconButton, { onClick: onClose, children: /* @__PURE__ */ jsx(ArrowBackIcon, {}) }) }),
1308
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", className: "mb-0.5", children: title }),
1309
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "secondary", className: "mb-5", children: subtitle }),
1310
+ registrationMode && noUserComponent && /* @__PURE__ */ jsx("div", { className: "w-full mb-2", children: noUserComponent }),
1311
+ registrationMode && /* @__PURE__ */ jsxs("div", { className: "w-full mb-3", children: [
1312
+ /* @__PURE__ */ jsx(Typography, { variant: "label", color: "secondary", className: "mb-1", children: "Display Name" }),
1313
+ /* @__PURE__ */ jsx(
1314
+ TextField,
1315
+ {
1316
+ placeholder: "Jane Doe (optional)",
1317
+ className: "w-full",
1318
+ value: displayName ?? "",
1319
+ disabled: authController.initialLoading,
1320
+ type: "text",
1321
+ size: "medium",
1322
+ onChange: (event) => setDisplayName(event.target.value)
1323
+ }
1324
+ )
1325
+ ] }),
1326
+ /* @__PURE__ */ jsxs("div", { className: "w-full mb-3", children: [
1327
+ /* @__PURE__ */ jsx(Typography, { variant: "label", color: "secondary", className: "mb-1", children: "Email" }),
1328
+ /* @__PURE__ */ jsx(
1329
+ TextField,
1330
+ {
1331
+ placeholder: "you@example.com",
1332
+ className: "w-full",
1333
+ autoFocus: true,
1334
+ value: email ?? "",
1335
+ disabled: authController.initialLoading,
1336
+ type: "email",
1337
+ size: "medium",
1338
+ onChange: (event) => setEmail(event.target.value)
1339
+ }
1340
+ )
1341
+ ] }),
1342
+ /* @__PURE__ */ jsxs("div", { className: "w-full mb-1", children: [
1343
+ /* @__PURE__ */ jsx(Typography, { variant: "label", color: "secondary", className: "mb-1", children: "Password" }),
1344
+ /* @__PURE__ */ jsx(
1345
+ TextField,
1346
+ {
1347
+ placeholder: "••••••••",
1348
+ className: "w-full",
1349
+ value: password ?? "",
1350
+ disabled: authController.initialLoading,
1351
+ inputRef: passwordRef,
1352
+ type: "password",
1353
+ size: "medium",
1354
+ onChange: (event) => setPassword(event.target.value)
1355
+ }
1356
+ )
1357
+ ] }),
1358
+ registrationMode && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "mb-3", children: "Password must be 8+ characters with uppercase, lowercase, and a number" }),
1359
+ !registrationMode && /* @__PURE__ */ jsx("div", { className: "w-full text-right mb-3", children: /* @__PURE__ */ jsx(
1224
1360
  "button",
1225
1361
  {
1226
1362
  type: "button",
1227
- className: "text-sm text-blue-600 hover:text-blue-800 hover:underline",
1363
+ className: cls(
1364
+ "text-xs font-medium hover:underline cursor-pointer",
1365
+ isDark ? "text-primary-400" : "text-primary-600"
1366
+ ),
1228
1367
  onClick: onForgotPassword,
1229
1368
  children: "Forgot password?"
1230
1369
  }
1231
1370
  ) }),
1232
- /* @__PURE__ */ jsxs("div", { className: "flex justify-end items-center w-full gap-2", children: [
1233
- authController.authLoading && /* @__PURE__ */ jsx(CircularProgress, {}),
1234
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: authController.authLoading, children: button })
1235
- ] })
1371
+ /* @__PURE__ */ jsx(
1372
+ LoadingButton,
1373
+ {
1374
+ type: "submit",
1375
+ variant: "filled",
1376
+ className: "w-full mt-1",
1377
+ size: "large",
1378
+ loading: authController.authLoading,
1379
+ disabled: authController.authLoading || !email || !password,
1380
+ children: buttonLabel
1381
+ }
1382
+ ),
1383
+ switchToRegister && /* @__PURE__ */ jsx("div", { className: "mt-4 text-center", children: /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "secondary", children: [
1384
+ "Don't have an account?",
1385
+ " ",
1386
+ /* @__PURE__ */ jsx(
1387
+ "button",
1388
+ {
1389
+ type: "button",
1390
+ className: cls(
1391
+ "font-semibold hover:underline cursor-pointer",
1392
+ isDark ? "text-primary-400" : "text-primary-600"
1393
+ ),
1394
+ onClick: switchToRegister,
1395
+ children: "Create one"
1396
+ }
1397
+ )
1398
+ ] }) }),
1399
+ switchToLogin && /* @__PURE__ */ jsx("div", { className: "mt-4 text-center", children: /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "secondary", children: [
1400
+ "Already have an account?",
1401
+ " ",
1402
+ /* @__PURE__ */ jsx(
1403
+ "button",
1404
+ {
1405
+ type: "button",
1406
+ className: cls(
1407
+ "font-semibold hover:underline cursor-pointer",
1408
+ isDark ? "text-primary-400" : "text-primary-600"
1409
+ ),
1410
+ onClick: switchToLogin,
1411
+ children: "Sign in"
1412
+ }
1413
+ )
1414
+ ] }) })
1236
1415
  ] });
1237
1416
  }
1238
1417
  function ForgotPasswordForm({
1239
1418
  onClose,
1240
1419
  authController
1241
1420
  }) {
1421
+ const modeState = useModeController();
1422
+ const isDark = modeState.mode === "dark";
1242
1423
  const [email, setEmail] = useState("");
1243
1424
  const [submitted, setSubmitted] = useState(false);
1244
1425
  const [error, setError] = useState(null);
@@ -1273,42 +1454,71 @@ function ForgotPasswordForm({
1273
1454
  }
1274
1455
  };
1275
1456
  if (submitted) {
1276
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center w-full max-w-[500px] gap-4 p-4", children: [
1457
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col w-full gap-4 mt-2", children: [
1277
1458
  /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(IconButton, { onClick: onClose, children: /* @__PURE__ */ jsx(ArrowBackIcon, {}) }) }),
1278
- /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1279
- /* @__PURE__ */ jsx(Typography, { variant: "subtitle1", className: "mb-4", children: "Check your email" }),
1280
- /* @__PURE__ */ jsxs(Typography, { variant: "body2", className: "text-gray-600", children: [
1459
+ /* @__PURE__ */ jsxs("div", { className: cls(
1460
+ "text-center rounded-xl p-6",
1461
+ isDark ? "bg-surface-800" : "bg-surface-50"
1462
+ ), children: [
1463
+ /* @__PURE__ */ jsx("div", { className: "text-3xl mb-3", children: "📧" }),
1464
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle1", className: "mb-2", children: "Check your email" }),
1465
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "secondary", children: [
1281
1466
  "If an account exists for ",
1282
1467
  /* @__PURE__ */ jsx("strong", { children: email }),
1283
1468
  ", you'll receive a password reset link shortly."
1284
1469
  ] })
1285
1470
  ] }),
1286
- /* @__PURE__ */ jsx(Button, { onClick: onClose, className: "mt-4", children: "Back to login" })
1471
+ /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "text", className: "mt-2", children: "Back to sign in" })
1287
1472
  ] });
1288
1473
  }
1289
- return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col items-center w-full max-w-[500px] gap-2", children: [
1290
- /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(IconButton, { onClick: onClose, children: /* @__PURE__ */ jsx(ArrowBackIcon, {}) }) }),
1291
- /* @__PURE__ */ jsx("div", { className: "flex justify-center w-full py-2", children: /* @__PURE__ */ jsx(Typography, { align: "center", variant: "subtitle2", children: "Reset your password" }) }),
1292
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-gray-600 text-center mb-2", children: "Enter your email address and we'll send you a link to reset your password." }),
1293
- error && /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(ErrorView, { error }) }),
1294
- /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(
1295
- TextField,
1474
+ return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col w-full gap-1 mt-2", children: [
1475
+ /* @__PURE__ */ jsx("div", { className: "w-full mb-2", children: /* @__PURE__ */ jsx(IconButton, { onClick: onClose, children: /* @__PURE__ */ jsx(ArrowBackIcon, {}) }) }),
1476
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", className: "mb-0.5", children: "Reset password" }),
1477
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "secondary", className: "mb-5", children: "Enter your email and we'll send you a reset link." }),
1478
+ error && /* @__PURE__ */ jsx("div", { className: "w-full mb-3", children: /* @__PURE__ */ jsx(ErrorView, { error }) }),
1479
+ /* @__PURE__ */ jsxs("div", { className: "w-full mb-3", children: [
1480
+ /* @__PURE__ */ jsx(Typography, { variant: "label", color: "secondary", className: "mb-1", children: "Email" }),
1481
+ /* @__PURE__ */ jsx(
1482
+ TextField,
1483
+ {
1484
+ placeholder: "you@example.com",
1485
+ className: "w-full",
1486
+ autoFocus: true,
1487
+ value: email,
1488
+ type: "email",
1489
+ size: "medium",
1490
+ onChange: (event) => setEmail(event.target.value)
1491
+ }
1492
+ )
1493
+ ] }),
1494
+ /* @__PURE__ */ jsx(
1495
+ LoadingButton,
1296
1496
  {
1297
- placeholder: "Email",
1497
+ type: "submit",
1498
+ variant: "filled",
1298
1499
  className: "w-full",
1299
- autoFocus: true,
1300
- value: email,
1301
- type: "email",
1302
- onChange: (event) => setEmail(event.target.value)
1500
+ size: "large",
1501
+ loading: authController.authLoading,
1502
+ disabled: authController.authLoading || !email,
1503
+ children: "Send reset link"
1303
1504
  }
1304
- ) }),
1305
- /* @__PURE__ */ jsxs("div", { className: "flex justify-end items-center w-full gap-2 mt-2", children: [
1306
- authController.authLoading && /* @__PURE__ */ jsx(CircularProgress, {}),
1307
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: authController.authLoading || !email, children: "Send reset link" })
1308
- ] })
1505
+ )
1309
1506
  ] });
1310
1507
  }
1311
- function createUserManagementAdminViews({ userManagement, apiUrl, getAuthToken }) {
1508
+ function RebaseAuth({ loginView }) {
1509
+ const dispatch = useRebaseRegistryDispatch();
1510
+ const registeredRef = useRef(false);
1511
+ useLayoutEffect(() => {
1512
+ dispatch.registerAuth({ loginView });
1513
+ registeredRef.current = true;
1514
+ return () => {
1515
+ registeredRef.current = false;
1516
+ dispatch.unregisterAuth();
1517
+ };
1518
+ }, [dispatch, loginView]);
1519
+ return null;
1520
+ }
1521
+ function createUserManagementAdminViews({ userManagement, apiUrl, getAuthToken, collections = [] }) {
1312
1522
  return [
1313
1523
  {
1314
1524
  slug: "dev/users",
@@ -1322,7 +1532,7 @@ function createUserManagementAdminViews({ userManagement, apiUrl, getAuthToken }
1322
1532
  name: "Roles",
1323
1533
  group: "Admin",
1324
1534
  icon: "gpp_good",
1325
- view: /* @__PURE__ */ jsx(RolesView, { userManagement })
1535
+ view: /* @__PURE__ */ jsx(RolesView, { userManagement, collections })
1326
1536
  }
1327
1537
  ];
1328
1538
  }
@@ -1595,7 +1805,7 @@ function UserDetailsForm({
1595
1805
  }
1596
1806
  ) });
1597
1807
  }
1598
- function RolesView({ userManagement }) {
1808
+ function RolesView({ userManagement, collections = [] }) {
1599
1809
  const { roles, saveRole, deleteRole, loading, allowDefaultRolesCreation } = userManagement;
1600
1810
  const snackbarController = useSnackbarController();
1601
1811
  const [dialogOpen, setDialogOpen] = useState(false);
@@ -1682,7 +1892,8 @@ function RolesView({ userManagement }) {
1682
1892
  open: dialogOpen,
1683
1893
  role: selectedRole,
1684
1894
  saveRole,
1685
- handleClose
1895
+ handleClose,
1896
+ collections
1686
1897
  },
1687
1898
  selectedRole?.id ?? "new"
1688
1899
  ),
@@ -1706,7 +1917,8 @@ function RoleDetailsForm({
1706
1917
  open,
1707
1918
  role: roleProp,
1708
1919
  saveRole,
1709
- handleClose
1920
+ handleClose,
1921
+ collections = []
1710
1922
  }) {
1711
1923
  const snackbarController = useSnackbarController();
1712
1924
  const isNewRole = !roleProp;
@@ -1790,7 +2002,7 @@ function RoleDetailsForm({
1790
2002
  ),
1791
2003
  /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Is Admin" })
1792
2004
  ] }) }),
1793
- /* @__PURE__ */ jsx("div", { className: "col-span-12", children: /* @__PURE__ */ jsx(CollectionPermissionsMatrix, { roleId, isAdmin }) })
2005
+ /* @__PURE__ */ jsx("div", { className: "col-span-12", children: /* @__PURE__ */ jsx(CollectionPermissionsMatrix, { roleId, isAdmin, collections }) })
1794
2006
  ] }) }),
1795
2007
  /* @__PURE__ */ jsxs(DialogActions, { children: [
1796
2008
  /* @__PURE__ */ jsx(Button, { variant: "text", onClick: handleClose, children: "Cancel" }),
@@ -1838,12 +2050,11 @@ function PermCell({ granted }) {
1838
2050
  }
1839
2051
  );
1840
2052
  }
1841
- function CollectionPermissionsMatrix({ roleId, isAdmin }) {
1842
- const { collections } = useCollectionRegistryController();
2053
+ function CollectionPermissionsMatrix({ roleId, isAdmin, collections }) {
1843
2054
  if (!collections || collections.length === 0) {
1844
2055
  return /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(Typography, { variant: "label", className: "text-surface-400", children: "No collections configured" }) });
1845
2056
  }
1846
- const topLevel = collections.filter((c) => !c.collectionGroup);
2057
+ const topLevel = collections;
1847
2058
  return /* @__PURE__ */ jsxs("div", { className: "mt-6", children: [
1848
2059
  /* @__PURE__ */ jsx(Typography, { variant: "label", className: "mb-2 block text-surface-600 dark:text-surface-400 uppercase tracking-wide text-xs", children: "Collection permissions" }),
1849
2060
  /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-surface-200 dark:border-surface-700 overflow-hidden", children: /* @__PURE__ */ jsxs(Table, { children: [
@@ -1852,7 +2063,8 @@ function CollectionPermissionsMatrix({ roleId, isAdmin }) {
1852
2063
  CRUD_OPS.map(({ op, label }) => /* @__PURE__ */ jsx(TableCell, { header: true, className: "text-center w-24", children: label }, op))
1853
2064
  ] }) }),
1854
2065
  /* @__PURE__ */ jsx(TableBody, { children: topLevel.map((collection) => {
1855
- const noRules = !collection.securityRules || collection.securityRules.length === 0;
2066
+ const extCol = collection;
2067
+ const noRules = !extCol.securityRules || extCol.securityRules.length === 0;
1856
2068
  return /* @__PURE__ */ jsxs(TableRow, { children: [
1857
2069
  /* @__PURE__ */ jsxs(TableCell, { children: [
1858
2070
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -1861,15 +2073,19 @@ function CollectionPermissionsMatrix({ roleId, isAdmin }) {
1861
2073
  ] }),
1862
2074
  /* @__PURE__ */ jsx("span", { className: "text-xs text-surface-400 font-mono", children: collection.slug })
1863
2075
  ] }),
1864
- CRUD_OPS.map(({ op }) => /* @__PURE__ */ jsx(TableCell, { className: "text-center", children: /* @__PURE__ */ jsx(PermCell, { granted: isAdmin || hasRoleAccess(collection.securityRules, roleId, op) }) }, op))
2076
+ CRUD_OPS.map(({ op }) => /* @__PURE__ */ jsx(TableCell, { className: "text-center", children: /* @__PURE__ */ jsx(PermCell, { granted: isAdmin || hasRoleAccess(extCol.securityRules, roleId, op) }) }, op))
1865
2077
  ] }, collection.slug);
1866
2078
  }) })
1867
2079
  ] }) }),
1868
2080
  !roleId && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "mt-2 text-surface-400 italic", children: "Enter a role ID above to preview permissions" })
1869
2081
  ] });
1870
2082
  }
2083
+ function FieldCaption({ children }) {
2084
+ return /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-surface-500 mt-1.5 ml-1 block", children });
2085
+ }
1871
2086
  export {
1872
2087
  AuthApiError,
2088
+ RebaseAuth,
1873
2089
  RebaseLoginView,
1874
2090
  RolesView,
1875
2091
  UsersView,