openvsx-webui-test 0.20.0-dev.5 → 0.20.2-dev.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 (136) hide show
  1. package/lib/components/scan-admin/scan-card/scan-card-expanded-content.d.ts +2 -1
  2. package/lib/components/scan-admin/scan-card/scan-card-expanded-content.d.ts.map +1 -1
  3. package/lib/components/scan-admin/scan-card/scan-card-expanded-content.js.map +1 -1
  4. package/lib/components/scan-admin/scan-card/scan-card-header.js +1 -1
  5. package/lib/components/scan-admin/scan-card/scan-card-header.js.map +1 -1
  6. package/lib/components/scan-admin/scan-card/utils.js +1 -1
  7. package/lib/components/scan-admin/scan-card/utils.js.map +1 -1
  8. package/lib/components/sidepanel/navigation-item.d.ts +0 -1
  9. package/lib/components/sidepanel/navigation-item.d.ts.map +1 -1
  10. package/lib/components/sidepanel/navigation-item.js +22 -8
  11. package/lib/components/sidepanel/navigation-item.js.map +1 -1
  12. package/lib/components/sidepanel/sidebar-context.d.ts +16 -0
  13. package/lib/components/sidepanel/sidebar-context.d.ts.map +1 -0
  14. package/lib/components/sidepanel/sidebar-context.js +15 -0
  15. package/lib/components/sidepanel/sidebar-context.js.map +1 -0
  16. package/lib/components/sidepanel/sidepanel.d.ts +4 -4
  17. package/lib/components/sidepanel/sidepanel.d.ts.map +1 -1
  18. package/lib/components/sidepanel/sidepanel.js +47 -10
  19. package/lib/components/sidepanel/sidepanel.js.map +1 -1
  20. package/lib/default/menu-content.d.ts +1 -1
  21. package/lib/default/menu-content.js +1 -1
  22. package/lib/default/menu-content.js.map +1 -1
  23. package/lib/extension-registry-service.d.ts +6 -0
  24. package/lib/extension-registry-service.d.ts.map +1 -1
  25. package/lib/extension-registry-service.js +17 -0
  26. package/lib/extension-registry-service.js.map +1 -1
  27. package/lib/hooks/use-local-storage.d.ts +23 -0
  28. package/lib/hooks/use-local-storage.d.ts.map +1 -0
  29. package/lib/hooks/use-local-storage.js +62 -0
  30. package/lib/hooks/use-local-storage.js.map +1 -0
  31. package/lib/main.d.ts.map +1 -1
  32. package/lib/main.js +5 -5
  33. package/lib/main.js.map +1 -1
  34. package/lib/other-pages.d.ts.map +1 -1
  35. package/lib/other-pages.js +7 -7
  36. package/lib/other-pages.js.map +1 -1
  37. package/lib/pages/admin-dashboard/{admin-routes.d.ts → admin-dashboard-routes.d.ts} +6 -9
  38. package/lib/pages/admin-dashboard/admin-dashboard-routes.d.ts.map +1 -0
  39. package/lib/pages/admin-dashboard/{admin-routes.js → admin-dashboard-routes.js} +6 -9
  40. package/lib/pages/admin-dashboard/admin-dashboard-routes.js.map +1 -0
  41. package/lib/pages/admin-dashboard/admin-dashboard.d.ts.map +1 -1
  42. package/lib/pages/admin-dashboard/admin-dashboard.js +45 -101
  43. package/lib/pages/admin-dashboard/admin-dashboard.js.map +1 -1
  44. package/lib/pages/admin-dashboard/admin-header.d.ts +19 -0
  45. package/lib/pages/admin-dashboard/admin-header.d.ts.map +1 -0
  46. package/lib/pages/admin-dashboard/admin-header.js +16 -0
  47. package/lib/pages/admin-dashboard/admin-header.js.map +1 -0
  48. package/lib/pages/admin-dashboard/admin-sidepanel.d.ts +19 -0
  49. package/lib/pages/admin-dashboard/admin-sidepanel.d.ts.map +1 -0
  50. package/lib/pages/admin-dashboard/admin-sidepanel.js +17 -0
  51. package/lib/pages/admin-dashboard/admin-sidepanel.js.map +1 -0
  52. package/lib/pages/admin-dashboard/customers/customer-member-list.js +1 -1
  53. package/lib/pages/admin-dashboard/customers/customer-member-list.js.map +1 -1
  54. package/lib/pages/admin-dashboard/customers/customers.js +1 -1
  55. package/lib/pages/admin-dashboard/customers/customers.js.map +1 -1
  56. package/lib/pages/admin-dashboard/namespace-admin.d.ts.map +1 -1
  57. package/lib/pages/admin-dashboard/namespace-admin.js +4 -1
  58. package/lib/pages/admin-dashboard/namespace-admin.js.map +1 -1
  59. package/lib/pages/admin-dashboard/namespace-change-dialog.d.ts.map +1 -1
  60. package/lib/pages/admin-dashboard/namespace-change-dialog.js +6 -1
  61. package/lib/pages/admin-dashboard/namespace-change-dialog.js.map +1 -1
  62. package/lib/pages/admin-dashboard/namespace-delete-dialog.d.ts +23 -0
  63. package/lib/pages/admin-dashboard/namespace-delete-dialog.d.ts.map +1 -0
  64. package/lib/pages/admin-dashboard/namespace-delete-dialog.js +53 -0
  65. package/lib/pages/admin-dashboard/namespace-delete-dialog.js.map +1 -0
  66. package/lib/pages/admin-dashboard/nav-types.d.ts +27 -0
  67. package/lib/pages/admin-dashboard/nav-types.d.ts.map +1 -0
  68. package/lib/pages/admin-dashboard/nav-types.js +14 -0
  69. package/lib/pages/admin-dashboard/nav-types.js.map +1 -0
  70. package/lib/pages/admin-dashboard/publisher-admin.js +1 -1
  71. package/lib/pages/admin-dashboard/publisher-admin.js.map +1 -1
  72. package/lib/pages/admin-dashboard/scan-admin.d.ts.map +1 -1
  73. package/lib/pages/admin-dashboard/scan-admin.js +2 -5
  74. package/lib/pages/admin-dashboard/scan-admin.js.map +1 -1
  75. package/lib/pages/admin-dashboard/usage-stats/usage-stats.js +1 -1
  76. package/lib/pages/admin-dashboard/usage-stats/usage-stats.js.map +1 -1
  77. package/lib/pages/admin-dashboard/welcome.d.ts +5 -1
  78. package/lib/pages/admin-dashboard/welcome.d.ts.map +1 -1
  79. package/lib/pages/admin-dashboard/welcome.js +18 -16
  80. package/lib/pages/admin-dashboard/welcome.js.map +1 -1
  81. package/lib/pages/extension-detail/extension-detail-routes.d.ts +0 -1
  82. package/lib/pages/extension-detail/extension-detail-routes.d.ts.map +1 -1
  83. package/lib/pages/extension-detail/extension-detail-routes.js +2 -3
  84. package/lib/pages/extension-detail/extension-detail-routes.js.map +1 -1
  85. package/lib/pages/extension-detail/extension-detail.d.ts.map +1 -1
  86. package/lib/pages/extension-detail/extension-detail.js +120 -249
  87. package/lib/pages/extension-detail/extension-detail.js.map +1 -1
  88. package/lib/pages/extension-detail/use-extension-details.d.ts +23 -0
  89. package/lib/pages/extension-detail/use-extension-details.d.ts.map +1 -0
  90. package/lib/pages/extension-detail/use-extension-details.js +80 -0
  91. package/lib/pages/extension-detail/use-extension-details.js.map +1 -0
  92. package/lib/pages/user/avatar.js +1 -1
  93. package/lib/pages/user/avatar.js.map +1 -1
  94. package/lib/pages/user/user-setting-tabs.d.ts.map +1 -1
  95. package/lib/pages/user/user-setting-tabs.js +1 -1
  96. package/lib/pages/user/user-setting-tabs.js.map +1 -1
  97. package/lib/pages/user/user-settings-namespace-detail.d.ts +1 -0
  98. package/lib/pages/user/user-settings-namespace-detail.d.ts.map +1 -1
  99. package/lib/pages/user/user-settings-namespace-detail.js +19 -3
  100. package/lib/pages/user/user-settings-namespace-detail.js.map +1 -1
  101. package/lib/pages/user/user-settings-namespaces.js.map +1 -1
  102. package/package.json +3 -1
  103. package/src/components/scan-admin/scan-card/scan-card-expanded-content.tsx +4 -4
  104. package/src/components/scan-admin/scan-card/scan-card-header.tsx +1 -1
  105. package/src/components/scan-admin/scan-card/utils.ts +1 -1
  106. package/src/components/sidepanel/navigation-item.tsx +79 -23
  107. package/src/components/sidepanel/sidebar-context.tsx +17 -0
  108. package/src/components/sidepanel/sidepanel.tsx +57 -29
  109. package/src/default/menu-content.tsx +1 -1
  110. package/src/extension-registry-service.ts +18 -0
  111. package/src/hooks/use-local-storage.ts +67 -0
  112. package/src/main.tsx +11 -6
  113. package/src/other-pages.tsx +20 -16
  114. package/src/pages/admin-dashboard/{admin-routes.ts → admin-dashboard-routes.ts} +5 -8
  115. package/src/pages/admin-dashboard/admin-dashboard.tsx +71 -216
  116. package/src/pages/admin-dashboard/admin-header.tsx +59 -0
  117. package/src/pages/admin-dashboard/admin-sidepanel.tsx +59 -0
  118. package/src/pages/admin-dashboard/customers/customer-member-list.tsx +1 -1
  119. package/src/pages/admin-dashboard/customers/customers.tsx +1 -1
  120. package/src/pages/admin-dashboard/namespace-admin.tsx +5 -0
  121. package/src/pages/admin-dashboard/namespace-change-dialog.tsx +55 -46
  122. package/src/pages/admin-dashboard/namespace-delete-dialog.tsx +89 -0
  123. package/src/pages/admin-dashboard/nav-types.ts +31 -0
  124. package/src/pages/admin-dashboard/publisher-admin.tsx +1 -1
  125. package/src/pages/admin-dashboard/scan-admin.tsx +2 -5
  126. package/src/pages/admin-dashboard/usage-stats/usage-stats.tsx +1 -1
  127. package/src/pages/admin-dashboard/welcome.tsx +80 -48
  128. package/src/pages/extension-detail/extension-detail-routes.ts +2 -3
  129. package/src/pages/extension-detail/extension-detail.tsx +290 -409
  130. package/src/pages/extension-detail/use-extension-details.tsx +101 -0
  131. package/src/pages/user/avatar.tsx +1 -1
  132. package/src/pages/user/user-setting-tabs.tsx +3 -2
  133. package/src/pages/user/user-settings-namespace-detail.tsx +38 -5
  134. package/src/pages/user/user-settings-namespaces.tsx +1 -1
  135. package/lib/pages/admin-dashboard/admin-routes.d.ts.map +0 -1
  136. package/lib/pages/admin-dashboard/admin-routes.js.map +0 -1
