@firecms/user_management 3.3.0 → 3.4.0-canary.1faa2a9

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.
@@ -7,4 +7,4 @@ export declare function IntroWidget({ noUsers, noRoles, userManagement }: {
7
7
  noUsers: boolean;
8
8
  noRoles: boolean;
9
9
  userManagement: UserManagement<any>;
10
- }): import("react").JSX.Element;
10
+ }): import("react").JSX.Element | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firecms/user_management",
3
3
  "type": "module",
4
- "version": "3.3.0",
4
+ "version": "3.4.0-canary.1faa2a9",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -29,9 +29,9 @@
29
29
  "types": "./dist/index.d.ts",
30
30
  "source": "src/index.ts",
31
31
  "dependencies": {
32
- "@firecms/core": "^3.3.0",
33
- "@firecms/formex": "^3.3.0",
34
- "@firecms/ui": "^3.3.0",
32
+ "@firecms/core": "3.4.0-canary.1faa2a9",
33
+ "@firecms/formex": "3.4.0-canary.1faa2a9",
34
+ "@firecms/ui": "3.4.0-canary.1faa2a9",
35
35
  "date-fns": "^3.6.0"
36
36
  },
37
37
  "peerDependencies": {
@@ -39,7 +39,7 @@
39
39
  "react-dom": ">=18.3.1 || >=19.0.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@types/node": "^20.19.17",
42
+ "@types/node": "^26.1.1",
43
43
  "@types/react": "^19.2.3",
44
44
  "@types/react-dom": "^19.2.3",
45
45
  "babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417",
@@ -0,0 +1,35 @@
1
+ import React from "react";
2
+ import { getLicenseSubscribeUrl, useLicenseStatus, useTranslation } from "@firecms/core";
3
+ import { Button, Paper, Typography } from "@firecms/ui";
4
+
5
+ /**
6
+ * Shown by the Users and Roles views instead of their editors while FireCMS
7
+ * PRO is paused. The user management plugin itself stays mounted, so sign-in
8
+ * and role checks keep working; only editing users and roles is paused.
9
+ */
10
+ export function UserManagementPausedNotice() {
11
+ const { t } = useTranslation();
12
+ const licenseStatus = useLicenseStatus();
13
+ const linkLabel = licenseStatus?.licenseState === "invalid_project"
14
+ ? t("license_add_project_to_license")
15
+ : t("license_get_license");
16
+
17
+ return (
18
+ <Paper
19
+ className={"flex flex-col items-start px-4 py-6 bg-white dark:bg-surface-accent-800 gap-2"}>
20
+ <Typography variant={"subtitle2"} className={"uppercase"}>
21
+ {t("pro_features_paused")}
22
+ </Typography>
23
+ <Typography>
24
+ {t("user_management_paused_description")}
25
+ </Typography>
26
+ <Button
27
+ component={"a"}
28
+ href={getLicenseSubscribeUrl(licenseStatus)}
29
+ target={"_blank"}
30
+ rel={"noopener noreferrer"}>
31
+ {linkLabel}
32
+ </Button>
33
+ </Paper>
34
+ );
35
+ }
@@ -1,14 +1,16 @@
1
1
  import React, { useCallback, useState } from "react";
2
2
 
3
- import { Role, useNavigationController, useTranslation
3
+ import { isProPaused, Role, useLicenseStatus, useNavigationController, useTranslation
4
4
  } from "@firecms/core";
5
5
  import { AddIcon, Button, Container, Typography } from "@firecms/ui";
6
6
  import { RolesTable } from "./RolesTable";
7
7
  import { RolesDetailsForm } from "./RolesDetailsForm";
8
+ import { UserManagementPausedNotice } from "../UserManagementPausedNotice";
8
9
 
9
10
  export const RolesView = React.memo(
10
11
  function RolesView({ children }: { children?: React.ReactNode }) {
11
12
  const { t } = useTranslation();
13
+ const paused = isProPaused(useLicenseStatus());
12
14
 
13
15
 
14
16
  const { collections } = useNavigationController();
@@ -25,6 +27,18 @@ export const RolesView = React.memo(
25
27
  setDialogOpen(false);
26
28
  };
27
29
 
30
+ if (paused) {
31
+ return (
32
+ <Container className="w-full flex flex-col py-4 gap-4" maxWidth={"6xl"}>
33
+ {children}
34
+ <Typography gutterBottom variant="h4"
35
+ className="mt-12"
36
+ component="h4">{t("roles")}</Typography>
37
+ <UserManagementPausedNotice/>
38
+ </Container>
39
+ );
40
+ }
41
+
28
42
  return (
29
43
  <Container className="w-full flex flex-col py-4 gap-4" maxWidth={"6xl"}>
30
44
 
@@ -1,13 +1,13 @@
1
1
  import { useState } from "react";
2
2
 
3
3
  import { format } from "date-fns";
4
- import * as locales from "date-fns/locale";
5
4
 
6
5
  import {
7
6
  defaultDateFormat,
8
7
  ConfirmationDialog, Role,
9
8
  useAuthController,
10
9
  useCustomizationController, User,
10
+ useDateFnsLocale,
11
11
  useSnackbarController,
12
12
  useTranslation
13
13
  } from "@firecms/core";
@@ -44,7 +44,7 @@ export function UsersTable({ onUserClicked }: {
44
44
  const snackbarController = useSnackbarController();
45
45
 
46
46
  const customizationController = useCustomizationController();
47
- const dateUtilsLocale = customizationController?.locale ? locales[customizationController?.locale as keyof typeof locales] : undefined;
47
+ const dateUtilsLocale = useDateFnsLocale(customizationController?.locale);
48
48
  const dateFormat: string = customizationController?.dateTimeFormat ?? defaultDateFormat;
49
49
 
50
50
  const [userToBeDeleted, setUserToBeDeleted] = useState<User | undefined>(undefined);
@@ -4,11 +4,13 @@ import { UsersTable } from "./UsersTable";
4
4
  import { UserDetailsForm } from "./UserDetailsForm";
5
5
  import React, { useCallback, useState } from "react";
6
6
  import { useUserManagement } from "../../hooks/useUserManagement";
7
- import { User, useTranslation
7
+ import { isProPaused, useLicenseStatus, User, useTranslation
8
8
  } from "@firecms/core";
9
+ import { UserManagementPausedNotice } from "../UserManagementPausedNotice";
9
10
 
10
11
  export const UsersView = function UsersView({ children }: { children?: React.ReactNode }) {
11
12
  const { t } = useTranslation();
13
+ const paused = isProPaused(useLicenseStatus());
12
14
 
13
15
 
14
16
  const [dialogOpen, setDialogOpen] = useState<boolean>(false);
@@ -33,6 +35,18 @@ export const UsersView = function UsersView({ children }: { children?: React.Rea
33
35
  setDialogOpen(true);
34
36
  }, []);
35
37
 
38
+ if (paused) {
39
+ return (
40
+ <Container className="w-full flex flex-col py-4 gap-4" maxWidth={"6xl"}>
41
+ {children}
42
+ <Typography gutterBottom variant="h4"
43
+ className="mt-12"
44
+ component="h4">{t("users")}</Typography>
45
+ <UserManagementPausedNotice/>
46
+ </Container>
47
+ );
48
+ }
49
+
36
50
  return (
37
51
  <Container className="w-full flex flex-col py-4 gap-4" maxWidth={"6xl"}>
38
52
 
@@ -200,7 +200,7 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
200
200
  path: usersPath,
201
201
  id: userExists.uid
202
202
  }
203
- await dataSourceDelegate.deleteEntity({ entity })
203
+ await dataSourceDelegate.deleteEntity({ entity, pathSegments: undefined })
204
204
  .then(() => {
205
205
  console.debug("Deleted previous user", userExists);
206
206
  })
@@ -213,6 +213,10 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
213
213
  return dataSourceDelegate.saveEntity({
214
214
  status: "existing",
215
215
  path: usersPath,
216
+ // `usersPath` / `rolesPath` are configurable path strings this hook never parses.
217
+ // Segments are left undefined rather than split out of one: absent means "not known
218
+ // here", and a split would be wrong for any configured id containing "/".
219
+ pathSegments: undefined,
216
220
  entityId: email,
217
221
  values: removeUndefined(data)
218
222
  }).then(() => user);
@@ -229,6 +233,10 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
229
233
  return dataSourceDelegate.saveEntity({
230
234
  status: "existing",
231
235
  path: rolesPath,
236
+ // `usersPath` / `rolesPath` are configurable path strings this hook never parses.
237
+ // Segments are left undefined rather than split out of one: absent means "not known
238
+ // here", and a split would be wrong for any configured id containing "/".
239
+ pathSegments: undefined,
232
240
  entityId: id,
233
241
  values: removeUndefined(roleData)
234
242
  }).then(() => {
@@ -246,7 +254,7 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
246
254
  id: uid,
247
255
  values: {}
248
256
  };
249
- await dataSourceDelegate.deleteEntity({ entity })
257
+ await dataSourceDelegate.deleteEntity({ entity, pathSegments: undefined })
250
258
  }, [usersPath, dataSourceDelegate?.initialised]);
251
259
 
252
260
  const deleteRole = useCallback(async (role: Role): Promise<void> => {
@@ -259,7 +267,7 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
259
267
  id: id,
260
268
  values: {}
261
269
  };
262
- await dataSourceDelegate.deleteEntity({ entity })
270
+ await dataSourceDelegate.deleteEntity({ entity, pathSegments: undefined })
263
271
  }, [rolesPath, dataSourceDelegate?.initialised]);
264
272
 
265
273
  const collectionPermissions: PermissionsBuilder = useCallback(({
@@ -1,4 +1,12 @@
1
- import { FireCMSPlugin, useAuthController, User, useSnackbarController, useTranslation } from "@firecms/core";
1
+ import {
2
+ FireCMSPlugin,
3
+ isProPaused,
4
+ useAuthController,
5
+ useLicenseStatus,
6
+ User,
7
+ useSnackbarController,
8
+ useTranslation
9
+ } from "@firecms/core";
2
10
  import { UserManagementProvider } from "./UserManagementProvider";
3
11
  import { UserManagement } from "./types";
4
12
  import { AddIcon, Button, Paper, Typography } from "@firecms/ui";
@@ -47,6 +55,12 @@ export function IntroWidget({
47
55
  const authController = useAuthController();
48
56
  const snackbarController = useSnackbarController();
49
57
  const { t } = useTranslation();
58
+ const licenseStatus = useLicenseStatus();
59
+
60
+ // Creating roles and users is paused with PRO; the Users and Roles views
61
+ // say so. The plugin itself stays mounted for sign-in and role checks.
62
+ if (isProPaused(licenseStatus))
63
+ return null;
50
64
 
51
65
  const buttonLabel = noUsers && noRoles
52
66
  ? t("create_default_roles_and_add_admin")