@strapi/admin 5.52.3 → 5.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/dist/admin/admin/src/App.js +1 -1
  2. package/dist/admin/admin/src/App.js.map +1 -1
  3. package/dist/admin/admin/src/App.mjs +1 -1
  4. package/dist/admin/admin/src/App.mjs.map +1 -1
  5. package/dist/admin/admin/src/components/DismissibleBanner.js +60 -0
  6. package/dist/admin/admin/src/components/DismissibleBanner.js.map +1 -0
  7. package/dist/admin/admin/src/components/DismissibleBanner.mjs +58 -0
  8. package/dist/admin/admin/src/components/DismissibleBanner.mjs.map +1 -0
  9. package/dist/admin/admin/src/components/Form.js +11 -2
  10. package/dist/admin/admin/src/components/Form.js.map +1 -1
  11. package/dist/admin/admin/src/components/Form.mjs +11 -2
  12. package/dist/admin/admin/src/components/Form.mjs.map +1 -1
  13. package/dist/admin/admin/src/components/FormInputs/DateTime.js +1 -1
  14. package/dist/admin/admin/src/components/FormInputs/DateTime.js.map +1 -1
  15. package/dist/admin/admin/src/components/FormInputs/DateTime.mjs +1 -1
  16. package/dist/admin/admin/src/components/FormInputs/DateTime.mjs.map +1 -1
  17. package/dist/admin/admin/src/components/FormInputs/Time.js +1 -1
  18. package/dist/admin/admin/src/components/FormInputs/Time.js.map +1 -1
  19. package/dist/admin/admin/src/components/FormInputs/Time.mjs +1 -1
  20. package/dist/admin/admin/src/components/FormInputs/Time.mjs.map +1 -1
  21. package/dist/admin/admin/src/components/MainNav/NavUser.js +1 -2
  22. package/dist/admin/admin/src/components/MainNav/NavUser.js.map +1 -1
  23. package/dist/admin/admin/src/components/MainNav/NavUser.mjs +1 -2
  24. package/dist/admin/admin/src/components/MainNav/NavUser.mjs.map +1 -1
  25. package/dist/admin/admin/src/components/MediaLibraryBanner.js +85 -0
  26. package/dist/admin/admin/src/components/MediaLibraryBanner.js.map +1 -0
  27. package/dist/admin/admin/src/components/MediaLibraryBanner.mjs +83 -0
  28. package/dist/admin/admin/src/components/MediaLibraryBanner.mjs.map +1 -0
  29. package/dist/admin/admin/src/components/Providers.js +6 -6
  30. package/dist/admin/admin/src/components/Providers.js.map +1 -1
  31. package/dist/admin/admin/src/components/Providers.mjs +6 -6
  32. package/dist/admin/admin/src/components/Providers.mjs.map +1 -1
  33. package/dist/admin/admin/src/components/UpsellBanner.js +56 -86
  34. package/dist/admin/admin/src/components/UpsellBanner.js.map +1 -1
  35. package/dist/admin/admin/src/components/UpsellBanner.mjs +58 -88
  36. package/dist/admin/admin/src/components/UpsellBanner.mjs.map +1 -1
  37. package/dist/admin/admin/src/core/store/configure.js +14 -2
  38. package/dist/admin/admin/src/core/store/configure.js.map +1 -1
  39. package/dist/admin/admin/src/core/store/configure.mjs +14 -2
  40. package/dist/admin/admin/src/core/store/configure.mjs.map +1 -1
  41. package/dist/admin/admin/src/core/store/hooks.mjs +1 -1
  42. package/dist/admin/admin/src/features/Auth.js +162 -46
  43. package/dist/admin/admin/src/features/Auth.js.map +1 -1
  44. package/dist/admin/admin/src/features/Auth.mjs +164 -48
  45. package/dist/admin/admin/src/features/Auth.mjs.map +1 -1
  46. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.js +19 -0
  47. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.js.map +1 -0
  48. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.mjs +17 -0
  49. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.mjs.map +1 -0
  50. package/dist/admin/admin/src/hooks/{useIdleSessionLogout.js → useSessionKeepalive.js} +34 -27
  51. package/dist/admin/admin/src/hooks/useSessionKeepalive.js.map +1 -0
  52. package/dist/admin/admin/src/hooks/{useIdleSessionLogout.mjs → useSessionKeepalive.mjs} +34 -27
  53. package/dist/admin/admin/src/hooks/useSessionKeepalive.mjs.map +1 -0
  54. package/dist/admin/admin/src/layouts/AuthenticatedLayout.js +2 -0
  55. package/dist/admin/admin/src/layouts/AuthenticatedLayout.js.map +1 -1
  56. package/dist/admin/admin/src/layouts/AuthenticatedLayout.mjs +2 -0
  57. package/dist/admin/admin/src/layouts/AuthenticatedLayout.mjs.map +1 -1
  58. package/dist/admin/admin/src/pages/SessionsPage.js +2 -5
  59. package/dist/admin/admin/src/pages/SessionsPage.js.map +1 -1
  60. package/dist/admin/admin/src/pages/SessionsPage.mjs +2 -5
  61. package/dist/admin/admin/src/pages/SessionsPage.mjs.map +1 -1
  62. package/dist/admin/admin/src/render.js +5 -0
  63. package/dist/admin/admin/src/render.js.map +1 -1
  64. package/dist/admin/admin/src/render.mjs +5 -0
  65. package/dist/admin/admin/src/render.mjs.map +1 -1
  66. package/dist/admin/admin/src/translations/en.json.js +30 -0
  67. package/dist/admin/admin/src/translations/en.json.js.map +1 -1
  68. package/dist/admin/admin/src/translations/en.json.mjs +30 -0
  69. package/dist/admin/admin/src/translations/en.json.mjs.map +1 -1
  70. package/dist/admin/admin/src/utils/baseQuery.js +3 -13
  71. package/dist/admin/admin/src/utils/baseQuery.js.map +1 -1
  72. package/dist/admin/admin/src/utils/baseQuery.mjs +3 -13
  73. package/dist/admin/admin/src/utils/baseQuery.mjs.map +1 -1
  74. package/dist/admin/admin/src/utils/getFetchClient.js +152 -48
  75. package/dist/admin/admin/src/utils/getFetchClient.js.map +1 -1
  76. package/dist/admin/admin/src/utils/getFetchClient.mjs +151 -50
  77. package/dist/admin/admin/src/utils/getFetchClient.mjs.map +1 -1
  78. package/dist/admin/admin/src/utils/unsavedChangesRegistry.js +25 -0
  79. package/dist/admin/admin/src/utils/unsavedChangesRegistry.js.map +1 -0
  80. package/dist/admin/admin/src/utils/unsavedChangesRegistry.mjs +22 -0
  81. package/dist/admin/admin/src/utils/unsavedChangesRegistry.mjs.map +1 -0
  82. package/dist/admin/admin/tests/utils.js +14 -14
  83. package/dist/admin/admin/tests/utils.js.map +1 -1
  84. package/dist/admin/admin/tests/utils.mjs +14 -14
  85. package/dist/admin/admin/tests/utils.mjs.map +1 -1
  86. package/dist/admin/ee/admin/src/constants.js +6 -0
  87. package/dist/admin/ee/admin/src/constants.js.map +1 -1
  88. package/dist/admin/ee/admin/src/constants.mjs +6 -0
  89. package/dist/admin/ee/admin/src/constants.mjs.map +1 -1
  90. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js +31 -3
  91. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js.map +1 -1
  92. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs +33 -5
  93. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs.map +1 -1
  94. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js +177 -0
  95. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js.map +1 -0
  96. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs +175 -0
  97. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs.map +1 -0
  98. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js +173 -0
  99. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js.map +1 -0
  100. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs +151 -0
  101. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs.map +1 -0
  102. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js +14 -1
  103. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js.map +1 -1
  104. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs +14 -1
  105. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs.map +1 -1
  106. package/dist/admin/index.js +3 -0
  107. package/dist/admin/index.js.map +1 -1
  108. package/dist/admin/index.mjs +1 -1
  109. package/dist/admin/shared/utils/audit-log-export.js +16 -0
  110. package/dist/admin/shared/utils/audit-log-export.js.map +1 -0
  111. package/dist/admin/shared/utils/audit-log-export.mjs +9 -0
  112. package/dist/admin/shared/utils/audit-log-export.mjs.map +1 -0
  113. package/dist/admin/src/components/DismissibleBanner.d.ts +8 -0
  114. package/dist/admin/src/components/MediaLibraryBanner.d.ts +2 -0
  115. package/dist/admin/src/hooks/useRegisterUnsavedChanges.d.ts +6 -0
  116. package/dist/admin/src/hooks/useSessionKeepalive.d.ts +88 -0
  117. package/dist/admin/src/utils/getFetchClient.d.ts +22 -7
  118. package/dist/admin/src/utils/unsavedChangesRegistry.d.ts +11 -0
  119. package/dist/admin/tests/store.d.ts +4 -0
  120. package/dist/admin/tests/utils.d.ts +4 -0
  121. package/dist/ee/admin/src/constants.d.ts +4 -0
  122. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.d.ts +10 -0
  123. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.d.ts +18 -0
  124. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.d.ts +13 -0
  125. package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts +1 -0
  126. package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts.map +1 -1
  127. package/dist/ee/server/src/audit-logs/routes/audit-logs.d.ts.map +1 -1
  128. package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts +24 -3
  129. package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts.map +1 -1
  130. package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts +8 -2
  131. package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts.map +1 -1
  132. package/dist/ee/server/src/audit-logs/utils/csv.d.ts +5 -0
  133. package/dist/ee/server/src/audit-logs/utils/csv.d.ts.map +1 -0
  134. package/dist/ee/server/src/audit-logs/utils/export-token.d.ts +5 -0
  135. package/dist/ee/server/src/audit-logs/utils/export-token.d.ts.map +1 -0
  136. package/dist/ee/server/src/audit-logs/utils.d.ts +10 -0
  137. package/dist/ee/server/src/audit-logs/utils.d.ts.map +1 -0
  138. package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts +2 -0
  139. package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts.map +1 -1
  140. package/dist/ee/server/src/config/admin-actions.d.ts.map +1 -1
  141. package/dist/ee/server/src/index.d.ts +1 -0
  142. package/dist/ee/server/src/index.d.ts.map +1 -1
  143. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js +50 -0
  144. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js.map +1 -1
  145. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs +51 -1
  146. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs.map +1 -1
  147. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js +13 -4
  148. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js.map +1 -1
  149. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs +13 -4
  150. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs.map +1 -1
  151. package/dist/server/ee/server/src/audit-logs/services/audit-logs.js +201 -7
  152. package/dist/server/ee/server/src/audit-logs/services/audit-logs.js.map +1 -1
  153. package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs +201 -8
  154. package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs.map +1 -1
  155. package/dist/server/ee/server/src/audit-logs/services/lifecycles.js +119 -34
  156. package/dist/server/ee/server/src/audit-logs/services/lifecycles.js.map +1 -1
  157. package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs +119 -34
  158. package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs.map +1 -1
  159. package/dist/server/ee/server/src/audit-logs/utils/csv.js +33 -0
  160. package/dist/server/ee/server/src/audit-logs/utils/csv.js.map +1 -0
  161. package/dist/server/ee/server/src/audit-logs/utils/csv.mjs +29 -0
  162. package/dist/server/ee/server/src/audit-logs/utils/csv.mjs.map +1 -0
  163. package/dist/server/ee/server/src/audit-logs/utils/export-token.js +33 -0
  164. package/dist/server/ee/server/src/audit-logs/utils/export-token.js.map +1 -0
  165. package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs +29 -0
  166. package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs.map +1 -0
  167. package/dist/server/ee/server/src/audit-logs/utils.js +14 -0
  168. package/dist/server/ee/server/src/audit-logs/utils.js.map +1 -0
  169. package/dist/server/ee/server/src/audit-logs/utils.mjs +12 -0
  170. package/dist/server/ee/server/src/audit-logs/utils.mjs.map +1 -0
  171. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js +11 -0
  172. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js.map +1 -1
  173. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs +11 -1
  174. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs.map +1 -1
  175. package/dist/server/ee/server/src/config/admin-actions.js +8 -0
  176. package/dist/server/ee/server/src/config/admin-actions.js.map +1 -1
  177. package/dist/server/ee/server/src/config/admin-actions.mjs +8 -0
  178. package/dist/server/ee/server/src/config/admin-actions.mjs.map +1 -1
  179. package/dist/server/ee/server/src/index.js +10 -0
  180. package/dist/server/ee/server/src/index.js.map +1 -1
  181. package/dist/server/ee/server/src/index.mjs +10 -0
  182. package/dist/server/ee/server/src/index.mjs.map +1 -1
  183. package/dist/server/shared/utils/audit-log-export.js +22 -0
  184. package/dist/server/shared/utils/audit-log-export.js.map +1 -0
  185. package/dist/server/shared/utils/audit-log-export.mjs +12 -0
  186. package/dist/server/shared/utils/audit-log-export.mjs.map +1 -0
  187. package/dist/shared/contracts/audit-logs.d.ts +15 -1
  188. package/dist/shared/contracts/audit-logs.d.ts.map +1 -1
  189. package/dist/shared/contracts/shared.d.ts +1 -0
  190. package/dist/shared/contracts/shared.d.ts.map +1 -1
  191. package/dist/shared/utils/audit-log-export.d.ts +9 -0
  192. package/dist/shared/utils/audit-log-export.d.ts.map +1 -0
  193. package/package.json +7 -7
  194. package/dist/admin/admin/src/hooks/useIdleSessionLogout.js.map +0 -1
  195. package/dist/admin/admin/src/hooks/useIdleSessionLogout.mjs.map +0 -1
  196. package/dist/admin/src/hooks/useIdleSessionLogout.d.ts +0 -87