@@ -8,38 +8,38 @@
8
8
  * SPDX-License-Identifier: EPL-2.0
9
9
  ********************************************************************************/
10
10
 
11
- import { ChangeEvent, FunctionComponent, useState, useContext, useEffect, useRef } from 'react';
12
- import {
13
- Button, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControlLabel, TextField
14
- } from '@mui/material';
15
- import { ButtonWithProgress } from '../../components/button-with-progress';
16
- import { Namespace, SuccessResult, isError } from '../../extension-registry-types';
17
- import { MainContext } from '../../context';
18
- import { InfoDialog } from '../../components/info-dialog';
11
+ import { ChangeEvent, FunctionComponent, useState, useContext, useEffect, useRef } from 'react';
12
+ import {
13
+ Button, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControlLabel, TextField
14
+ } from '@mui/material';
15
+ import { ButtonWithProgress } from '../../components/button-with-progress';
16
+ import { Namespace, SuccessResult, isError } from '../../extension-registry-types';
17
+ import { MainContext } from '../../context';
18
+ import { InfoDialog } from '../../components/info-dialog';
19
19
 
20
- export interface NamespaceChangeDialogProps {
21
- open: boolean;
22
- onClose: () => void;
23
- namespace: Namespace;
24
- setLoadingState: (loading: boolean) => void;
25
- }
20
+ export interface NamespaceChangeDialogProps {
21
+ open: boolean;
22
+ onClose: () => void;
23
+ namespace: Namespace;
24
+ setLoadingState: (loading: boolean) => void;
25
+ }
26
26
 
