openvsx-webui-test 0.20.1-rc.0 → 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 (78) hide show
  1. package/lib/components/sidepanel/navigation-item.d.ts +0 -1
  2. package/lib/components/sidepanel/navigation-item.d.ts.map +1 -1
  3. package/lib/components/sidepanel/navigation-item.js +22 -8
  4. package/lib/components/sidepanel/navigation-item.js.map +1 -1
  5. package/lib/components/sidepanel/sidebar-context.d.ts +16 -0
  6. package/lib/components/sidepanel/sidebar-context.d.ts.map +1 -0
  7. package/lib/components/sidepanel/sidebar-context.js +15 -0
  8. package/lib/components/sidepanel/sidebar-context.js.map +1 -0
  9. package/lib/components/sidepanel/sidepanel.d.ts +4 -4
  10. package/lib/components/sidepanel/sidepanel.d.ts.map +1 -1
  11. package/lib/components/sidepanel/sidepanel.js +47 -10
  12. package/lib/components/sidepanel/sidepanel.js.map +1 -1
  13. package/lib/extension-registry-service.d.ts +6 -0
  14. package/lib/extension-registry-service.d.ts.map +1 -1
  15. package/lib/extension-registry-service.js +17 -0
  16. package/lib/extension-registry-service.js.map +1 -1
  17. package/lib/hooks/use-local-storage.d.ts +23 -0
  18. package/lib/hooks/use-local-storage.d.ts.map +1 -0
  19. package/lib/hooks/use-local-storage.js +62 -0
  20. package/lib/hooks/use-local-storage.js.map +1 -0
  21. package/lib/pages/admin-dashboard/admin-dashboard.d.ts.map +1 -1
  22. package/lib/pages/admin-dashboard/admin-dashboard.js +39 -95
  23. package/lib/pages/admin-dashboard/admin-dashboard.js.map +1 -1
  24. package/lib/pages/admin-dashboard/admin-header.d.ts +19 -0
  25. package/lib/pages/admin-dashboard/admin-header.d.ts.map +1 -0
  26. package/lib/pages/admin-dashboard/admin-header.js +16 -0
  27. package/lib/pages/admin-dashboard/admin-header.js.map +1 -0
  28. package/lib/pages/admin-dashboard/admin-sidepanel.d.ts +19 -0
  29. package/lib/pages/admin-dashboard/admin-sidepanel.d.ts.map +1 -0
  30. package/lib/pages/admin-dashboard/admin-sidepanel.js +17 -0
  31. package/lib/pages/admin-dashboard/admin-sidepanel.js.map +1 -0
  32. package/lib/pages/admin-dashboard/namespace-admin.d.ts.map +1 -1
  33. package/lib/pages/admin-dashboard/namespace-admin.js +4 -1
  34. package/lib/pages/admin-dashboard/namespace-admin.js.map +1 -1
  35. package/lib/pages/admin-dashboard/namespace-change-dialog.d.ts.map +1 -1
  36. package/lib/pages/admin-dashboard/namespace-change-dialog.js +6 -1
  37. package/lib/pages/admin-dashboard/namespace-change-dialog.js.map +1 -1
  38. package/lib/pages/admin-dashboard/namespace-delete-dialog.d.ts +23 -0
  39. package/lib/pages/admin-dashboard/namespace-delete-dialog.d.ts.map +1 -0
  40. package/lib/pages/admin-dashboard/namespace-delete-dialog.js +53 -0
  41. package/lib/pages/admin-dashboard/namespace-delete-dialog.js.map +1 -0
  42. package/lib/pages/admin-dashboard/nav-types.d.ts +27 -0
  43. package/lib/pages/admin-dashboard/nav-types.d.ts.map +1 -0
  44. package/lib/pages/admin-dashboard/nav-types.js +14 -0
  45. package/lib/pages/admin-dashboard/nav-types.js.map +1 -0
  46. package/lib/pages/admin-dashboard/scan-admin.d.ts.map +1 -1
  47. package/lib/pages/admin-dashboard/scan-admin.js +2 -5
  48. package/lib/pages/admin-dashboard/scan-admin.js.map +1 -1
  49. package/lib/pages/admin-dashboard/welcome.d.ts +5 -1
  50. package/lib/pages/admin-dashboard/welcome.d.ts.map +1 -1
  51. package/lib/pages/admin-dashboard/welcome.js +18 -16
  52. package/lib/pages/admin-dashboard/welcome.js.map +1 -1
  53. package/lib/pages/user/user-setting-tabs.d.ts.map +1 -1
  54. package/lib/pages/user/user-setting-tabs.js +1 -1
  55. package/lib/pages/user/user-setting-tabs.js.map +1 -1
  56. package/lib/pages/user/user-settings-namespace-detail.d.ts +1 -0
  57. package/lib/pages/user/user-settings-namespace-detail.d.ts.map +1 -1
  58. package/lib/pages/user/user-settings-namespace-detail.js +18 -2
  59. package/lib/pages/user/user-settings-namespace-detail.js.map +1 -1
  60. package/lib/pages/user/user-settings-namespaces.js.map +1 -1
  61. package/package.json +1 -1
  62. package/src/components/sidepanel/navigation-item.tsx +79 -23
  63. package/src/components/sidepanel/sidebar-context.tsx +17 -0
  64. package/src/components/sidepanel/sidepanel.tsx +57 -29
  65. package/src/extension-registry-service.ts +18 -0
  66. package/src/hooks/use-local-storage.ts +67 -0
  67. package/src/pages/admin-dashboard/admin-dashboard.tsx +48 -197
  68. package/src/pages/admin-dashboard/admin-header.tsx +59 -0
  69. package/src/pages/admin-dashboard/admin-sidepanel.tsx +59 -0
  70. package/src/pages/admin-dashboard/namespace-admin.tsx +5 -0
  71. package/src/pages/admin-dashboard/namespace-change-dialog.tsx +55 -46
  72. package/src/pages/admin-dashboard/namespace-delete-dialog.tsx +89 -0
  73. package/src/pages/admin-dashboard/nav-types.ts +31 -0
  74. package/src/pages/admin-dashboard/scan-admin.tsx +2 -5
  75. package/src/pages/admin-dashboard/welcome.tsx +80 -48
  76. package/src/pages/user/user-setting-tabs.tsx +3 -2
  77. package/src/pages/user/user-settings-namespace-detail.tsx +37 -4
  78. package/src/pages/user/user-settings-namespaces.tsx +1 -1