@@ -1,111 +1,81 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { useEffect } from 'react';
3
3
  import { useLicenseLimits } from '@strapi/admin/strapi-admin/ee';
4
- import { IconButton, Box, Flex, Typography, LinkButton } from '@strapi/design-system';
5
- import { ArrowsOut, Cross } from '@strapi/icons';
4
+ import { IconButton, Box, LinkButton, Typography } from '@strapi/design-system';
5
+ import { ArrowsOut } from '@strapi/icons';
6
6
  import { subDays, isAfter, isValid } from 'date-fns';
7
7
  import { useIntl } from 'react-intl';
8
8
  import { styled } from 'styled-components';
9
9
  import { useGetLicenseTrialTimeLeftQuery } from '../services/admin.mjs';
10
- import { RESPONSIVE_DEFAULT_SPACING } from '../constants/theme.mjs';
11
10
  import { useScopedPersistentState } from '../hooks/usePersistentState.mjs';
11
+ import { DismissibleBanner } from './DismissibleBanner.mjs';
12
12
 
13
- const BannerBackground = styled(Flex)`
14
- background: linear-gradient(
15
- 90deg,
16
- ${({ theme })=>theme.colors.primary600} 0%,
17
- ${({ theme })=>theme.colors.alternative600} 121.48%
18
- );
19
- position: relative;
20
- `;
21
- const FixedButtonWrapper = styled(Box)`
13
+ /**
14
+ * Unlike `DismissibleBanner`'s own close button, this has no banner box left to
15
+ * anchor to once dismissed (the banner unmounts entirely), so it stays pinned
16
+ * to the viewport instead.
17
+ */ const FixedButtonWrapper = styled(Box)`
22
18
  position: fixed;
23
19
  display: flex;
24
20
  flex-direction: column;
25
- z-index: 11;
21
+ z-index: 1;
26
22
  align-items: flex-end;
27
23
  top: 9px;
28
24
  right: 16px;