27
- export const NamespaceChangeDialog: FunctionComponent<NamespaceChangeDialogProps> = props => {
28
- const { open } = props;
29
- const { service, handleError } = useContext(MainContext);
30
- const [working, setWorking] = useState(false);
31
- const [newNamespace, setNewNamespace] = useState('');
32
- const [removeOldNamespace, setRemoveOldNamespace] = useState(false);
33
- const [mergeIfNewNamespaceAlreadyExists, setMergeIfNewNamespaceAlreadyExists] = useState(false);
34
- const [infoDialogIsOpen, setInfoDialogIsOpen] = useState(false);
35
- const [infoDialogMessage, setInfoDialogMessage] = useState('');
27
+ export const NamespaceChangeDialog: FunctionComponent<NamespaceChangeDialogProps> = props => {
28
+ const { open } = props;
29
+ const { service, handleError } = useContext(MainContext);
30
+ const [working, setWorking] = useState(false);
31
+ const [newNamespace, setNewNamespace] = useState('');
32
+ const [removeOldNamespace, setRemoveOldNamespace] = useState(false);
33
+ const [mergeIfNewNamespaceAlreadyExists, setMergeIfNewNamespaceAlreadyExists] = useState(false);
34
+ const [infoDialogIsOpen, setInfoDialogIsOpen] = useState(false);
35
+ const [infoDialogMessage, setInfoDialogMessage] = useState('');
36
36
 
37
- const abortController = useRef<AbortController>(new AbortController());
38
- useEffect(() => {
39
- return () => {
40
- abortController.current.abort();
41
- };
42
- }, []);
37
+ const abortController = useRef<AbortController>(new AbortController());
38
+ useEffect(() => {
39
+ return () => {
40
+ abortController.current.abort();
41
+ };
42
+ }, []);
43
43
 
44
44
  useEffect(() => {
45
45
  if (open) {
@@ -70,7 +70,12 @@
70
70
  setWorking(true);
71
71
  props.setLoadingState(true);
72
72
  const oldNamespace = props.namespace.name;
73
- const result = await service.admin.changeNamespace(abortController.current, { oldNamespace, newNamespace, removeOldNamespace, mergeIfNewNamespaceAlreadyExists });
73
+ const result = await service.admin.changeNamespace(abortController.current, {
74
+ oldNamespace,
75
+ newNamespace,
76
+ removeOldNamespace,
77
+ mergeIfNewNamespaceAlreadyExists
78
+ });
74
79
  if (isError(result)) {
75
80
  throw result;
76
81
  }
@@ -87,12 +92,12 @@
87
92
  }
88
93
  };
89
94
 
90
- return <>
95
+ return <>
91
96
  <Dialog onClose={onClose} open={open} aria-labelledby='form-dialog-title'>
92
97
  <DialogTitle id='form-dialog-title'>Change Namespace</DialogTitle>
93
98
  <DialogContent>
94
99
  <DialogContentText>
95
- Enter the new Namespace name.
100
+ Enter the new Namespace name.
96
101
  </DialogContentText>
97
102
  <TextField
98
103
  autoFocus
@@ -106,27 +111,31 @@
106
111
  }}
107
112
  />
108
113
  <FormControlLabel
109
- control={<Checkbox checked={removeOldNamespace} onChange={onRemoveOldNamespaceChange} name='remove-old-namespace' />}
110
- label={`Remove '${props.namespace.name}' namespace after namespace change`} />
114
+ control={<Checkbox checked={removeOldNamespace} onChange={onRemoveOldNamespaceChange}
115
+ name='remove-old-namespace'/>}
116
+ label={`Remove '${props.namespace.name}' namespace after namespace change`}/>
111
117
  <FormControlLabel
112
- control={<Checkbox checked={mergeIfNewNamespaceAlreadyExists} onChange={onMergeIfNewNamespaceAlreadyExistsChange} name='merge-change-namespace' />}
113
- label='Merge namespaces if new namespace already exists' />
114
- </DialogContent>
115
- <DialogActions>
116
- <Button
118
+ control={<Checkbox checked={mergeIfNewNamespaceAlreadyExists}
119
+ onChange={onMergeIfNewNamespaceAlreadyExistsChange}
120
+ name='merge-change-namespace'/>}
121
+ label='Merge namespaces if new namespace already exists'/>
122
+ </DialogContent>
123
+ <DialogActions>
124
+ <Button
117
125
  variant='contained'
118
126
  color='primary'
119
- onClick={onClose} >
127
+ onClick={onClose}>
120
128
  Cancel
121
129
  </Button>
122
130
  <ButtonWithProgress
123
131
  sx={{ ml: 1 }}
124
132
  working={working}
125
- onClick={handleChangeNamespace} >
133
+ onClick={handleChangeNamespace}>
126
134
  Change Namespace
127
135
  </ButtonWithProgress>
128
- </DialogActions>
129
- </Dialog>
130
- <InfoDialog infoMessage={infoDialogMessage} isInfoDialogOpen={infoDialogIsOpen} handleCloseDialog={onInfoDialogClose}/>
131
- </>;
132
- };
136
+ </DialogActions>
137
+ </Dialog>
138
+ <InfoDialog infoMessage={infoDialogMessage} isInfoDialogOpen={infoDialogIsOpen}
139
+ handleCloseDialog={onInfoDialogClose}/>
140
+ </>;
141
+ };
@@ -0,0 +1,89 @@
1
+ /******************************************************************************
2
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
3
+ *
4
+ * See the NOTICE file(s) distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ *
7
+ * This program and the accompanying materials are made available under the
8
+ * terms of the Eclipse Public License 2.0 which is available at
9
+ * https://www.eclipse.org/legal/epl-2.0.
10
+ *
11
+ * SPDX-License-Identifier: EPL-2.0
12
+ *****************************************************************************/
13
+
14
+ import { FunctionComponent, useState, useContext, useEffect, useRef } from 'react';
15
+ import {
16
+ Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle
17
+ } from '@mui/material';
18
+ import { ButtonWithProgress } from '../../components/button-with-progress';
19
+ import { Namespace, isError } from '../../extension-registry-types';
20
+ import { MainContext } from '../../context';
21
+
22
+ export interface NamespaceDeleteDialogProps {
23
+ open: boolean;
24
+ onClose: () => void;
25
+ onDelete: () => void;
26
+ namespace: Namespace;
27
+ setLoadingState: (loading: boolean) => void;
28
+ }
29
+
30
+ export const NamespaceDeleteDialog: FunctionComponent<NamespaceDeleteDialogProps> = props => {
31
+ const { open, onClose, onDelete, namespace } = props;
32
+ const { service, handleError } = useContext(MainContext);
33
+ const [working, setWorking] = useState(false);
34
+
35
+ const abortController = useRef<AbortController>(new AbortController());
36
+ useEffect(() => {
37
+ return () => {
38
+ abortController.current.abort();
39
+ };
40
+ }, []);
41
+
42
+ const handleDeleteNamespace = async () => {
43
+ try {
44
+ if (!props.namespace) {
45
+ return;
46
+ }
47
+ setWorking(true);
48
+ props.setLoadingState(true);
49
+ const name = props.namespace.name;
50
+ const result = await service.admin.deleteNamespace(abortController.current, { name });
51
+ if (isError(result)) {
52
+ throw result;
53
+ }
54
+
55
+ props.setLoadingState(false);
56
+ setWorking(false);
57
+ onDelete();
58
+ } catch (err) {
59
+ props.setLoadingState(false);
60
+ setWorking(false);
61
+ handleError(err);
62
+ }
63
+ };
64
+
65
+ return <>
66
+ <Dialog onClose={onClose} open={open} aria-labelledby='form-dialog-title'>
67
+ <DialogTitle id='form-dialog-title'>Delete Namespace</DialogTitle>
68
+ <DialogContent>
69
+ <DialogContentText>
70
+ Are you sure you want to delete the namespace <strong>{namespace.name}</strong>?
71
+ </DialogContentText>
72
+ </DialogContent>
73
+ <DialogActions>
74
+ <Button
75
+ variant='contained'
76
+ color='primary'
77
+ onClick={onClose}>
78
+ Cancel
79
+ </Button>
80
+ <ButtonWithProgress
81
+ sx={{ ml: 1 }}
82
+ working={working}
83
+ onClick={handleDeleteNamespace}>
84
+ Delete Namespace
85
+ </ButtonWithProgress>
86
+ </DialogActions>
87
+ </Dialog>
88
+ </>;
89
+ };
@@ -0,0 +1,31 @@
1
+ /******************************************************************************
2
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
3
+ *
4
+ * See the NOTICE file(s) distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ *
7
+ * This program and the accompanying materials are made available under the
8
+ * terms of the Eclipse Public License 2.0 which is available at
9
+ * https://www.eclipse.org/legal/epl-2.0.
10
+ *
11
+ * SPDX-License-Identifier: EPL-2.0
12
+ *****************************************************************************/
13
+
14
+ import { ReactNode } from 'react';
15
+
16
+ export interface RouteEntry {
17
+ path: string;
18
+ name: string;
19
+ icon: ReactNode;
20
+ description?: string;
21
+ }
22
+
23
+ export interface NavGroup {
24
+ name: string;
25
+ icon: ReactNode;
26
+ children: RouteEntry[];
27
+ }
28
+
29
+ export type NavEntry = RouteEntry | NavGroup;
30
+
31
+ export const isNavGroup = (entry: NavEntry): entry is NavGroup => 'children' in entry;
@@ -16,7 +16,7 @@ import { MainContext } from '../../context';
16
16
  import { StyledInput } from './namespace-input';