@@ -8,39 +8,32 @@
8
8
  * SPDX-License-Identifier: EPL-2.0
9
9
  ********************************************************************************/
10
10
 
11
- import { FunctionComponent, ReactNode, useContext, useState, lazy, Suspense } from 'react';
11
+ import { FunctionComponent, ReactNode, useContext, lazy, Suspense } from 'react';
12
12
  import {
13
13
  Box,
14
14
  Container,
15
15
  CssBaseline,
16
16
  Typography,
17
17
  IconButton,
18
- Breadcrumbs,
19
- LinkProps,
20
- Link,
21
- Toolbar
22
18
  } from '@mui/material';
23
- import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
24
- import { styled } from "@mui/material/styles";
25
- import { Link as RouterLink, Route, Routes, useNavigate, useLocation } from 'react-router-dom';
19
+ import { styled } from '@mui/material/styles';
20
+ import { Route, Routes, useNavigate } from 'react-router-dom';
26
21
  import AccountBoxIcon from '@mui/icons-material/AccountBox';
27
22
  import AssignmentIndIcon from '@mui/icons-material/AssignmentInd';
28
23
  import BarChartIcon from '@mui/icons-material/BarChart';
29
24
  import ExtensionSharpIcon from '@mui/icons-material/ExtensionSharp';
