@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/components/AdminViews.d.ts +5 -3
- package/dist/components/RebaseAuth.d.ts +6 -0
- package/dist/components/RebaseLoginView.d.ts +0 -1
- package/dist/hooks/useBackendUserManagement.d.ts +34 -5
- package/dist/hooks/useRebaseAuthController.d.ts +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +436 -220
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +437 -221
- package/dist/index.umd.js.map +1 -1
- package/dist/types.d.ts +3 -1
- package/package.json +8 -8
- package/src/components/AdminViews.tsx +31 -12
- package/src/components/RebaseAuth.tsx +23 -0
- package/src/components/RebaseLoginView.tsx +331 -209
- package/src/hooks/useBackendUserManagement.ts +147 -35
- package/src/hooks/useRebaseAuthController.ts +44 -6
- package/src/index.ts +7 -2
- package/src/types.ts +3 -1
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,
|
|
4
|
-
import { useModeController, ErrorView, RebaseLogo, useSnackbarController, ConfirmationDialog, useAuthController
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
973
|
+
await loadAdminUsers();
|
|
891
974
|
} catch (error) {
|
|
892
975
|
console.error("Failed to bootstrap admin:", error);
|
|
893
976
|
throw error;
|
|
894
977
|
}
|
|
895
|
-
}, [apiRequest,
|
|
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
|
|
926
|
-
const [
|
|
927
|
-
const [
|
|
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
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
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
|
-
|
|
1003
|
-
|
|
1004
|
-
{
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
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(
|
|
1171
|
+
return /* @__PURE__ */ jsx(
|
|
1053
1172
|
Button,
|
|
1054
1173
|
{
|
|
1055
1174
|
disabled,
|
|
1056
|
-
className:
|
|
1175
|
+
className: "w-full",
|
|
1176
|
+
variant: "outlined",
|
|
1177
|
+
size: "large",
|
|
1057
1178
|
onClick,
|
|
1058
|
-
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center
|
|
1059
|
-
/* @__PURE__ */ jsx("
|
|
1060
|
-
/* @__PURE__ */ jsx(
|
|
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(
|
|
1213
|
+
return /* @__PURE__ */ jsx(
|
|
1093
1214
|
Button,
|
|
1094
1215
|
{
|
|
1095
1216
|
disabled,
|
|
1096
|
-
className:
|
|
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
|
|
1099
|
-
/* @__PURE__ */
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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(
|
|
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
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
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:
|
|
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__ */
|
|
1233
|
-
|
|
1234
|
-
|
|
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
|
|
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:
|
|
1279
|
-
|
|
1280
|
-
|
|
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-
|
|
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
|
|
1290
|
-
/* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(IconButton, { onClick: onClose, children: /* @__PURE__ */ jsx(ArrowBackIcon, {}) }) }),
|
|
1291
|
-
/* @__PURE__ */ jsx(
|
|
1292
|
-
/* @__PURE__ */ jsx(Typography, { variant: "body2",
|
|
1293
|
-
error && /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(ErrorView, { error }) }),
|
|
1294
|
-
/* @__PURE__ */
|
|
1295
|
-
|
|
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
|
-
|
|
1497
|
+
type: "submit",
|
|
1498
|
+
variant: "filled",
|
|
1298
1499
|
className: "w-full",
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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,
|