17
17
  import { SearchListContainer } from './search-list-container';
18
18
  import { PublisherDetails } from './publisher-details';
19
- import { AdminDashboardRoutes } from './admin-routes';
19
+ import { AdminDashboardRoutes } from './admin-dashboard-routes';
20
20
 
21
21
  // eslint-disable-next-line react-refresh/only-export-components
22
22
  export const UpdateContext = createContext({ handleUpdate: () => { } });
@@ -45,14 +45,11 @@ const ScanAdminContent: FunctionComponent = () => {
45
45
 
46
46
  return (
47
47
  <Box sx={{
48
- width: 'min(1500px, 100vw - 280px)',
49
- maxWidth: 'none',
48
+ width: '100%',
49
+ maxWidth: 1500,
50
50
  mx: 'auto',
51
51
  px: 2,
52
52
  pb: 3,
53
- position: 'relative',
54
- left: '50%',
55
- transform: 'translateX(-50%)',
56
53
  }}>
57
54
  <Typography variant='h5' gutterBottom sx={{ mb: 2 }}>
58
55
  Extension Scans
@@ -17,7 +17,7 @@ import { useParams, useNavigate } from "react-router-dom";
17
17
  import { MainContext } from "../../../context";
18
18
  import type { Customer } from "../../../extension-registry-types";
19
19
  import { handleError } from "../../../utils";
20
- import { AdminDashboardRoutes } from "../admin-routes";
20
+ import { AdminDashboardRoutes } from "../admin-dashboard-routes";
21
21
  import { SearchListContainer } from "../search-list-container";
22
22
  import { CustomerSearch } from "./usage-stats-search";
23
23
  import { UsageStatsChart } from "../../../components/rate-limiting/usage-stats/usage-stats-chart";
@@ -9,54 +9,86 @@
9
9
  ********************************************************************************/
10
10
 
11
11
  import { FunctionComponent } from 'react';
12
- import { Typography, Grid, Paper } from '@mui/material';
13
- import { styled, Theme } from '@mui/material/styles';
14
- import { Link } from 'react-router-dom';
15
- import { AdminDashboardRoutes } from './admin-routes';
16
-
17
- export const Welcome: FunctionComponent = props => {
18
- return <Grid container direction='column' spacing={2} sx={{ height: '100%' }}>
19
- <Grid item container direction='column' alignItems='center' justifyContent='flex-end'>
20
- <Paper elevation={3} sx={{ p: 4 }}>
21
- <Typography sx={{ mb: 2 }} align='center' variant='h5'>Welcome to the Admin Dashboard!</Typography>
22
- <Typography align='center'>You can switch pages in the sidepanel menu on the left side.</Typography>
23
- <Typography align='center'>
24
- Choose between administration for
25
- </Typography>
26
- <Grid container justifyContent='center' alignItems='center' sx={{ mt: 2 }}>
27
- <WelcomeLinkItem route={AdminDashboardRoutes.NAMESPACE_ADMIN} label='Namespaces' description='Manage user roles, create new namespaces' />
28
- <WelcomeLinkItem route={AdminDashboardRoutes.EXTENSION_ADMIN} label='Extensions' description='Search for extensions and remove certain versions' />
29
- <WelcomeLinkItem route={AdminDashboardRoutes.PUBLISHER_ADMIN} label='Publishers' description='Search for publishers and revoke their contributions' />
30
- <WelcomeLinkItem route={AdminDashboardRoutes.SCANS_ADMIN} label='Scans' description='View security scan results and manage quarantined extensions' />
31
- <WelcomeLinkItem route={AdminDashboardRoutes.TIERS} label='Tiers' description='Manage rate-limit tiers' />
32
- <WelcomeLinkItem route={AdminDashboardRoutes.CUSTOMERS} label='Customers' description='Manage rate-limit customers' />
33
- <WelcomeLinkItem route={AdminDashboardRoutes.USAGE_STATS} label='Usage Stats' description='Show usage stats for customers' />
34
- <WelcomeLinkItem route={AdminDashboardRoutes.LOGS} label='Logs' description='Browse admin logs' />
35
- </Grid>
36
- </Paper>
37
- </Grid>
38
- </Grid>;
12
+ import { Box, Card, CardActionArea, CardContent, Divider, Grid, Typography } from '@mui/material';
13
+ import { useNavigate } from 'react-router-dom';
14
+ import { isNavGroup, NavEntry, RouteEntry } from './nav-types';
15
+
16
+ interface NavSection {
17
+ groupName?: string;
18
+ entries: RouteEntry[];
19
+ }
20
+
21
+ /** Preserve the original group structure: ungrouped items come first as a section without a title. */
22
+ function buildSections(items: NavEntry[]): NavSection[] {
23
+ const ungrouped: RouteEntry[] = items.filter((e): e is RouteEntry => !isNavGroup(e));
24
+ const groups: NavSection[] = items
25
+ .filter(isNavGroup)
26
+ .map(group => ({ groupName: group.name, entries: group.children }));
27
+
28
+ return ungrouped.length > 0 ? [{ entries: ungrouped }, ...groups] : groups;
29
+ }
30
+
31
+ const NavCard: FunctionComponent<{ entry: RouteEntry }> = ({ entry }) => {
32
+ const navigate = useNavigate();
33
+ return (
34
+ <Card variant='outlined' sx={{ height: '100%' }}>
35
+ <CardActionArea
36
+ onClick={() => navigate(entry.path)}
37
+ sx={{ height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', py: 3, px: 2 }}
38
+ >
39
+ <Box sx={{ fontSize: 40, color: 'primary.main', mb: 1, display: 'flex' }}>
40
+ {entry.icon}
41
+ </Box>
42
+ <CardContent sx={{ textAlign: 'center', p: 1 }}>
43
+ <Typography variant='h6' gutterBottom>{entry.name}</Typography>
44
+ {entry.description && (
45
+ <Typography variant='body2' color='text.secondary'>
46
+ {entry.description}
47
+ </Typography>
48
+ )}
49
+ </CardContent>
50
+ </CardActionArea>
51
+ </Card>
52
+ );
39
53
  };
40
54
 
41
- const StyledLink = styled(Link)(({ theme }: { theme: Theme }) => ({
42
- color: theme.palette.secondary.main,
43
- textDecoration: 'none',
44
- '&:hover': {
45
- textDecoration: 'underline'
46
- }
47
- }));
48
-
49
- const WelcomeLinkItem: FunctionComponent<{ route: string, label: string, description: string }> = props => {
50
- return <Grid container item xs={8} sx={{ mb: 2 }}>
51
- <Grid container alignItems='center' item xs={12} md={4}>
52
- <Typography>
53
- <StyledLink to={props.route}>
54
- {props.label}
55
- </StyledLink>
55
+ export interface WelcomeProps {
56
+ items: NavEntry[];
57
+ }
58
+
59
+ export const Welcome: FunctionComponent<WelcomeProps> = ({ items }) => {
60
+ const sections = buildSections(items);
61
+
62
+ return (
63
+ <Box sx={{ py: 4, px: 2 }}>
64
+ <Typography variant='h5' gutterBottom>
65
+ Welcome to the Admin Dashboard
56
66
  </Typography>
57
- </Grid>
58
- <Grid item xs={12} md={8}>
59
- <Typography variant='body1' style={{ lineHeight: 1.5 }}>{props.description}</Typography>
60
- </Grid>
61
- </Grid>;
62
- };
67
+ <Typography variant='body1' color='text.secondary' sx={{ mb: 4 }}>
68
+ Select a section below to get started
69
+ </Typography>
70
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
71
+ {sections.map((section, i) => (
72
+ <Box key={section.groupName ?? '__root__'}>
73
+ {section.groupName && (
74
+ <>
75
+ <Typography variant='overline' color='text.secondary' sx={{ mb: 1, display: 'block' }}>
76
+ {section.groupName}
77
+ </Typography>
78
+ <Divider sx={{ mb: 2 }} />
79
+ </>
80
+ )}
81
+ {!section.groupName && i > 0 && <Divider sx={{ mb: 2 }} />}
82
+ <Grid container spacing={3}>
83
+ {section.entries.map(entry => (
84
+ <Grid item key={entry.path} xs={12} sm={6} md={4} lg={3}>
85
+ <NavCard entry={entry} />
86
+ </Grid>
87
+ ))}
88
+ </Grid>
89
+ </Box>
90
+ ))}
91
+ </Box>
92
+ </Box>
93
+ );
94
+ };
@@ -18,12 +18,11 @@ export namespace ExtensionDetailRoutes {
18
18
  export const NAMESPACE = ':namespace';
19
19
  export const NAME = ':name';
20
20
  export const TARGET = `:target(${getTargetPlatforms().join('|')})`;
21
- export const VERSION = ':version?';
22
21
  }
23
22
 
24
23
  export const ROOT = 'extension';
25
- export const MAIN = createRoute([ROOT, Parameters.NAMESPACE, Parameters.NAME, Parameters.VERSION]);
26
- export const MAIN_TARGET = createRoute([ROOT, Parameters.NAMESPACE, Parameters.NAME, Parameters.TARGET, Parameters.VERSION]);
24
+ export const MAIN = createRoute([ROOT, Parameters.NAMESPACE, Parameters.NAME, '*']);
25
+ export const MAIN_TARGET = createRoute([ROOT, Parameters.NAMESPACE, Parameters.NAME, Parameters.TARGET, '*']);
27
26
  export const LATEST = createRoute([ROOT, Parameters.NAMESPACE, Parameters.NAME]);
28
27
  export const LATEST_TARGET = createRoute([ROOT, Parameters.NAMESPACE, Parameters.NAME, Parameters.TARGET]);
29
28
  export const PRE_RELEASE = createRoute([ROOT, Parameters.NAMESPACE, Parameters.NAME, 'pre-release']);