30
- import HighlightOffIcon from '@mui/icons-material/HighlightOff';
31
25
  import HistoryIcon from '@mui/icons-material/History';
32
- import MenuIcon from '@mui/icons-material/Menu';
33
26
  import PeopleIcon from '@mui/icons-material/People';
34
27
  import PersonIcon from '@mui/icons-material/Person';
35
28
  import SecurityIcon from '@mui/icons-material/Security';
36
29
  import SpeedIcon from '@mui/icons-material/Speed';
37
30
  import StarIcon from '@mui/icons-material/Star';
38
- import { DrawerHeader } from '../../components/sidepanel/drawer-header';
39
- import { Sidepanel } from "../../components/sidepanel/sidepanel";
40
31
  import { LoginComponent } from "../../default/login";
41
32
  import { MainContext } from '../../context';
42
- import { NavigationItem } from '../../components/sidepanel/navigation-item';
43
33
  import { AdminDashboardRoutes } from './admin-dashboard-routes';
34
+ import { AdminSidepanel } from './admin-sidepanel';
35
+ import { AdminHeader } from './admin-header';
36
+ import { isNavGroup, NavEntry } from './nav-types';
44
37
 
45
38
  import { NamespaceAdmin } from './namespace-admin';
46
39
  import { PublisherAdmin } from './publisher-admin';
@@ -54,51 +47,23 @@ import { Welcome } from './welcome';
54
47
  const ExtensionAdmin = lazy(() => import('./extension-admin').then(m => ({ default: m.ExtensionAdmin })));
55
48
  const UsageStatsView = lazy(() => import('./usage-stats/usage-stats').then(m => ({ default: m.UsageStatsView })));
56
49
 
57
- const Message: FunctionComponent<{message: string}> = ({ message }) => {
58
- return (<Box sx={{
59
- display: 'flex',
60
- justifyContent: 'center',
61
- alignItems: 'center',
62
- width: '100%'
63
- }}>
64
- <Typography variant='h6'>{message}</Typography>
65
- </Box>);
66
- };
67
-
68
- interface RouteEntry {
69
- path: string;
70
- name: string;
71
- icon: ReactNode;
72
- }
73
-
74
- interface NavGroup {
75
- name: string;
76
- icon: ReactNode;
77
- children: RouteEntry[];
78
- }
79
-
80
- type NavEntry = RouteEntry | NavGroup;
81
-
82
- const isNavGroup = (entry: NavEntry): entry is NavGroup => 'children' in entry;
83
-
84
50
  const navConfig: NavEntry[] = [
85
- { path: AdminDashboardRoutes.NAMESPACE_ADMIN, name: 'Namespaces', icon: <AssignmentIndIcon /> },
86
- { path: AdminDashboardRoutes.EXTENSION_ADMIN, name: 'Extensions', icon: <ExtensionSharpIcon /> },
87
- { path: AdminDashboardRoutes.PUBLISHER_ADMIN, name: 'Publisher', icon: <PersonIcon /> },
88
- { path: AdminDashboardRoutes.SCANS_ADMIN, name: 'Scans', icon: <SecurityIcon /> },
51
+ { path: AdminDashboardRoutes.NAMESPACE_ADMIN, name: 'Namespaces', icon: <AssignmentIndIcon />, description: 'Manage user roles and create new namespaces' },
52
+ { path: AdminDashboardRoutes.EXTENSION_ADMIN, name: 'Extensions', icon: <ExtensionSharpIcon />, description: 'Search for extensions and remove certain versions' },
53
+ { path: AdminDashboardRoutes.PUBLISHER_ADMIN, name: 'Publisher', icon: <PersonIcon />, description: 'Search for publishers and revoke their contributions' },
54
+ { path: AdminDashboardRoutes.SCANS_ADMIN, name: 'Scans', icon: <SecurityIcon />, description: 'View security scan results and manage quarantined extensions' },
89
55
  {
90
56
  name: 'Rate Limiting',
91
57
  icon: <SpeedIcon />,
92
58
  children: [
93
- { path: AdminDashboardRoutes.TIERS, name: 'Tiers', icon: <StarIcon /> },
94
- { path: AdminDashboardRoutes.CUSTOMERS, name: 'Customers', icon: <PeopleIcon /> },
95
- { path: AdminDashboardRoutes.USAGE_STATS, name: 'Usage Stats', icon: <BarChartIcon /> },
59
+ { path: AdminDashboardRoutes.TIERS, name: 'Tiers', icon: <StarIcon />, description: 'Manage rate-limit tiers' },
60
+ { path: AdminDashboardRoutes.CUSTOMERS, name: 'Customers', icon: <PeopleIcon />, description: 'Manage rate-limit customers' },
61
+ { path: AdminDashboardRoutes.USAGE_STATS, name: 'Usage Stats', icon: <BarChartIcon />, description: 'Show usage stats for customers' },
96
62
  ],
97
63
  },
98
- { path: AdminDashboardRoutes.LOGS, name: 'Logs', icon: <HistoryIcon /> },
64
+ { path: AdminDashboardRoutes.LOGS, name: 'Logs', icon: <HistoryIcon />, description: 'Browse admin activity logs' },
99
65
  ];