29
25
  `;
30
26
  const Banner = ({ isTrialEndedRecently, onDismiss })=>{
31
27
  const { formatMessage } = useIntl();
32
- return /*#__PURE__*/ jsxs(Fragment, {
33
- children: [
34
- /*#__PURE__*/ jsx(BannerBackground, {
35
- width: "100%",
36
- justifyContent: "center",
37
- children: /*#__PURE__*/ jsxs(Flex, {
38
- justifyContent: "center",
39
- alignItems: "center",
40
- width: "100%",
41
- paddingTop: 2,
42
- paddingBottom: 2,
43
- paddingLeft: RESPONSIVE_DEFAULT_SPACING,
44
- paddingRight: RESPONSIVE_DEFAULT_SPACING,
45
- gap: 2,
46
- children: [
47
- /*#__PURE__*/ jsxs(Box, {
48
- children: [
49
- /*#__PURE__*/ jsx(Typography, {
50
- variant: "delta",
51
- fontWeight: "bold",
52
- textColor: "neutral0",
53
- textAlign: "center",
54
- fontSize: 2,
55
- children: formatMessage(isTrialEndedRecently ? {
56
- id: 'app.components.UpsellBanner.intro.ended',
57
- defaultMessage: 'Your trial has ended: '
58
- } : {
59
- id: 'app.components.UpsellBanner.intro',
60
- defaultMessage: 'Access to Growth plan features: '
61
- })
62
- }),
63
- /*#__PURE__*/ jsx(Typography, {
64
- variant: "delta",
65
- textColor: "neutral0",
66
- textAlign: "center",
67
- paddingRight: 4,
68
- fontSize: 2,
69
- children: formatMessage(isTrialEndedRecently ? {
70
- id: 'app.components.UpsellBanner.text.ended',
71
- defaultMessage: 'Keep access to Growth features by upgrading now.'
72
- } : {
73
- id: 'app.components.UpsellBanner.text',
74
- defaultMessage: 'As part of your trial, you can explore premium tools such as Content History, Releases, and Single Sign-On (SSO).'
75
- })
76
- })
77
- ]
78
- }),
79
- /*#__PURE__*/ jsx(Box, {
80
- children: /*#__PURE__*/ jsx(LinkButton, {
81
- width: "max-content",
82
- variant: "tertiary",
83
- href: "https://billing.strapi.io",
84
- target: "_blank",
85
- children: formatMessage(isTrialEndedRecently ? {
86
- id: 'app.components.UpsellBanner.button.ended',
87
- defaultMessage: 'Keep Growth plan'
88
- } : {
89
- id: 'app.components.UpsellBanner.button',
90
- defaultMessage: 'Upgrade now'
91
- })
92
- })
93
- })
94
- ]
95
- })
96
- }),
97
- /*#__PURE__*/ jsx(FixedButtonWrapper, {
98
- children: /*#__PURE__*/ jsx(IconButton, {
99
- withTooltip: false,
100
- label: formatMessage({
101
- id: 'app.components.UpsellBanner.close',
102
- defaultMessage: 'Close'
103
- }),
104
- onClick: onDismiss,
105
- children: /*#__PURE__*/ jsx(Cross, {})
28
+ return /*#__PURE__*/ jsx(DismissibleBanner, {
29
+ onDismiss: onDismiss,
30
+ closeLabel: formatMessage({
31
+ id: 'app.components.UpsellBanner.close',
32
+ defaultMessage: 'Close'
33
+ }),
34
+ message: /*#__PURE__*/ jsxs(Fragment, {
35
+ children: [
36
+ /*#__PURE__*/ jsx(Typography, {
37
+ variant: "delta",
38
+ fontWeight: "bold",
39
+ textColor: "neutral0",
40
+ textAlign: "center",
41
+ fontSize: 2,
42
+ children: formatMessage(isTrialEndedRecently ? {
43
+ id: 'app.components.UpsellBanner.intro.ended',
44
+ defaultMessage: 'Your trial has ended: '
45
+ } : {
46
+ id: 'app.components.UpsellBanner.intro',
47
+ defaultMessage: 'Access to Growth plan features: '
48
+ })
49
+ }),
50
+ /*#__PURE__*/ jsx(Typography, {
51
+ variant: "delta",
52
+ textColor: "neutral0",
53
+ textAlign: "center",
54
+ paddingRight: 4,
55
+ fontSize: 2,
56
+ children: formatMessage(isTrialEndedRecently ? {
57
+ id: 'app.components.UpsellBanner.text.ended',
58
+ defaultMessage: 'Keep access to Growth features by upgrading now.'
59
+ } : {
60
+ id: 'app.components.UpsellBanner.text',
61
+ defaultMessage: 'As part of your trial, you can explore premium tools such as Content History, Releases, and Single Sign-On (SSO).'
62
+ })
106
63
  })
64
+ ]
65
+ }),
66
+ action: /*#__PURE__*/ jsx(LinkButton, {
67
+ width: "max-content",
68
+ variant: "tertiary",
69
+ href: "https://billing.strapi.io",
70
+ target: "_blank",
71
+ children: formatMessage(isTrialEndedRecently ? {
72
+ id: 'app.components.UpsellBanner.button.ended',
73
+ defaultMessage: 'Keep Growth plan'
74
+ } : {
75
+ id: 'app.components.UpsellBanner.button',
76
+ defaultMessage: 'Upgrade now'
107
77
  })
108
- ]
78
+ })
109
79
  });
110
80
  };
111
81
  const UpsellBanner = ()=>{
@@ -1 +1 @@
1
- {"version":3,"file":"UpsellBanner.mjs","sources":["../../../../../admin/src/components/UpsellBanner.tsx"],"sourcesContent":["import { useEffect } from 'react';\n\nimport { useLicenseLimits } from '@strapi/admin/strapi-admin/ee';\nimport { Box, Flex, IconButton, LinkButton, Typography } from '@strapi/design-system';\nimport { ArrowsOut, Cross } from '@strapi/icons';\nimport { isAfter, isValid, subDays } from 'date-fns';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useGetLicenseTrialTimeLeftQuery } from '../../src/services/admin';\nimport { RESPONSIVE_DEFAULT_SPACING } from '../constants/theme';\nimport { useScopedPersistentState } from '../hooks/usePersistentState';\n\nconst BannerBackground = styled(Flex)`\n background: linear-gradient(\n 90deg,\n ${({ theme }) => theme.colors.primary600} 0%,\n ${({ theme }) => theme.colors.alternative600} 121.48%\n );\n position: relative;\n`;\n\nconst FixedButtonWrapper = styled(Box)`\n position: fixed;\n display: flex;\n flex-direction: column;\n z-index: 11;\n align-items: flex-end;\n top: 9px;\n right: 16px;\n`;\n\nconst Banner = ({\n isTrialEndedRecently,\n onDismiss,\n}: {\n isTrialEndedRecently: boolean;\n onDismiss: () => void;\n}) => {\n const { formatMessage } = useIntl();\n\n return (\n <>\n <BannerBackground width=\"100%\" justifyContent=\"center\">\n <Flex\n justifyContent=\"center\"\n alignItems=\"center\"\n width=\"100%\"\n paddingTop={2}\n paddingBottom={2}\n paddingLeft={RESPONSIVE_DEFAULT_SPACING}\n paddingRight={RESPONSIVE_DEFAULT_SPACING}\n gap={2}\n >\n <Box>\n <Typography\n variant=\"delta\"\n fontWeight=\"bold\"\n textColor=\"neutral0\"\n textAlign=\"center\"\n fontSize={2}\n >\n {formatMessage(\n isTrialEndedRecently\n ? {\n id: 'app.components.UpsellBanner.intro.ended',\n defaultMessage: 'Your trial has ended: ',\n }\n : {\n id: 'app.components.UpsellBanner.intro',\n defaultMessage: 'Access to Growth plan features: ',\n }\n )}\n </Typography>\n <Typography\n variant=\"delta\"\n textColor=\"neutral0\"\n textAlign=\"center\"\n paddingRight={4}\n fontSize={2}\n >\n {formatMessage(\n isTrialEndedRecently\n ? {\n id: 'app.components.UpsellBanner.text.ended',\n defaultMessage: 'Keep access to Growth features by upgrading now.',\n }\n : {\n id: 'app.components.UpsellBanner.text',\n defaultMessage:\n 'As part of your trial, you can explore premium tools such as Content History, Releases, and Single Sign-On (SSO).',\n }\n )}\n </Typography>\n </Box>\n <Box>\n <LinkButton\n width=\"max-content\"\n variant=\"tertiary\"\n href=\"https://billing.strapi.io\"\n target=\"_blank\"\n >\n {formatMessage(\n isTrialEndedRecently\n ? {\n id: 'app.components.UpsellBanner.button.ended',\n defaultMessage: 'Keep Growth plan',\n }\n : {\n id: 'app.components.UpsellBanner.button',\n defaultMessage: 'Upgrade now',\n }\n )}\n </LinkButton>\n </Box>\n </Flex>\n </BannerBackground>\n <FixedButtonWrapper>\n <IconButton\n withTooltip={false}\n label={formatMessage({\n id: 'app.components.UpsellBanner.close',\n defaultMessage: 'Close',\n })}\n onClick={onDismiss}\n >\n <Cross />\n </IconButton>\n </FixedButtonWrapper>\n </>\n );\n};\n\nconst UpsellBanner = () => {\n const { license } = useLicenseLimits();\n const { formatMessage } = useIntl();\n\n const [cachedTrialEndsAt, setCachedTrialEndsAt] = useScopedPersistentState<string | undefined>(\n 'STRAPI_FREE_TRIAL_ENDS_AT',\n undefined\n );\n\n const [dismissedFor, setDismissedFor] = useScopedPersistentState<string | undefined>(\n 'STRAPI_UPSELL_BANNER_DISMISSED_FOR',\n undefined\n );\n\n const sevenDaysAgo = subDays(new Date(), 7);\n\n const timeLeftData = useGetLicenseTrialTimeLeftQuery(undefined, {\n skip: !license?.isTrial,\n });\n\n useEffect(() => {\n if (timeLeftData.data?.trialEndsAt) {\n setCachedTrialEndsAt(timeLeftData.data.trialEndsAt);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [timeLeftData.data?.trialEndsAt]);\n\n // When the license is not a trial + not EE, and the cached trial end date is found in the localstorage, that means the trial has ended\n // We show the banner to encourage the user to upgrade (for 7 days after the trial ends)\n const isTrialEndedRecently = Boolean(\n !license?.isTrial &&\n !window.strapi.isEE &&\n cachedTrialEndsAt &&\n isAfter(new Date(cachedTrialEndsAt), sevenDaysAgo)\n );\n\n const trialEndsAt = timeLeftData.data?.trialEndsAt ?? cachedTrialEndsAt;\n\n const toCanonicalISO = (v: string | undefined): string | undefined => {\n if (!v) return undefined;\n const date = new Date(v);\n return isValid(date) ? date.toISOString() : undefined;\n };\n\n const isDismissed = Boolean(\n trialEndsAt && toCanonicalISO(dismissedFor) === toCanonicalISO(trialEndsAt)\n );\n\n const handleDismiss = () => setDismissedFor(toCanonicalISO(trialEndsAt));\n const handleReopen = () => setDismissedFor(undefined);\n\n if (!(timeLeftData.data?.trialEndsAt || isTrialEndedRecently)) {\n return null;\n }\n\n if (isDismissed) {\n return (\n <FixedButtonWrapper>\n <IconButton\n withTooltip={false}\n label={formatMessage({\n id: 'app.components.UpsellBanner.reopen',\n defaultMessage: 'Reopen banner',\n })}\n onClick={handleReopen}\n >\n <ArrowsOut />\n </IconButton>\n </FixedButtonWrapper>\n );\n }\n\n return <Banner isTrialEndedRecently={isTrialEndedRecently} onDismiss={handleDismiss} />;\n};\n\nexport { UpsellBanner };\n"],"names":["BannerBackground","styled","Flex","theme","colors","primary600","alternative600","FixedButtonWrapper","Box","Banner","isTrialEndedRecently","onDismiss","formatMessage","useIntl","_jsxs","_Fragment","_jsx","width","justifyContent","alignItems","paddingTop","paddingBottom","paddingLeft","RESPONSIVE_DEFAULT_SPACING","paddingRight","gap","Typography","variant","fontWeight","textColor","textAlign","fontSize","id","defaultMessage","LinkButton","href","target","IconButton","withTooltip","label","onClick","Cross","UpsellBanner","license","useLicenseLimits","cachedTrialEndsAt","setCachedTrialEndsAt","useScopedPersistentState","undefined","dismissedFor","setDismissedFor","sevenDaysAgo","subDays","Date","timeLeftData","useGetLicenseTrialTimeLeftQuery","skip","isTrial","useEffect","data","trialEndsAt","Boolean","window","strapi","isEE","isAfter","toCanonicalISO","v","date","isValid","toISOString","isDismissed","handleDismiss","handleReopen","ArrowsOut"],"mappings":"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"}
1
+ {"version":3,"file":"UpsellBanner.mjs","sources":["../../../../../admin/src/components/UpsellBanner.tsx"],"sourcesContent":["import { useEffect } from 'react';\n\nimport { useLicenseLimits } from '@strapi/admin/strapi-admin/ee';\nimport { Box, IconButton, LinkButton, Typography } from '@strapi/design-system';\nimport { ArrowsOut } from '@strapi/icons';\nimport { isAfter, isValid, subDays } from 'date-fns';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useGetLicenseTrialTimeLeftQuery } from '../../src/services/admin';\nimport { useScopedPersistentState } from '../hooks/usePersistentState';\n\nimport { DismissibleBanner } from './DismissibleBanner';\n\n/**\n * Unlike `DismissibleBanner`'s own close button, this has no banner box left to\n * anchor to once dismissed (the banner unmounts entirely), so it stays pinned\n * to the viewport instead.\n */\nconst FixedButtonWrapper = styled(Box)`\n position: fixed;\n display: flex;\n flex-direction: column;\n z-index: 1;\n align-items: flex-end;\n top: 9px;\n right: 16px;\n`;\n\nconst Banner = ({\n isTrialEndedRecently,\n onDismiss,\n}: {\n isTrialEndedRecently: boolean;\n onDismiss: () => void;\n}) => {\n const { formatMessage } = useIntl();\n\n return (\n <DismissibleBanner\n onDismiss={onDismiss}\n closeLabel={formatMessage({\n id: 'app.components.UpsellBanner.close',\n defaultMessage: 'Close',\n })}\n message={\n <>\n <Typography\n variant=\"delta\"\n fontWeight=\"bold\"\n textColor=\"neutral0\"\n textAlign=\"center\"\n fontSize={2}\n >\n {formatMessage(\n isTrialEndedRecently\n ? {\n id: 'app.components.UpsellBanner.intro.ended',\n defaultMessage: 'Your trial has ended: ',\n }\n : {\n id: 'app.components.UpsellBanner.intro',\n defaultMessage: 'Access to Growth plan features: ',\n }\n )}\n </Typography>\n <Typography\n variant=\"delta\"\n textColor=\"neutral0\"\n textAlign=\"center\"\n paddingRight={4}\n fontSize={2}\n >\n {formatMessage(\n isTrialEndedRecently\n ? {\n id: 'app.components.UpsellBanner.text.ended',\n defaultMessage: 'Keep access to Growth features by upgrading now.',\n }\n : {\n id: 'app.components.UpsellBanner.text',\n defaultMessage:\n 'As part of your trial, you can explore premium tools such as Content History, Releases, and Single Sign-On (SSO).',\n }\n )}\n </Typography>\n </>\n }\n action={\n <LinkButton\n width=\"max-content\"\n variant=\"tertiary\"\n href=\"https://billing.strapi.io\"\n target=\"_blank\"\n >\n {formatMessage(\n isTrialEndedRecently\n ? {\n id: 'app.components.UpsellBanner.button.ended',\n defaultMessage: 'Keep Growth plan',\n }\n : {\n id: 'app.components.UpsellBanner.button',\n defaultMessage: 'Upgrade now',\n }\n )}\n </LinkButton>\n }\n />\n );\n};\n\nconst UpsellBanner = () => {\n const { license } = useLicenseLimits();\n const { formatMessage } = useIntl();\n\n const [cachedTrialEndsAt, setCachedTrialEndsAt] = useScopedPersistentState<string | undefined>(\n 'STRAPI_FREE_TRIAL_ENDS_AT',\n undefined\n );\n\n const [dismissedFor, setDismissedFor] = useScopedPersistentState<string | undefined>(\n 'STRAPI_UPSELL_BANNER_DISMISSED_FOR',\n undefined\n );\n\n const sevenDaysAgo = subDays(new Date(), 7);\n\n const timeLeftData = useGetLicenseTrialTimeLeftQuery(undefined, {\n skip: !license?.isTrial,\n });\n\n useEffect(() => {\n if (timeLeftData.data?.trialEndsAt) {\n setCachedTrialEndsAt(timeLeftData.data.trialEndsAt);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [timeLeftData.data?.trialEndsAt]);\n\n // When the license is not a trial + not EE, and the cached trial end date is found in the localstorage, that means the trial has ended\n // We show the banner to encourage the user to upgrade (for 7 days after the trial ends)\n const isTrialEndedRecently = Boolean(\n !license?.isTrial &&\n !window.strapi.isEE &&\n cachedTrialEndsAt &&\n isAfter(new Date(cachedTrialEndsAt), sevenDaysAgo)\n );\n\n const trialEndsAt = timeLeftData.data?.trialEndsAt ?? cachedTrialEndsAt;\n\n const toCanonicalISO = (v: string | undefined): string | undefined => {\n if (!v) return undefined;\n const date = new Date(v);\n return isValid(date) ? date.toISOString() : undefined;\n };\n\n const isDismissed = Boolean(\n trialEndsAt && toCanonicalISO(dismissedFor) === toCanonicalISO(trialEndsAt)\n );\n\n const handleDismiss = () => setDismissedFor(toCanonicalISO(trialEndsAt));\n const handleReopen = () => setDismissedFor(undefined);\n\n if (!(timeLeftData.data?.trialEndsAt || isTrialEndedRecently)) {\n return null;\n }\n\n if (isDismissed) {\n return (\n <FixedButtonWrapper>\n <IconButton\n withTooltip={false}\n label={formatMessage({\n id: 'app.components.UpsellBanner.reopen',\n defaultMessage: 'Reopen banner',\n })}\n onClick={handleReopen}\n >\n <ArrowsOut />\n </IconButton>\n </FixedButtonWrapper>\n );\n }\n\n return <Banner isTrialEndedRecently={isTrialEndedRecently} onDismiss={handleDismiss} />;\n};\n\nexport { UpsellBanner };\n"],"names":["FixedButtonWrapper","styled","Box","Banner","isTrialEndedRecently","onDismiss","formatMessage","useIntl","_jsx","DismissibleBanner","closeLabel","id","defaultMessage","message","_jsxs","_Fragment","Typography","variant","fontWeight","textColor","textAlign","fontSize","paddingRight","action","LinkButton","width","href","target","UpsellBanner","license","useLicenseLimits","cachedTrialEndsAt","setCachedTrialEndsAt","useScopedPersistentState","undefined","dismissedFor","setDismissedFor","sevenDaysAgo","subDays","Date","timeLeftData","useGetLicenseTrialTimeLeftQuery","skip","isTrial","useEffect","data","trialEndsAt","Boolean","window","strapi","isEE","isAfter","toCanonicalISO","v","date","isValid","toISOString","isDismissed","handleDismiss","handleReopen","IconButton","withTooltip","label","onClick","ArrowsOut"],"mappings":"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"}
@@ -3,6 +3,7 @@
3
3
  var toolkit = require('@reduxjs/toolkit');
4
4
  var reducer = require('../../reducer.js');
5
5
  var api = require('../../services/api.js');
6
+ var getFetchClient = require('../../utils/getFetchClient.js');
6
7
  var basename = require('../utils/basename.js');
7
8
 
8
9
  /**
@@ -60,9 +61,20 @@ const injectReducerStoreEnhancer = (appReducers)=>(next)=>(...args)=>{
60
61
  });
61
62
  return store;
62
63
  };
63
- const rtkQueryUnauthorizedMiddleware = ({ dispatch })=>(next)=>(action)=>{
64
+ /**
65
+ * A 401 here means the fetch layer already tried to refresh the access token and
66
+ * the server refused: the session is over. Hand that to `AuthProvider`, which
67
+ * can warn about unsaved changes before clearing auth state. Reloading the page
68
+ * instead (as this used to) unmounts every form first, so the warning — and the
69
+ * user's edits — never make it to the screen. Falls back to the hard redirect
70
+ * when no provider is listening, so the user is never stranded on a dead page.
71
+ *
72
+ * `triggerSessionExpired` is idempotent per dead session (baseQuery often
73
+ * already called it for this 401). The `!` check is only the no-listener
74
+ * fallback; it must not run logout a second time.
75
+ */ const rtkQueryUnauthorizedMiddleware = ({ dispatch })=>(next)=>(action)=>{
64
76
  // isRejectedWithValue Or isRejected
65
- if (toolkit.isRejected(action) && action.payload?.status === 401) {
77
+ if (toolkit.isRejected(action) && action.payload?.status === 401 && !getFetchClient.triggerSessionExpired()) {
66
78
  dispatch(reducer.logout());
67
79
  const basename$1 = basename.getBasename();
68
80
  window.location.href = `${basename$1}/auth/login`;
@@ -1 +1 @@
1
- {"version":3,"file":"configure.js","sources":["../../../../../../admin/src/core/store/configure.ts"],"sourcesContent":["import {\n configureStore,\n StoreEnhancer,\n Middleware,\n Reducer,\n combineReducers,\n MiddlewareAPI,\n isRejected,\n} from '@reduxjs/toolkit';\n\nimport { reducer as appReducer, AppState, logout } from '../../reducer';\nimport { adminApi } from '../../services/api';\nimport { getBasename } from '../utils/basename';\n\n/**\n * @description Static reducers are ones we know, they live in the admin package.\n */\nconst staticReducers = {\n [adminApi.reducerPath]: adminApi.reducer,\n admin_app: appReducer,\n} as const;\n\nconst injectReducerStoreEnhancer: (appReducers: Record<string, Reducer>) => StoreEnhancer =\n (appReducers) =>\n (next) =>\n (...args) => {\n const store = next(...args);\n\n const asyncReducers: Record<string, Reducer> = {};\n\n return {\n ...store,\n asyncReducers,\n injectReducer: (key: string, asyncReducer: Reducer) => {\n asyncReducers[key] = asyncReducer;\n store.replaceReducer(\n // @ts-expect-error we dynamically add reducers which makes the types uncomfortable.\n combineReducers({\n ...appReducers,\n ...asyncReducers,\n })\n );\n },\n };\n };\n\ntype PreloadState = Partial<{\n admin_app: AppState;\n}>;\n\ntype DefaultMiddlewareOptions = {\n serializableCheck?: boolean;\n immutableCheck?: boolean;\n};\n\n/**\n * @description This is the main store configuration function, injected Reducers use our legacy app.addReducer API,\n * which we're trying to phase out. App Middlewares could potentially be improved...?\n */\nconst configureStoreImpl = (\n preloadedState: PreloadState = {},\n appMiddlewares: Array<() => Middleware> = [],\n injectedReducers: Record<string, Reducer> = {}\n) => {\n const coreReducers = { ...staticReducers, ...injectedReducers } as const;\n\n const defaultMiddlewareOptions: DefaultMiddlewareOptions = {};\n\n // These are already disabled in 'production' env but we also need to disable it in test environments\n // However, we want to leave them on for development so any issues can still be caught\n if (process.env.NODE_ENV === 'test') {\n defaultMiddlewareOptions.serializableCheck = false;\n defaultMiddlewareOptions.immutableCheck = false;\n }\n\n const store = configureStore({\n preloadedState: {\n admin_app: preloadedState.admin_app,\n },\n reducer: coreReducers,\n devTools: process.env.NODE_ENV !== 'production',\n middleware: (getDefaultMiddleware) => [\n ...getDefaultMiddleware(defaultMiddlewareOptions),\n rtkQueryUnauthorizedMiddleware,\n adminApi.middleware,\n ...appMiddlewares.map((m) => m()),\n ],\n enhancers: [injectReducerStoreEnhancer(coreReducers)],\n });\n\n return store;\n};\n\nconst rtkQueryUnauthorizedMiddleware: Middleware =\n ({ dispatch }: MiddlewareAPI) =>\n (next) =>\n (action) => {\n // isRejectedWithValue Or isRejected\n if (isRejected(action) && action.payload?.status === 401) {\n dispatch(logout());\n const basename = getBasename();\n window.location.href = `${basename}/auth/login`;\n return;\n }\n\n return next(action);\n };\n\ntype Store = ReturnType<typeof configureStoreImpl> & {\n asyncReducers: Record<string, Reducer>;\n injectReducer: (key: string, asyncReducer: Reducer) => void;\n};\n\ntype RootState = ReturnType<Store['getState']>;\n\ntype Dispatch = Store['dispatch'];\n\nexport { configureStoreImpl as configureStore };\nexport type { RootState, Dispatch, AppState, Store, PreloadState };\n"],"names":["staticReducers","adminApi","reducerPath","reducer","admin_app","appReducer","injectReducerStoreEnhancer","appReducers","next","args","store","asyncReducers","injectReducer","key","asyncReducer","replaceReducer","combineReducers","configureStoreImpl","preloadedState","appMiddlewares","injectedReducers","coreReducers","defaultMiddlewareOptions","process","env","NODE_ENV","serializableCheck","immutableCheck","configureStore","devTools","middleware","getDefaultMiddleware","rtkQueryUnauthorizedMiddleware","map","m","enhancers","dispatch","action","isRejected","payload","status","logout","basename","getBasename","window","location","href"],"mappings":";;;;;;;AAcA;;AAEC,IACD,MAAMA,cAAAA,GAAiB;AACrB,IAAA,CAACC,YAAAA,CAASC,WAAW,GAAGD,aAASE,OAAO;IACxCC,SAAAA,EAAWC;AACb,CAAA;AAEA,MAAMC,6BACJ,CAACC,WAAAA,GACD,CAACC,IAAAA,GACD,CAAC,GAAGC,IAAAA,GAAAA;AACF,YAAA,MAAMC,QAAQF,IAAAA,CAAAA,GAAQC,IAAAA,CAAAA;AAEtB,YAAA,MAAME,gBAAyC,EAAC;YAEhD,OAAO;AACL,gBAAA,GAAGD,KAAK;AACRC,gBAAAA,aAAAA;AACAC,gBAAAA,aAAAA,EAAe,CAACC,GAAAA,EAAaC,YAAAA,GAAAA;oBAC3BH,aAAa,CAACE,IAAI,GAAGC,YAAAA;oBACrBJ,KAAAA,CAAMK,cAAc;oBAElBC,uBAAAA,CAAgB;AACd,wBAAA,GAAGT,WAAW;AACd,wBAAA,GAAGI;AACL,qBAAA,CAAA,CAAA;AAEJ,gBAAA;AACF,aAAA;AACF,QAAA,CAAA;AAWF;;;AAGC,IACD,MAAMM,kBAAAA,GAAqB,CACzBC,cAAAA,GAA+B,EAAE,EACjCC,cAAAA,GAA0C,EAAE,EAC5CC,gBAAAA,GAA4C,EAAE,GAAA;AAE9C,IAAA,MAAMC,YAAAA,GAAe;AAAE,QAAA,GAAGrB,cAAc;AAAE,QAAA,GAAGoB;AAAiB,KAAA;AAE9D,IAAA,MAAME,2BAAqD,EAAC;;;AAI5D,IAAA,IAAIC,OAAAA,CAAQC,GAAG,CAACC,QAAQ,KAAK,MAAA,EAAQ;AACnCH,QAAAA,wBAAAA,CAAyBI,iBAAiB,GAAG,KAAA;AAC7CJ,QAAAA,wBAAAA,CAAyBK,cAAc,GAAG,KAAA;AAC5C,IAAA;AAEA,IAAA,MAAMjB,QAAQkB,sBAAAA,CAAe;QAC3BV,cAAAA,EAAgB;AACdd,YAAAA,SAAAA,EAAWc,eAAed;AAC5B,SAAA;QACAD,OAAAA,EAASkB,YAAAA;AACTQ,QAAAA,QAAAA,EAAUN,OAAAA,CAAQC,GAAG,CAACC,QAAQ,KAAK,YAAA;AACnCK,QAAAA,UAAAA,EAAY,CAACC,oBAAAA,GAAyB;mBACjCA,oBAAAA,CAAqBT,wBAAAA,CAAAA;AACxBU,gBAAAA,8BAAAA;AACA/B,gBAAAA,YAAAA,CAAS6B,UAAU;mBAChBX,cAAAA,CAAec,GAAG,CAAC,CAACC,CAAAA,GAAMA,CAAAA,EAAAA;AAC9B,aAAA;QACDC,SAAAA,EAAW;YAAC7B,0BAAAA,CAA2Be,YAAAA;AAAc;AACvD,KAAA,CAAA;IAEA,OAAOX,KAAAA;AACT;AAEA,MAAMsB,8BAAAA,GACJ,CAAC,EAAEI,QAAQ,EAAiB,GAC5B,CAAC5B,OACD,CAAC6B,MAAAA,GAAAA;;AAEC,YAAA,IAAIC,mBAAWD,MAAAA,CAAAA,IAAWA,MAAAA,CAAOE,OAAO,EAAEC,WAAW,GAAA,EAAK;gBACxDJ,QAAAA,CAASK,cAAAA,EAAAA,CAAAA;AACT,gBAAA,MAAMC,UAAAA,GAAWC,oBAAAA,EAAAA;AACjBC,gBAAAA,MAAAA,CAAOC,QAAQ,CAACC,IAAI,GAAG,CAAA,EAAGJ,UAAAA,CAAS,WAAW,CAAC;AAC/C,gBAAA;AACF,YAAA;AAEA,YAAA,OAAOlC,IAAAA,CAAK6B,MAAAA,CAAAA;AACd,QAAA,CAAA;;;;"}
1
+ {"version":3,"file":"configure.js","sources":["../../../../../../admin/src/core/store/configure.ts"],"sourcesContent":["import {\n configureStore,\n StoreEnhancer,\n Middleware,\n Reducer,\n combineReducers,\n MiddlewareAPI,\n isRejected,\n} from '@reduxjs/toolkit';\n\nimport { reducer as appReducer, AppState, logout } from '../../reducer';\nimport { adminApi } from '../../services/api';\nimport { triggerSessionExpired } from '../../utils/getFetchClient';\nimport { getBasename } from '../utils/basename';\n\n/**\n * @description Static reducers are ones we know, they live in the admin package.\n */\nconst staticReducers = {\n [adminApi.reducerPath]: adminApi.reducer,\n admin_app: appReducer,\n} as const;\n\nconst injectReducerStoreEnhancer: (appReducers: Record<string, Reducer>) => StoreEnhancer =\n (appReducers) =>\n (next) =>\n (...args) => {\n const store = next(...args);\n\n const asyncReducers: Record<string, Reducer> = {};\n\n return {\n ...store,\n asyncReducers,\n injectReducer: (key: string, asyncReducer: Reducer) => {\n asyncReducers[key] = asyncReducer;\n store.replaceReducer(\n // @ts-expect-error we dynamically add reducers which makes the types uncomfortable.\n combineReducers({\n ...appReducers,\n ...asyncReducers,\n })\n );\n },\n };\n };\n\ntype PreloadState = Partial<{\n admin_app: AppState;\n}>;\n\ntype DefaultMiddlewareOptions = {\n serializableCheck?: boolean;\n immutableCheck?: boolean;\n};\n\n/**\n * @description This is the main store configuration function, injected Reducers use our legacy app.addReducer API,\n * which we're trying to phase out. App Middlewares could potentially be improved...?\n */\nconst configureStoreImpl = (\n preloadedState: PreloadState = {},\n appMiddlewares: Array<() => Middleware> = [],\n injectedReducers: Record<string, Reducer> = {}\n) => {\n const coreReducers = { ...staticReducers, ...injectedReducers } as const;\n\n const defaultMiddlewareOptions: DefaultMiddlewareOptions = {};\n\n // These are already disabled in 'production' env but we also need to disable it in test environments\n // However, we want to leave them on for development so any issues can still be caught\n if (process.env.NODE_ENV === 'test') {\n defaultMiddlewareOptions.serializableCheck = false;\n defaultMiddlewareOptions.immutableCheck = false;\n }\n\n const store = configureStore({\n preloadedState: {\n admin_app: preloadedState.admin_app,\n },\n reducer: coreReducers,\n devTools: process.env.NODE_ENV !== 'production',\n middleware: (getDefaultMiddleware) => [\n ...getDefaultMiddleware(defaultMiddlewareOptions),\n rtkQueryUnauthorizedMiddleware,\n adminApi.middleware,\n ...appMiddlewares.map((m) => m()),\n ],\n enhancers: [injectReducerStoreEnhancer(coreReducers)],\n });\n\n return store;\n};\n\n/**\n * A 401 here means the fetch layer already tried to refresh the access token and\n * the server refused: the session is over. Hand that to `AuthProvider`, which\n * can warn about unsaved changes before clearing auth state. Reloading the page\n * instead (as this used to) unmounts every form first, so the warning — and the\n * user's edits — never make it to the screen. Falls back to the hard redirect\n * when no provider is listening, so the user is never stranded on a dead page.\n *\n * `triggerSessionExpired` is idempotent per dead session (baseQuery often\n * already called it for this 401). The `!` check is only the no-listener\n * fallback; it must not run logout a second time.\n */\nconst rtkQueryUnauthorizedMiddleware: Middleware =\n ({ dispatch }: MiddlewareAPI) =>\n (next) =>\n (action) => {\n // isRejectedWithValue Or isRejected\n if (isRejected(action) && action.payload?.status === 401 && !triggerSessionExpired()) {\n dispatch(logout());\n const basename = getBasename();\n window.location.href = `${basename}/auth/login`;\n return;\n }\n\n return next(action);\n };\n\ntype Store = ReturnType<typeof configureStoreImpl> & {\n asyncReducers: Record<string, Reducer>;\n injectReducer: (key: string, asyncReducer: Reducer) => void;\n};\n\ntype RootState = ReturnType<Store['getState']>;\n\ntype Dispatch = Store['dispatch'];\n\nexport { configureStoreImpl as configureStore };\nexport type { RootState, Dispatch, AppState, Store, PreloadState };\n"],"names":["staticReducers","adminApi","reducerPath","reducer","admin_app","appReducer","injectReducerStoreEnhancer","appReducers","next","args","store","asyncReducers","injectReducer","key","asyncReducer","replaceReducer","combineReducers","configureStoreImpl","preloadedState","appMiddlewares","injectedReducers","coreReducers","defaultMiddlewareOptions","process","env","NODE_ENV","serializableCheck","immutableCheck","configureStore","devTools","middleware","getDefaultMiddleware","rtkQueryUnauthorizedMiddleware","map","m","enhancers","dispatch","action","isRejected","payload","status","triggerSessionExpired","logout","basename","getBasename","window","location","href"],"mappings":";;;;;;;;AAeA;;AAEC,IACD,MAAMA,cAAAA,GAAiB;AACrB,IAAA,CAACC,YAAAA,CAASC,WAAW,GAAGD,aAASE,OAAO;IACxCC,SAAAA,EAAWC;AACb,CAAA;AAEA,MAAMC,6BACJ,CAACC,WAAAA,GACD,CAACC,IAAAA,GACD,CAAC,GAAGC,IAAAA,GAAAA;AACF,YAAA,MAAMC,QAAQF,IAAAA,CAAAA,GAAQC,IAAAA,CAAAA;AAEtB,YAAA,MAAME,gBAAyC,EAAC;YAEhD,OAAO;AACL,gBAAA,GAAGD,KAAK;AACRC,gBAAAA,aAAAA;AACAC,gBAAAA,aAAAA,EAAe,CAACC,GAAAA,EAAaC,YAAAA,GAAAA;oBAC3BH,aAAa,CAACE,IAAI,GAAGC,YAAAA;oBACrBJ,KAAAA,CAAMK,cAAc;oBAElBC,uBAAAA,CAAgB;AACd,wBAAA,GAAGT,WAAW;AACd,wBAAA,GAAGI;AACL,qBAAA,CAAA,CAAA;AAEJ,gBAAA;AACF,aAAA;AACF,QAAA,CAAA;AAWF;;;AAGC,IACD,MAAMM,kBAAAA,GAAqB,CACzBC,cAAAA,GAA+B,EAAE,EACjCC,cAAAA,GAA0C,EAAE,EAC5CC,gBAAAA,GAA4C,EAAE,GAAA;AAE9C,IAAA,MAAMC,YAAAA,GAAe;AAAE,QAAA,GAAGrB,cAAc;AAAE,QAAA,GAAGoB;AAAiB,KAAA;AAE9D,IAAA,MAAME,2BAAqD,EAAC;;;AAI5D,IAAA,IAAIC,OAAAA,CAAQC,GAAG,CAACC,QAAQ,KAAK,MAAA,EAAQ;AACnCH,QAAAA,wBAAAA,CAAyBI,iBAAiB,GAAG,KAAA;AAC7CJ,QAAAA,wBAAAA,CAAyBK,cAAc,GAAG,KAAA;AAC5C,IAAA;AAEA,IAAA,MAAMjB,QAAQkB,sBAAAA,CAAe;QAC3BV,cAAAA,EAAgB;AACdd,YAAAA,SAAAA,EAAWc,eAAed;AAC5B,SAAA;QACAD,OAAAA,EAASkB,YAAAA;AACTQ,QAAAA,QAAAA,EAAUN,OAAAA,CAAQC,GAAG,CAACC,QAAQ,KAAK,YAAA;AACnCK,QAAAA,UAAAA,EAAY,CAACC,oBAAAA,GAAyB;mBACjCA,oBAAAA,CAAqBT,wBAAAA,CAAAA;AACxBU,gBAAAA,8BAAAA;AACA/B,gBAAAA,YAAAA,CAAS6B,UAAU;mBAChBX,cAAAA,CAAec,GAAG,CAAC,CAACC,CAAAA,GAAMA,CAAAA,EAAAA;AAC9B,aAAA;QACDC,SAAAA,EAAW;YAAC7B,0BAAAA,CAA2Be,YAAAA;AAAc;AACvD,KAAA,CAAA;IAEA,OAAOX,KAAAA;AACT;AAEA;;;;;;;;;;;IAYA,MAAMsB,iCACJ,CAAC,EAAEI,QAAQ,EAAiB,GAC5B,CAAC5B,IAAAA,GACD,CAAC6B,MAAAA,GAAAA;;YAEC,IAAIC,kBAAAA,CAAWD,WAAWA,MAAAA,CAAOE,OAAO,EAAEC,MAAAA,KAAW,GAAA,IAAO,CAACC,oCAAAA,EAAAA,EAAyB;gBACpFL,QAAAA,CAASM,cAAAA,EAAAA,CAAAA;AACT,gBAAA,MAAMC,UAAAA,GAAWC,oBAAAA,EAAAA;AACjBC,gBAAAA,MAAAA,CAAOC,QAAQ,CAACC,IAAI,GAAG,CAAA,EAAGJ,UAAAA,CAAS,WAAW,CAAC;AAC/C,gBAAA;AACF,YAAA;AAEA,YAAA,OAAOnC,IAAAA,CAAK6B,MAAAA,CAAAA;AACd,QAAA,CAAA;;;;"}
@@ -1,6 +1,7 @@
1
1
  import { configureStore, isRejected, combineReducers } from '@reduxjs/toolkit';
2
2
  import { reducer, logout } from '../../reducer.mjs';
3
3
  import { adminApi } from '../../services/api.mjs';
4
+ import { triggerSessionExpired } from '../../utils/getFetchClient.mjs';
4
5
  import { getBasename } from '../utils/basename.mjs';
5
6
 
6
7
  /**
@@ -58,9 +59,20 @@ const injectReducerStoreEnhancer = (appReducers)=>(next)=>(...args)=>{
58
59
  });
59
60
  return store;
60
61
  };
61
- const rtkQueryUnauthorizedMiddleware = ({ dispatch })=>(next)=>(action)=>{
62
+ /**
63
+ * A 401 here means the fetch layer already tried to refresh the access token and
64
+ * the server refused: the session is over. Hand that to `AuthProvider`, which
65
+ * can warn about unsaved changes before clearing auth state. Reloading the page
66
+ * instead (as this used to) unmounts every form first, so the warning — and the
67
+ * user's edits — never make it to the screen. Falls back to the hard redirect
68
+ * when no provider is listening, so the user is never stranded on a dead page.
69
+ *
70
+ * `triggerSessionExpired` is idempotent per dead session (baseQuery often
71
+ * already called it for this 401). The `!` check is only the no-listener
72
+ * fallback; it must not run logout a second time.
73
+ */ const rtkQueryUnauthorizedMiddleware = ({ dispatch })=>(next)=>(action)=>{
62
74
  // isRejectedWithValue Or isRejected
63
- if (isRejected(action) && action.payload?.status === 401) {
75
+ if (isRejected(action) && action.payload?.status === 401 && !triggerSessionExpired()) {
64
76
  dispatch(logout());
65
77
  const basename = getBasename();
66
78
  window.location.href = `${basename}/auth/login`;
@@ -1 +1 @@
1
- {"version":3,"file":"configure.mjs","sources":["../../../../../../admin/src/core/store/configure.ts"],"sourcesContent":["import {\n configureStore,\n StoreEnhancer,\n Middleware,\n Reducer,\n combineReducers,\n MiddlewareAPI,\n isRejected,\n} from '@reduxjs/toolkit';\n\nimport { reducer as appReducer, AppState, logout } from '../../reducer';\nimport { adminApi } from '../../services/api';\nimport { getBasename } from '../utils/basename';\n\n/**\n * @description Static reducers are ones we know, they live in the admin package.\n */\nconst staticReducers = {\n [adminApi.reducerPath]: adminApi.reducer,\n admin_app: appReducer,\n} as const;\n\nconst injectReducerStoreEnhancer: (appReducers: Record<string, Reducer>) => StoreEnhancer =\n (appReducers) =>\n (next) =>\n (...args) => {\n const store = next(...args);\n\n const asyncReducers: Record<string, Reducer> = {};\n\n return {\n ...store,\n asyncReducers,\n injectReducer: (key: string, asyncReducer: Reducer) => {\n asyncReducers[key] = asyncReducer;\n store.replaceReducer(\n // @ts-expect-error we dynamically add reducers which makes the types uncomfortable.\n combineReducers({\n ...appReducers,\n ...asyncReducers,\n })\n );\n },\n };\n };\n\ntype PreloadState = Partial<{\n admin_app: AppState;\n}>;\n\ntype DefaultMiddlewareOptions = {\n serializableCheck?: boolean;\n immutableCheck?: boolean;\n};\n\n/**\n * @description This is the main store configuration function, injected Reducers use our legacy app.addReducer API,\n * which we're trying to phase out. App Middlewares could potentially be improved...?\n */\nconst configureStoreImpl = (\n preloadedState: PreloadState = {},\n appMiddlewares: Array<() => Middleware> = [],\n injectedReducers: Record<string, Reducer> = {}\n) => {\n const coreReducers = { ...staticReducers, ...injectedReducers } as const;\n\n const defaultMiddlewareOptions: DefaultMiddlewareOptions = {};\n\n // These are already disabled in 'production' env but we also need to disable it in test environments\n // However, we want to leave them on for development so any issues can still be caught\n if (process.env.NODE_ENV === 'test') {\n defaultMiddlewareOptions.serializableCheck = false;\n defaultMiddlewareOptions.immutableCheck = false;\n }\n\n const store = configureStore({\n preloadedState: {\n admin_app: preloadedState.admin_app,\n },\n reducer: coreReducers,\n devTools: process.env.NODE_ENV !== 'production',\n middleware: (getDefaultMiddleware) => [\n ...getDefaultMiddleware(defaultMiddlewareOptions),\n rtkQueryUnauthorizedMiddleware,\n adminApi.middleware,\n ...appMiddlewares.map((m) => m()),\n ],\n enhancers: [injectReducerStoreEnhancer(coreReducers)],\n });\n\n return store;\n};\n\nconst rtkQueryUnauthorizedMiddleware: Middleware =\n ({ dispatch }: MiddlewareAPI) =>\n (next) =>\n (action) => {\n // isRejectedWithValue Or isRejected\n if (isRejected(action) && action.payload?.status === 401) {\n dispatch(logout());\n const basename = getBasename();\n window.location.href = `${basename}/auth/login`;\n return;\n }\n\n return next(action);\n };\n\ntype Store = ReturnType<typeof configureStoreImpl> & {\n asyncReducers: Record<string, Reducer>;\n injectReducer: (key: string, asyncReducer: Reducer) => void;\n};\n\ntype RootState = ReturnType<Store['getState']>;\n\ntype Dispatch = Store['dispatch'];\n\nexport { configureStoreImpl as configureStore };\nexport type { RootState, Dispatch, AppState, Store, PreloadState };\n"],"names":["staticReducers","adminApi","reducerPath","reducer","admin_app","appReducer","injectReducerStoreEnhancer","appReducers","next","args","store","asyncReducers","injectReducer","key","asyncReducer","replaceReducer","combineReducers","configureStoreImpl","preloadedState","appMiddlewares","injectedReducers","coreReducers","defaultMiddlewareOptions","process","env","NODE_ENV","serializableCheck","immutableCheck","configureStore","devTools","middleware","getDefaultMiddleware","rtkQueryUnauthorizedMiddleware","map","m","enhancers","dispatch","action","isRejected","payload","status","logout","basename","getBasename","window","location","href"],"mappings":";;;;;AAcA;;AAEC,IACD,MAAMA,cAAAA,GAAiB;AACrB,IAAA,CAACC,QAAAA,CAASC,WAAW,GAAGD,SAASE,OAAO;IACxCC,SAAAA,EAAWC;AACb,CAAA;AAEA,MAAMC,6BACJ,CAACC,WAAAA,GACD,CAACC,IAAAA,GACD,CAAC,GAAGC,IAAAA,GAAAA;AACF,YAAA,MAAMC,QAAQF,IAAAA,CAAAA,GAAQC,IAAAA,CAAAA;AAEtB,YAAA,MAAME,gBAAyC,EAAC;YAEhD,OAAO;AACL,gBAAA,GAAGD,KAAK;AACRC,gBAAAA,aAAAA;AACAC,gBAAAA,aAAAA,EAAe,CAACC,GAAAA,EAAaC,YAAAA,GAAAA;oBAC3BH,aAAa,CAACE,IAAI,GAAGC,YAAAA;oBACrBJ,KAAAA,CAAMK,cAAc;oBAElBC,eAAAA,CAAgB;AACd,wBAAA,GAAGT,WAAW;AACd,wBAAA,GAAGI;AACL,qBAAA,CAAA,CAAA;AAEJ,gBAAA;AACF,aAAA;AACF,QAAA,CAAA;AAWF;;;AAGC,IACD,MAAMM,kBAAAA,GAAqB,CACzBC,cAAAA,GAA+B,EAAE,EACjCC,cAAAA,GAA0C,EAAE,EAC5CC,gBAAAA,GAA4C,EAAE,GAAA;AAE9C,IAAA,MAAMC,YAAAA,GAAe;AAAE,QAAA,GAAGrB,cAAc;AAAE,QAAA,GAAGoB;AAAiB,KAAA;AAE9D,IAAA,MAAME,2BAAqD,EAAC;;;AAI5D,IAAA,IAAIC,OAAAA,CAAQC,GAAG,CAACC,QAAQ,KAAK,MAAA,EAAQ;AACnCH,QAAAA,wBAAAA,CAAyBI,iBAAiB,GAAG,KAAA;AAC7CJ,QAAAA,wBAAAA,CAAyBK,cAAc,GAAG,KAAA;AAC5C,IAAA;AAEA,IAAA,MAAMjB,QAAQkB,cAAAA,CAAe;QAC3BV,cAAAA,EAAgB;AACdd,YAAAA,SAAAA,EAAWc,eAAed;AAC5B,SAAA;QACAD,OAAAA,EAASkB,YAAAA;AACTQ,QAAAA,QAAAA,EAAUN,OAAAA,CAAQC,GAAG,CAACC,QAAQ,KAAK,YAAA;AACnCK,QAAAA,UAAAA,EAAY,CAACC,oBAAAA,GAAyB;mBACjCA,oBAAAA,CAAqBT,wBAAAA,CAAAA;AACxBU,gBAAAA,8BAAAA;AACA/B,gBAAAA,QAAAA,CAAS6B,UAAU;mBAChBX,cAAAA,CAAec,GAAG,CAAC,CAACC,CAAAA,GAAMA,CAAAA,EAAAA;AAC9B,aAAA;QACDC,SAAAA,EAAW;YAAC7B,0BAAAA,CAA2Be,YAAAA;AAAc;AACvD,KAAA,CAAA;IAEA,OAAOX,KAAAA;AACT;AAEA,MAAMsB,8BAAAA,GACJ,CAAC,EAAEI,QAAQ,EAAiB,GAC5B,CAAC5B,OACD,CAAC6B,MAAAA,GAAAA;;AAEC,YAAA,IAAIC,WAAWD,MAAAA,CAAAA,IAAWA,MAAAA,CAAOE,OAAO,EAAEC,WAAW,GAAA,EAAK;gBACxDJ,QAAAA,CAASK,MAAAA,EAAAA,CAAAA;AACT,gBAAA,MAAMC,QAAAA,GAAWC,WAAAA,EAAAA;AACjBC,gBAAAA,MAAAA,CAAOC,QAAQ,CAACC,IAAI,GAAG,CAAA,EAAGJ,QAAAA,CAAS,WAAW,CAAC;AAC/C,gBAAA;AACF,YAAA;AAEA,YAAA,OAAOlC,IAAAA,CAAK6B,MAAAA,CAAAA;AACd,QAAA,CAAA;;;;"}
1
+ {"version":3,"file":"configure.mjs","sources":["../../../../../../admin/src/core/store/configure.ts"],"sourcesContent":["import {\n configureStore,\n StoreEnhancer,\n Middleware,\n Reducer,\n combineReducers,\n MiddlewareAPI,\n isRejected,\n} from '@reduxjs/toolkit';\n\nimport { reducer as appReducer, AppState, logout } from '../../reducer';\nimport { adminApi } from '../../services/api';\nimport { triggerSessionExpired } from '../../utils/getFetchClient';\nimport { getBasename } from '../utils/basename';\n\n/**\n * @description Static reducers are ones we know, they live in the admin package.\n */\nconst staticReducers = {\n [adminApi.reducerPath]: adminApi.reducer,\n admin_app: appReducer,\n} as const;\n\nconst injectReducerStoreEnhancer: (appReducers: Record<string, Reducer>) => StoreEnhancer =\n (appReducers) =>\n (next) =>\n (...args) => {\n const store = next(...args);\n\n const asyncReducers: Record<string, Reducer> = {};\n\n return {\n ...store,\n asyncReducers,\n injectReducer: (key: string, asyncReducer: Reducer) => {\n asyncReducers[key] = asyncReducer;\n store.replaceReducer(\n // @ts-expect-error we dynamically add reducers which makes the types uncomfortable.\n combineReducers({\n ...appReducers,\n ...asyncReducers,\n })\n );\n },\n };\n };\n\ntype PreloadState = Partial<{\n admin_app: AppState;\n}>;\n\ntype DefaultMiddlewareOptions = {\n serializableCheck?: boolean;\n immutableCheck?: boolean;\n};\n\n/**\n * @description This is the main store configuration function, injected Reducers use our legacy app.addReducer API,\n * which we're trying to phase out. App Middlewares could potentially be improved...?\n */\nconst configureStoreImpl = (\n preloadedState: PreloadState = {},\n appMiddlewares: Array<() => Middleware> = [],\n injectedReducers: Record<string, Reducer> = {}\n) => {\n const coreReducers = { ...staticReducers, ...injectedReducers } as const;\n\n const defaultMiddlewareOptions: DefaultMiddlewareOptions = {};\n\n // These are already disabled in 'production' env but we also need to disable it in test environments\n // However, we want to leave them on for development so any issues can still be caught\n if (process.env.NODE_ENV === 'test') {\n defaultMiddlewareOptions.serializableCheck = false;\n defaultMiddlewareOptions.immutableCheck = false;\n }\n\n const store = configureStore({\n preloadedState: {\n admin_app: preloadedState.admin_app,\n },\n reducer: coreReducers,\n devTools: process.env.NODE_ENV !== 'production',\n middleware: (getDefaultMiddleware) => [\n ...getDefaultMiddleware(defaultMiddlewareOptions),\n rtkQueryUnauthorizedMiddleware,\n adminApi.middleware,\n ...appMiddlewares.map((m) => m()),\n ],\n enhancers: [injectReducerStoreEnhancer(coreReducers)],\n });\n\n return store;\n};\n\n/**\n * A 401 here means the fetch layer already tried to refresh the access token and\n * the server refused: the session is over. Hand that to `AuthProvider`, which\n * can warn about unsaved changes before clearing auth state. Reloading the page\n * instead (as this used to) unmounts every form first, so the warning — and the\n * user's edits — never make it to the screen. Falls back to the hard redirect\n * when no provider is listening, so the user is never stranded on a dead page.\n *\n * `triggerSessionExpired` is idempotent per dead session (baseQuery often\n * already called it for this 401). The `!` check is only the no-listener\n * fallback; it must not run logout a second time.\n */\nconst rtkQueryUnauthorizedMiddleware: Middleware =\n ({ dispatch }: MiddlewareAPI) =>\n (next) =>\n (action) => {\n // isRejectedWithValue Or isRejected\n if (isRejected(action) && action.payload?.status === 401 && !triggerSessionExpired()) {\n dispatch(logout());\n const basename = getBasename();\n window.location.href = `${basename}/auth/login`;\n return;\n }\n\n return next(action);\n };\n\ntype Store = ReturnType<typeof configureStoreImpl> & {\n asyncReducers: Record<string, Reducer>;\n injectReducer: (key: string, asyncReducer: Reducer) => void;\n};\n\ntype RootState = ReturnType<Store['getState']>;\n\ntype Dispatch = Store['dispatch'];\n\nexport { configureStoreImpl as configureStore };\nexport type { RootState, Dispatch, AppState, Store, PreloadState };\n"],"names":["staticReducers","adminApi","reducerPath","reducer","admin_app","appReducer","injectReducerStoreEnhancer","appReducers","next","args","store","asyncReducers","injectReducer","key","asyncReducer","replaceReducer","combineReducers","configureStoreImpl","preloadedState","appMiddlewares","injectedReducers","coreReducers","defaultMiddlewareOptions","process","env","NODE_ENV","serializableCheck","immutableCheck","configureStore","devTools","middleware","getDefaultMiddleware","rtkQueryUnauthorizedMiddleware","map","m","enhancers","dispatch","action","isRejected","payload","status","triggerSessionExpired","logout","basename","getBasename","window","location","href"],"mappings":";;;;;;AAeA;;AAEC,IACD,MAAMA,cAAAA,GAAiB;AACrB,IAAA,CAACC,QAAAA,CAASC,WAAW,GAAGD,SAASE,OAAO;IACxCC,SAAAA,EAAWC;AACb,CAAA;AAEA,MAAMC,6BACJ,CAACC,WAAAA,GACD,CAACC,IAAAA,GACD,CAAC,GAAGC,IAAAA,GAAAA;AACF,YAAA,MAAMC,QAAQF,IAAAA,CAAAA,GAAQC,IAAAA,CAAAA;AAEtB,YAAA,MAAME,gBAAyC,EAAC;YAEhD,OAAO;AACL,gBAAA,GAAGD,KAAK;AACRC,gBAAAA,aAAAA;AACAC,gBAAAA,aAAAA,EAAe,CAACC,GAAAA,EAAaC,YAAAA,GAAAA;oBAC3BH,aAAa,CAACE,IAAI,GAAGC,YAAAA;oBACrBJ,KAAAA,CAAMK,cAAc;oBAElBC,eAAAA,CAAgB;AACd,wBAAA,GAAGT,WAAW;AACd,wBAAA,GAAGI;AACL,qBAAA,CAAA,CAAA;AAEJ,gBAAA;AACF,aAAA;AACF,QAAA,CAAA;AAWF;;;AAGC,IACD,MAAMM,kBAAAA,GAAqB,CACzBC,cAAAA,GAA+B,EAAE,EACjCC,cAAAA,GAA0C,EAAE,EAC5CC,gBAAAA,GAA4C,EAAE,GAAA;AAE9C,IAAA,MAAMC,YAAAA,GAAe;AAAE,QAAA,GAAGrB,cAAc;AAAE,QAAA,GAAGoB;AAAiB,KAAA;AAE9D,IAAA,MAAME,2BAAqD,EAAC;;;AAI5D,IAAA,IAAIC,OAAAA,CAAQC,GAAG,CAACC,QAAQ,KAAK,MAAA,EAAQ;AACnCH,QAAAA,wBAAAA,CAAyBI,iBAAiB,GAAG,KAAA;AAC7CJ,QAAAA,wBAAAA,CAAyBK,cAAc,GAAG,KAAA;AAC5C,IAAA;AAEA,IAAA,MAAMjB,QAAQkB,cAAAA,CAAe;QAC3BV,cAAAA,EAAgB;AACdd,YAAAA,SAAAA,EAAWc,eAAed;AAC5B,SAAA;QACAD,OAAAA,EAASkB,YAAAA;AACTQ,QAAAA,QAAAA,EAAUN,OAAAA,CAAQC,GAAG,CAACC,QAAQ,KAAK,YAAA;AACnCK,QAAAA,UAAAA,EAAY,CAACC,oBAAAA,GAAyB;mBACjCA,oBAAAA,CAAqBT,wBAAAA,CAAAA;AACxBU,gBAAAA,8BAAAA;AACA/B,gBAAAA,QAAAA,CAAS6B,UAAU;mBAChBX,cAAAA,CAAec,GAAG,CAAC,CAACC,CAAAA,GAAMA,CAAAA,EAAAA;AAC9B,aAAA;QACDC,SAAAA,EAAW;YAAC7B,0BAAAA,CAA2Be,YAAAA;AAAc;AACvD,KAAA,CAAA;IAEA,OAAOX,KAAAA;AACT;AAEA;;;;;;;;;;;IAYA,MAAMsB,iCACJ,CAAC,EAAEI,QAAQ,EAAiB,GAC5B,CAAC5B,IAAAA,GACD,CAAC6B,MAAAA,GAAAA;;YAEC,IAAIC,UAAAA,CAAWD,WAAWA,MAAAA,CAAOE,OAAO,EAAEC,MAAAA,KAAW,GAAA,IAAO,CAACC,qBAAAA,EAAAA,EAAyB;gBACpFL,QAAAA,CAASM,MAAAA,EAAAA,CAAAA;AACT,gBAAA,MAAMC,QAAAA,GAAWC,WAAAA,EAAAA;AACjBC,gBAAAA,MAAAA,CAAOC,QAAQ,CAACC,IAAI,GAAG,CAAA,EAAGJ,QAAAA,CAAS,WAAW,CAAC;AAC/C,gBAAA;AACF,YAAA;AAEA,YAAA,OAAOnC,IAAAA,CAAK6B,MAAAA,CAAAA;AACd,QAAA,CAAA;;;;"}
@@ -1,5 +1,5 @@
1
1
  import { createSelector } from '@reduxjs/toolkit';
2
- import { useStore, useDispatch, useSelector } from 'react-redux';
2
+ import { useStore, useSelector, useDispatch } from 'react-redux';
3
3
 
4
4
  const useTypedDispatch = useDispatch;
5
5
  const useTypedStore = useStore;