@strapi-community/plugin-better-auth-dashboard 1.0.0-alpha.9 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,359 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const reactQuery = require("react-query");
6
+ const reactRouterDom = require("react-router-dom");
7
+ const index = require("./index-BHfYu0uA.js");
8
+ const designSystem = require("@strapi/design-system");
9
+ const admin = require("@strapi/strapi/admin");
10
+ const reactIntl = require("react-intl");
11
+ require("@strapi/icons");
12
+ const styled = require("styled-components");
13
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
14
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
15
+ const HEIGHT_TOP_NAVIGATION = "6.4rem";
16
+ const HEIGHT_TOP_NAVIGATION_MEDIUM = "5.6rem";
17
+ const WIDTH_SIDE_NAVIGATION = "23.2rem";
18
+ const MainSubNav = styled__default.default(designSystem.SubNav)`
19
+ width: 100%;
20
+ height: 100%;
21
+ overflow: hidden;
22
+ background-color: ${({ theme }) => theme.colors.neutral0};
23
+ display: flex;
24
+ flex-direction: column;
25
+ border-right: 0;
26
+ box-shadow: none;
27
+ position: relative;
28
+
29
+ ${({ theme }) => theme.breakpoints.medium} {
30
+ width: ${WIDTH_SIDE_NAVIGATION};
31
+ position: sticky;
32
+ top: 0;
33
+ border-right: 1px solid ${({ theme }) => theme.colors.neutral150};
34
+ overscroll-behavior: contain;
35
+ }
36
+ `;
37
+ const Main = ({ children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(MainSubNav, { ...props, children });
38
+ const StyledLink = styled__default.default(reactRouterDom.NavLink)`
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: space-between;
42
+ text-decoration: none;
43
+ height: 32px;
44
+ color: ${({ theme }) => theme.colors.neutral800};
45
+
46
+ &.active > div {
47
+ background-color: ${({ theme }) => theme.colors.primary100};
48
+ color: ${({ theme }) => theme.colors.primary700};
49
+ font-weight: 500;
50
+ }
51
+
52
+ &:hover.active > div {
53
+ background-color: ${({ theme }) => theme.colors.primary100};
54
+ color: ${({ theme }) => theme.colors.primary700};
55
+ font-weight: 500;
56
+ }
57
+
58
+ &:hover > div {
59
+ background-color: ${({ theme }) => theme.colors.neutral100};
60
+ }
61
+
62
+ &:focus-visible {
63
+ outline-offset: -2px;
64
+ }
65
+ `;
66
+ const Link = (props) => {
67
+ const { to, label, endAction, handleClick, ...rest } = props;
68
+ return /* @__PURE__ */ jsxRuntime.jsx(StyledLink, { to, ...rest, onClick: handleClick, children: /* @__PURE__ */ jsxRuntime.jsx(
69
+ designSystem.Box,
70
+ {
71
+ width: "100%",
72
+ paddingLeft: 3,
73
+ paddingRight: 3,
74
+ paddingTop: { initial: 1, large: 0 },
75
+ paddingBottom: { initial: 1, large: 0 },
76
+ borderRadius: 1,
77
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
78
+ designSystem.Flex,
79
+ {
80
+ justifyContent: "space-between",
81
+ width: "100%",
82
+ gap: { initial: 2, large: 1 },
83
+ children: [
84
+ /* @__PURE__ */ jsxRuntime.jsx(
85
+ designSystem.Typography,
86
+ {
87
+ tag: "div",
88
+ lineHeight: "32px",
89
+ width: { initial: "80dvw", medium: "100%" },
90
+ overflow: "hidden",
91
+ style: { textOverflow: "ellipsis", whiteSpace: "nowrap" },
92
+ children: label
93
+ }
94
+ ),
95
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, children: endAction })
96
+ ]
97
+ }
98
+ )
99
+ }
100
+ ) });
101
+ };
102
+ const StyledHeader = styled__default.default(designSystem.Flex)`
103
+ flex: 0 0 ${HEIGHT_TOP_NAVIGATION};
104
+ height: ${HEIGHT_TOP_NAVIGATION};
105
+
106
+ ${({ theme }) => theme.breakpoints.medium} {
107
+ flex: 0 0 ${HEIGHT_TOP_NAVIGATION_MEDIUM};
108
+ height: ${HEIGHT_TOP_NAVIGATION_MEDIUM};
109
+ }
110
+ `;
111
+ const Header = ({ label }) => {
112
+ return /* @__PURE__ */ jsxRuntime.jsx(
113
+ StyledHeader,
114
+ {
115
+ justifyContent: "space-between",
116
+ paddingLeft: 5,
117
+ paddingRight: 5,
118
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", tag: "h2", children: label })
119
+ }
120
+ );
121
+ };
122
+ const Sections = ({
123
+ children,
124
+ ...props
125
+ }) => {
126
+ return /* @__PURE__ */ jsxRuntime.jsx(
127
+ designSystem.Box,
128
+ {
129
+ paddingTop: { initial: 5, large: 4 },
130
+ paddingBottom: { initial: 5, large: 4 },
131
+ paddingLeft: { initial: 3, large: 0 },
132
+ paddingRight: { initial: 3, large: 0 },
133
+ maxWidth: { initial: "100%", medium: WIDTH_SIDE_NAVIGATION },
134
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { tag: "ul", gap: 6, direction: "column", alignItems: "stretch", ...props, children: children.map((child, index2) => {
135
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: child }, index2);
136
+ }) })
137
+ }
138
+ );
139
+ };
140
+ const Section = ({
141
+ label,
142
+ children,
143
+ badgeLabel
144
+ }) => {
145
+ const listId = react.useId();
146
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
147
+ /* @__PURE__ */ jsxRuntime.jsx(
148
+ designSystem.Box,
149
+ {
150
+ paddingLeft: {
151
+ initial: 3,
152
+ large: 5
153
+ },
154
+ paddingRight: {
155
+ initial: 3,
156
+ large: 5
157
+ },
158
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { position: "relative", justifyContent: "space-between", gap: 2, children: [
159
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: label }) }) }),
160
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: badgeLabel && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral600", children: badgeLabel }) })
161
+ ] })
162
+ }
163
+ ),
164
+ /* @__PURE__ */ jsxRuntime.jsx(
165
+ designSystem.Flex,
166
+ {
167
+ tag: "ol",
168
+ id: listId,
169
+ direction: "column",
170
+ gap: { initial: 2, large: "2px" },
171
+ alignItems: "stretch",
172
+ marginLeft: {
173
+ initial: 0,
174
+ large: 2
175
+ },
176
+ marginRight: {
177
+ initial: 0,
178
+ large: 2
179
+ },
180
+ children: children.map((child, index2) => {
181
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: child }, index2);
182
+ })
183
+ }
184
+ )
185
+ ] });
186
+ };
187
+ styled__default.default.button`
188
+ cursor: pointer;
189
+ width: 100%;
190
+ border: none;
191
+ padding: 0;
192
+ background: transparent;
193
+ display: flex;
194
+ align-items: center;
195
+ border-radius: ${({ theme }) => theme.borderRadius};
196
+ padding-left: ${({ theme }) => theme.spaces[3]};
197
+ padding-right: ${({ theme }) => theme.spaces[3]};
198
+ padding-top: ${({ theme }) => theme.spaces[2]};
199
+ padding-bottom: ${({ theme }) => theme.spaces[2]};
200
+
201
+ &:hover {
202
+ background-color: ${({ theme }) => theme.colors.neutral100};
203
+ }
204
+ `;
205
+ styled__default.default.li`
206
+ ${StyledLink} > div {
207
+ padding-left: 36px;
208
+ }
209
+ `;
210
+ styled__default.default(designSystem.Box)`
211
+ width: 100%;
212
+
213
+ ${MainSubNav} {
214
+ background-color: transparent;
215
+ border-right: none;
216
+ }
217
+
218
+ ${({ theme }) => theme.breakpoints.medium} {
219
+ overflow: hidden;
220
+
221
+ ${MainSubNav} {
222
+ top: 0;
223
+ }
224
+ }
225
+ `;
226
+ const Content = ({ children }) => {
227
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ScrollArea, { children });
228
+ };
229
+ const SubNav = {
230
+ Main,
231
+ Content,
232
+ Header,
233
+ Link,
234
+ Sections,
235
+ Section
236
+ };
237
+ const Nav = ({ isFullPage = false }) => {
238
+ const { formatMessage } = reactIntl.useIntl();
239
+ const queryClient = reactQuery.useQueryClient();
240
+ const checkUserHasPermissions = admin.useAuth(
241
+ "DashboardNav",
242
+ (state) => state.checkUserHasPermissions
243
+ );
244
+ const dashLinks = index.getDashLinks();
245
+ const [sections, setSections] = react.useState([]);
246
+ react.useEffect(() => {
247
+ let isCurrent = true;
248
+ const loadAuthorizedSections = async () => {
249
+ const authorizedSections = await Promise.all(
250
+ Object.values(dashLinks).map(async (section) => ({
251
+ ...section,
252
+ links: (await Promise.all(
253
+ section.links.map(async (link) => {
254
+ const permissions = link.permissions ?? [];
255
+ const [hasPermission, isAvailable] = await Promise.all([
256
+ permissions.length === 0 ? true : checkUserHasPermissions(permissions).then(
257
+ (authorizedPermissions) => authorizedPermissions.length > 0
258
+ ),
259
+ queryClient.fetchQuery(
260
+ index.getDashLinkAvailabilityQueryKey(link),
261
+ () => index.resolveDashLinkAvailability(link),
262
+ { staleTime: index.DASH_LINK_AVAILABILITY_STALE_TIME }
263
+ )
264
+ ]);
265
+ return hasPermission && isAvailable ? link : null;
266
+ })
267
+ )).filter((link) => link !== null)
268
+ }))
269
+ );
270
+ if (isCurrent) {
271
+ setSections(
272
+ authorizedSections.filter((section) => section.links.length > 0).sort(
273
+ (a, b) => (a.priority ?? 0) - (b.priority ?? 0) || a.id.localeCompare(b.id)
274
+ )
275
+ );
276
+ }
277
+ };
278
+ loadAuthorizedSections();
279
+ return () => {
280
+ isCurrent = false;
281
+ };
282
+ }, [checkUserHasPermissions, dashLinks, queryClient]);
283
+ return /* @__PURE__ */ jsxRuntime.jsxs(SubNav.Main, { children: [
284
+ !isFullPage && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
285
+ /* @__PURE__ */ jsxRuntime.jsx(SubNav.Header, { label: "Authentication" }),
286
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
287
+ ] }),
288
+ /* @__PURE__ */ jsxRuntime.jsxs(SubNav.Content, { children: [
289
+ isFullPage && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
290
+ /* @__PURE__ */ jsxRuntime.jsx(SubNav.Header, { label: "Authentication" }),
291
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
292
+ ] }),
293
+ /* @__PURE__ */ jsxRuntime.jsx(SubNav.Sections, { "data-testid": "main-nav", children: sections.map((section) => /* @__PURE__ */ jsxRuntime.jsx(
294
+ SubNav.Section,
295
+ {
296
+ label: formatMessage(section.intlLabel),
297
+ children: section.links.map((link) => {
298
+ return /* @__PURE__ */ jsxRuntime.jsx(
299
+ SubNav.Link,
300
+ {
301
+ to: `/plugins/${index.PLUGIN_ID}/${link.to.replace(/^\/+/, "")}`,
302
+ label: formatMessage(link.intlLabel),
303
+ "data-testid": `nav-${link.id}`,
304
+ children: formatMessage(link.intlLabel)
305
+ },
306
+ link.id
307
+ );
308
+ })
309
+ },
310
+ section.id
311
+ )) })
312
+ ] })
313
+ ] });
314
+ };
315
+ const RESPONSIVE_DEFAULT_SPACING = {
316
+ initial: 4,
317
+ medium: 6,
318
+ large: 10
319
+ };
320
+ const App = () => {
321
+ return /* @__PURE__ */ jsxRuntime.jsx(
322
+ "div",
323
+ {
324
+ "data-testid": "dashboard-root",
325
+ style: { display: "flex", flex: "1 1 auto", minHeight: "100vh" },
326
+ children: /* @__PURE__ */ jsxRuntime.jsxs(admin.Layouts.Root, { sideNav: /* @__PURE__ */ jsxRuntime.jsx(Nav, {}), children: [
327
+ /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Title, { children: "Authentication" }),
328
+ /* @__PURE__ */ jsxRuntime.jsx(
329
+ designSystem.Box,
330
+ {
331
+ display: { initial: "block", medium: "none" },
332
+ paddingLeft: RESPONSIVE_DEFAULT_SPACING,
333
+ paddingRight: RESPONSIVE_DEFAULT_SPACING,
334
+ paddingTop: RESPONSIVE_DEFAULT_SPACING,
335
+ children: /* @__PURE__ */ jsxRuntime.jsx(admin.BackButton, { fallback: "/plugins/better-auth-dashboard" })
336
+ }
337
+ ),
338
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Outlet, {})
339
+ ] })
340
+ }
341
+ );
342
+ };
343
+ function Root() {
344
+ const isDashboardIndex = reactRouterDom.useMatch({
345
+ path: `/plugins/${index.PLUGIN_ID}`,
346
+ end: true
347
+ });
348
+ const queryClient = react.useMemo(
349
+ () => new reactQuery.QueryClient({
350
+ defaultOptions: {
351
+ queries: { retry: 1, refetchOnWindowFocus: false, staleTime: 3e4 }
352
+ }
353
+ }),
354
+ []
355
+ );
356
+ return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClient, children: isDashboardIndex ? /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "overview", replace: true }) : /* @__PURE__ */ jsxRuntime.jsx(App, {}) });
357
+ }
358
+ exports.Root = Root;
359
+ exports.default = Root;