100
66
 
101
- // Flat name lookup for breadcrumbs
102
67
  const routeNames: { [key: string]: string } = {
103
68
  [AdminDashboardRoutes.MAIN]: 'Admin Dashboard',
104
69
  ...navConfig.reduce<{ [key: string]: string }>((acc, entry) => {
@@ -113,165 +78,51 @@ const routeNames: { [key: string]: string } = {
113
78
  }, {}),
114
79
  };
115
80
 
116
- const drawerWidth = 240;
117
-
118
- interface AppBarProps extends MuiAppBarProps {
119
- open?: boolean;
120
- }
121
-
122
- const AppBar = styled(MuiAppBar, {
123
- shouldForwardProp: (prop) => prop !== 'open',
124
- })<AppBarProps>(({ theme, open }) => ({
125
- transition: theme.transitions.create(['margin', 'width'], {
126
- easing: theme.transitions.easing.sharp,
127
- duration: theme.transitions.duration.leavingScreen,
128
- }),
129
- ...(open && {
130
- width: `calc(100% - ${drawerWidth}px)`,
131
- marginLeft: `${drawerWidth}px`,
132
- transition: theme.transitions.create(['margin', 'width'], {
133
- easing: theme.transitions.easing.easeOut,
134
- duration: theme.transitions.duration.enteringScreen,
135
- }),
136
- }),
81
+ const ScrollableContent = styled(Box)(({ theme }) => ({
82
+ flex: 1,
83
+ overflowY: 'auto',
84
+ '&::-webkit-scrollbar': {
85
+ width: '12px',
86
+ },
87
+ '&::-webkit-scrollbar-track': {
88
+ backgroundColor: theme.palette.action.hover,
89
+ },
90
+ '&::-webkit-scrollbar-thumb': {
91
+ backgroundColor: theme.palette.action.selected,
92
+ borderRadius: '6px',
93
+ '&:hover': {
94
+ backgroundColor: theme.palette.action.focus,
95
+ },
96
+ },
137
97
  }));
138
98
 
139
- interface LinkRouterProps extends LinkProps {
140
- to: string;
141
- replace?: boolean;
142
- }
143
-
144
- const LinkRouter = (props: LinkRouterProps) => (
145
- <Link {...props} component={RouterLink as any} />
146
- );
147
-
148
- const BreadcrumbsComponent = () => {
149
- const { pathname } = useLocation();
150
-
151
- const pathnames = pathname.split("/").filter((segment) => segment);
152
-
153
- return (
154
- <Breadcrumbs aria-label='breadcrumb' sx={{ pt: 2, pb: 2, px: 4 }} >
155
- <LinkRouter underline='hover' color='inherit' to='/'>
156
- Home
157
- </LinkRouter>
158
- {pathnames.map((value, index) => {
159
- const last = index === pathnames.length - 1;
160
- const to = `/${pathnames.slice(0, index + 1).join("/")}`;
161
-
162
- return last ? (
163
- <Typography color='text.primary' key={to}>
164
- {routeNames[to] ?? value}
165
- </Typography>
166
- ) : (
167
- <LinkRouter underline='hover' color='inherit' to={to} key={to}>
168
- {routeNames[to]}
169
- </LinkRouter>
170
- );
171
- })}
172
- </Breadcrumbs>
173
- );
99
+ const Message: FunctionComponent<{message: string}> = ({ message }) => {
100
+ return (<Box sx={{
101
+ display: 'flex',
102
+ justifyContent: 'center',
103
+ alignItems: 'center',
104
+ width: '100%'
105
+ }}>
106
+ <Typography variant='h6'>{message}</Typography>
107
+ </Box>);
174
108
  };
175
109
 
176
- const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })<{
177
- open?: boolean;
178
- }>(({ theme, open }) => ({
179
- flexGrow: 1,
180
- padding: theme.spacing(3),
181
- transition: theme.transitions.create('margin', {
182
- easing: theme.transitions.easing.sharp,
183
- duration: theme.transitions.duration.leavingScreen,
184
- }),
185
- marginLeft: `-${drawerWidth}px`,
186
- ...(open && {
187
- transition: theme.transitions.create('margin', {
188
- easing: theme.transitions.easing.easeOut,
189
- duration: theme.transitions.duration.enteringScreen,
190
- }),
191
- marginLeft: 0,
192
- }),
193
- }));
194
-
195
110
  export const AdminDashboard: FunctionComponent<AdminDashboardProps> = props => {
196
111
  const { user, loginProviders } = useContext(MainContext);
197
- const [drawerOpen, setDrawerOpen] = useState(true);
198
112
 
199
113
  const navigate = useNavigate();
200
114
  const toMainPage = () => navigate('/');
201
115
 
202
- const [currentPage, setCurrentPage] = useState<string | undefined>(useLocation().pathname);
203
- const handleOpenRoute = (route: string) => {
204
- setCurrentPage(route);
205
- };
206
-
207
116
  let content: ReactNode = null;
208
117
  if (user?.role === 'admin') {
209
118
  content =
210
- <Box sx={{ display: 'flex', width: '100%' }}>
119
+ <Box sx={{ display: 'flex', width: '100%', height: '100%' }}>
211
120
  <CssBaseline />
212
- <AppBar position='fixed' open={drawerOpen} color='default' enableColorOnDark elevation={0}>
213
- <Toolbar>
214
- <IconButton
215
- aria-label='open drawer'
216
- onClick={() => setDrawerOpen(true)}
217
- edge='start'
218
- sx={[{ mr: 2 }, drawerOpen && { display: 'none' }]}
219
- >
220
- <MenuIcon />
221
- </IconButton>
222
- <Box sx={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
223
- <BreadcrumbsComponent />
224
- <IconButton onClick={toMainPage} sx={{ mt: 1, mr: 1 }}>
225
- <HighlightOffIcon/>
226
- </IconButton>
227
- </Box>
228
- </Toolbar>
229
- </AppBar>
230
- <Sidepanel width={drawerWidth} open={drawerOpen} handleDrawerClose={() => setDrawerOpen(false)} >
231
- {navConfig.map((entry) => {
232
- if (isNavGroup(entry)) {
233
- return (
234
- <NavigationItem
235
- key={entry.name}
236
- label={entry.name}
237
- icon={entry.icon}
238
- >
239
- {entry.children.map((child) => (
240
- <NavigationItem key={child.path} onOpenRoute={handleOpenRoute} active={currentPage?.startsWith(child.path)}
241
- label={child.name} icon={child.icon} route={child.path}/>
242
- ))}
243
- </NavigationItem>
244
- );
245
- }
246
- return (
247
- <NavigationItem key={entry.path} onOpenRoute={handleOpenRoute} active={currentPage?.startsWith(entry.path)}
248
- label={entry.name} icon={entry.icon} route={entry.path}/>
249
- );
250
- })}
251
- </Sidepanel>
252
- <Main open={drawerOpen} >
253
- <DrawerHeader />
254
- <Box
255
- overflow='auto'
256
- flex={1}
257
- sx={{
258
- overflowY: 'scroll',
259
- '&::-webkit-scrollbar': {
260
- width: '12px',
261
- },
262
- '&::-webkit-scrollbar-track': {
263
- backgroundColor: 'rgba(0, 0, 0, 0.2)',
264
- },
265
- '&::-webkit-scrollbar-thumb': {
266
- backgroundColor: 'rgba(255, 255, 255, 0.2)',
267
- borderRadius: '6px',
268
- '&:hover': {
269
- backgroundColor: 'rgba(255, 255, 255, 0.3)',
270
- },
271
- },
272
- }}
273
- >
274
- <Container sx={{ pt: 2, pb: 4, px: 3 }} maxWidth='xl'>
121
+ <AdminSidepanel items={navConfig} />
122
+ <Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, overflow: 'hidden' }}>
123
+ <AdminHeader routeNames={routeNames} onClose={toMainPage} />
124
+ <ScrollableContent>
125
+ <Container sx={{ pt: 3, pb: 4, px: 3 }} maxWidth='xl'>
275
126
  <Suspense fallback={null}>
276
127
  <Routes>
277
128
  <Route path='/namespaces' element={<NamespaceAdmin/>} />
@@ -286,12 +137,12 @@ export const AdminDashboard: FunctionComponent<AdminDashboardProps> = props => {
286
137
  <Route path='/usage' element={<UsageStatsView/>} />
287
138
  <Route path='/usage/:customer' element={<UsageStatsView/>} />
288
139
  <Route path='/logs' element={<Logs/>} />
289
- <Route path='*' element={<Welcome/>} />
140
+ <Route path='*' element={<Welcome items={navConfig} />} />
290
141
  </Routes>
291
142
  </Suspense>
292
143
  </Container>
293
- </Box>
294
- </Main>
144
+ </ScrollableContent>
145
+ </Box>
295
146
  </Box>;
296
147
  } else if (user) {
297
148
  content = <Message message='You are not authorized as administrator.'/>;
@@ -0,0 +1,59 @@
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 } from 'react';
15
+ import { AppBar, Breadcrumbs, IconButton, Link, Toolbar, Typography } from '@mui/material';
16
+ import HighlightOffIcon from '@mui/icons-material/HighlightOff';
17
+ import { Link as RouterLink, useLocation } from 'react-router-dom';
18
+
19
+ export interface AdminHeaderProps {
20
+ routeNames: Record<string, string>;
21
+ onClose: () => void;
22
+ }
23
+
24
+ const BreadcrumbsNav: FunctionComponent<{ routeNames: Record<string, string> }> = ({ routeNames }) => {
25
+ const { pathname } = useLocation();
26
+ const segments = pathname.split('/').filter(Boolean);
27
+
28
+ return (
29
+ <Breadcrumbs aria-label='breadcrumb' sx={{ pt: 2, pb: 2, px: 4 }}>
30
+ <Link component={RouterLink} to='/' underline='hover' color='inherit'>
31
+ Home
32
+ </Link>
33
+ {segments.map((value, index) => {
34
+ const to = `/${segments.slice(0, index + 1).join('/')}`;
35
+ const label = routeNames[to] ?? value;
36
+ const isLast = index === segments.length - 1;
37
+
38
+ return isLast ? (
39
+ <Typography color='text.primary' key={to}>{label}</Typography>
40
+ ) : (
41
+ <Link component={RouterLink} to={to} underline='hover' color='inherit' key={to}>
42
+ {label}
43
+ </Link>
44
+ );
45
+ })}
46
+ </Breadcrumbs>
47
+ );
48
+ };
49
+
50
+ export const AdminHeader: FunctionComponent<AdminHeaderProps> = ({ routeNames, onClose }) => (
51
+ <AppBar position='sticky' color='default' enableColorOnDark elevation={0}>
52
+ <Toolbar sx={{ display: 'flex', justifyContent: 'space-between' }}>
53
+ <BreadcrumbsNav routeNames={routeNames} />
54
+ <IconButton onClick={onClose} aria-label='close admin dashboard' sx={{ mt: 1, mr: 1 }}>
55
+ <HighlightOffIcon />
56
+ </IconButton>
57
+ </Toolbar>
58
+ </AppBar>
59
+ );
@@ -0,0 +1,59 @@
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 } from 'react';
15
+ import { useLocation } from 'react-router-dom';
16
+ import { Sidepanel } from '../../components/sidepanel/sidepanel';
17
+ import { NavigationItem } from '../../components/sidepanel/navigation-item';
18
+ import { isNavGroup, NavEntry } from './nav-types';
19
+ import { useLocalStorage } from '../../hooks/use-local-storage';
20
+
21
+ export interface AdminSidepanelProps {
22
+ items: NavEntry[];
23
+ }
24
+
25
+ export const AdminSidepanel: FunctionComponent<AdminSidepanelProps> = ({ items }) => {
26
+ const [open, setOpen] = useLocalStorage('openvsx-admin-sidepanel-open', true);
27
+ const { pathname } = useLocation();
28
+
29
+ return (
30
+ <Sidepanel open={open} onToggle={() => setOpen(prev => !prev)}>
31
+ {items.map((entry) => {
32
+ if (isNavGroup(entry)) {
33
+ return (
34
+ <NavigationItem key={entry.name} label={entry.name} icon={entry.icon}>
35
+ {entry.children.map((child) => (
36
+ <NavigationItem
37
+ key={child.path}
38
+ active={pathname.startsWith(child.path)}
39
+ label={child.name}
40
+ icon={child.icon}
41
+ route={child.path}
42
+ />
43
+ ))}
44
+ </NavigationItem>
45
+ );
46
+ }
47
+ return (
48
+ <NavigationItem
49
+ key={entry.path}
50
+ active={pathname.startsWith(entry.path)}
51
+ label={entry.name}
52
+ icon={entry.icon}
53
+ route={entry.path}
54
+ />
55
+ );
56
+ })}
57
+ </Sidepanel>
58
+ );
59
+ };
@@ -31,6 +31,10 @@ export const NamespaceAdmin: FunctionComponent = props => {
31
31
  };
32
32
  }, []);
33
33
 
34
+ const handleDeleteNamespace = () => {
35
+ setCurrentNamespace(undefined);
36
+ };
37
+
34
38
  const fetchNamespace = async (namespaceName: string) => {
35
39
  if (!namespaceName) {
36
40
  setCurrentNamespace(undefined);
@@ -82,6 +86,7 @@ export const NamespaceAdmin: FunctionComponent = props => {
82
86
  listContainer = <NamespaceDetailConfigContext.Provider value={{ defaultMemberRole: 'owner' }}>
83
87
  <NamespaceDetail
84
88
  setLoadingState={setLoading}
89
+ onDelete={handleDeleteNamespace}
85
90
  namespace={currentNamespace}
86
91
  filterUsers={() => true}
87
92
  fixSelf={false}
@@ -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
+ };