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.
- package/lib/components/sidepanel/navigation-item.d.ts +0 -1
- package/lib/components/sidepanel/navigation-item.d.ts.map +1 -1
- package/lib/components/sidepanel/navigation-item.js +22 -8
- package/lib/components/sidepanel/navigation-item.js.map +1 -1
- package/lib/components/sidepanel/sidebar-context.d.ts +16 -0
- package/lib/components/sidepanel/sidebar-context.d.ts.map +1 -0
- package/lib/components/sidepanel/sidebar-context.js +15 -0
- package/lib/components/sidepanel/sidebar-context.js.map +1 -0
- package/lib/components/sidepanel/sidepanel.d.ts +4 -4
- package/lib/components/sidepanel/sidepanel.d.ts.map +1 -1
- package/lib/components/sidepanel/sidepanel.js +47 -10
- package/lib/components/sidepanel/sidepanel.js.map +1 -1
- package/lib/extension-registry-service.d.ts +6 -0
- package/lib/extension-registry-service.d.ts.map +1 -1
- package/lib/extension-registry-service.js +17 -0
- package/lib/extension-registry-service.js.map +1 -1
- package/lib/hooks/use-local-storage.d.ts +23 -0
- package/lib/hooks/use-local-storage.d.ts.map +1 -0
- package/lib/hooks/use-local-storage.js +62 -0
- package/lib/hooks/use-local-storage.js.map +1 -0
- package/lib/pages/admin-dashboard/admin-dashboard.d.ts.map +1 -1
- package/lib/pages/admin-dashboard/admin-dashboard.js +39 -95
- package/lib/pages/admin-dashboard/admin-dashboard.js.map +1 -1
- package/lib/pages/admin-dashboard/admin-header.d.ts +19 -0
- package/lib/pages/admin-dashboard/admin-header.d.ts.map +1 -0
- package/lib/pages/admin-dashboard/admin-header.js +16 -0
- package/lib/pages/admin-dashboard/admin-header.js.map +1 -0
- package/lib/pages/admin-dashboard/admin-sidepanel.d.ts +19 -0
- package/lib/pages/admin-dashboard/admin-sidepanel.d.ts.map +1 -0
- package/lib/pages/admin-dashboard/admin-sidepanel.js +17 -0
- package/lib/pages/admin-dashboard/admin-sidepanel.js.map +1 -0
- package/lib/pages/admin-dashboard/namespace-admin.d.ts.map +1 -1
- package/lib/pages/admin-dashboard/namespace-admin.js +4 -1
- package/lib/pages/admin-dashboard/namespace-admin.js.map +1 -1
- package/lib/pages/admin-dashboard/namespace-change-dialog.d.ts.map +1 -1
- package/lib/pages/admin-dashboard/namespace-change-dialog.js +6 -1
- package/lib/pages/admin-dashboard/namespace-change-dialog.js.map +1 -1
- package/lib/pages/admin-dashboard/namespace-delete-dialog.d.ts +23 -0
- package/lib/pages/admin-dashboard/namespace-delete-dialog.d.ts.map +1 -0
- package/lib/pages/admin-dashboard/namespace-delete-dialog.js +53 -0
- package/lib/pages/admin-dashboard/namespace-delete-dialog.js.map +1 -0
- package/lib/pages/admin-dashboard/nav-types.d.ts +27 -0
- package/lib/pages/admin-dashboard/nav-types.d.ts.map +1 -0
- package/lib/pages/admin-dashboard/nav-types.js +14 -0
- package/lib/pages/admin-dashboard/nav-types.js.map +1 -0
- package/lib/pages/admin-dashboard/scan-admin.d.ts.map +1 -1
- package/lib/pages/admin-dashboard/scan-admin.js +2 -5
- package/lib/pages/admin-dashboard/scan-admin.js.map +1 -1
- package/lib/pages/admin-dashboard/welcome.d.ts +5 -1
- package/lib/pages/admin-dashboard/welcome.d.ts.map +1 -1
- package/lib/pages/admin-dashboard/welcome.js +18 -16
- package/lib/pages/admin-dashboard/welcome.js.map +1 -1
- package/lib/pages/user/user-setting-tabs.d.ts.map +1 -1
- package/lib/pages/user/user-setting-tabs.js +1 -1
- package/lib/pages/user/user-setting-tabs.js.map +1 -1
- package/lib/pages/user/user-settings-namespace-detail.d.ts +1 -0
- package/lib/pages/user/user-settings-namespace-detail.d.ts.map +1 -1
- package/lib/pages/user/user-settings-namespace-detail.js +18 -2
- package/lib/pages/user/user-settings-namespace-detail.js.map +1 -1
- package/lib/pages/user/user-settings-namespaces.js.map +1 -1
- package/package.json +1 -1
- package/src/components/sidepanel/navigation-item.tsx +79 -23
- package/src/components/sidepanel/sidebar-context.tsx +17 -0
- package/src/components/sidepanel/sidepanel.tsx +57 -29
- package/src/extension-registry-service.ts +18 -0
- package/src/hooks/use-local-storage.ts +67 -0
- package/src/pages/admin-dashboard/admin-dashboard.tsx +48 -197
- package/src/pages/admin-dashboard/admin-header.tsx +59 -0
- package/src/pages/admin-dashboard/admin-sidepanel.tsx +59 -0
- package/src/pages/admin-dashboard/namespace-admin.tsx +5 -0
- package/src/pages/admin-dashboard/namespace-change-dialog.tsx +55 -46
- package/src/pages/admin-dashboard/namespace-delete-dialog.tsx +89 -0
- package/src/pages/admin-dashboard/nav-types.ts +31 -0
- package/src/pages/admin-dashboard/scan-admin.tsx +2 -5
- package/src/pages/admin-dashboard/welcome.tsx +80 -48
- package/src/pages/user/user-setting-tabs.tsx +3 -2
- package/src/pages/user/user-settings-namespace-detail.tsx +37 -4
- 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,
|
|
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
|
|
24
|
-
import {
|
|
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
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
</
|
|
294
|
-
</
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
export interface NamespaceChangeDialogProps {
|
|
21
|
+
open: boolean;
|
|
22
|
+
onClose: () => void;
|
|
23
|
+
namespace: Namespace;
|
|
24
|
+
setLoadingState: (loading: boolean) => void;
|
|
25
|
+
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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, {
|
|
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
|
-
|
|
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
|
-
|
|
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}
|
|
110
|
-
|
|
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}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
</DialogActions>
|
|
137
|
+
</Dialog>
|
|
138
|
+
<InfoDialog infoMessage={infoDialogMessage} isInfoDialogOpen={infoDialogIsOpen}
|
|
139
|
+
handleCloseDialog={onInfoDialogClose}/>
|
|
140
|
+
</>;
|
|
141
|
+
};
|