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
@@ -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;
@@ -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
@@ -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-dashboard-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
+ };
@@ -20,6 +20,7 @@ export const UserSettingTabs = (): ReactElement => {
20
20
  const isATablet = useMediaQuery(theme.breakpoints.down('md'));
21
21
  const isAMobile = useMediaQuery(theme.breakpoints.down('sm'));
22
22
  const { tab } = useParams();
23
+
23
24
  const navigate = useNavigate();
24
25
 
25
26
  const handleChange = (event: ChangeEvent, newTab: string) => {
@@ -32,10 +33,10 @@ export const UserSettingTabs = (): ReactElement => {
32
33
 
33
34
  return (
34
35
  <Tabs
35
- value={tab}
36
+ value={tab ?? 'extensions'}
36
37
  onChange={handleChange}
37
38
  orientation={isATablet ? 'horizontal' : 'vertical'}
38
- centered={isAMobile ? true : false}
39
+ centered={isAMobile}
39
40
  indicatorColor='secondary'
40
41
  >
41
42
  <Tab value='profile' label='Profile' />
@@ -17,6 +17,7 @@ import { UserNamespaceExtensionListContainer } from './user-namespace-extension-
17
17
  import { AdminDashboardRoutes } from '../admin-dashboard/admin-dashboard-routes';
18
18
  import { Namespace, UserData } from '../../extension-registry-types';
19
19
  import { NamespaceChangeDialog } from '../admin-dashboard/namespace-change-dialog';
20
+ import { NamespaceDeleteDialog } from '../admin-dashboard/namespace-delete-dialog';
20
21
  import { UserNamespaceMemberList } from './user-namespace-member-list';
21
22
  import { UserNamespaceDetails } from './user-namespace-details';
22
23
 
@@ -61,6 +62,7 @@ const NamespaceHeader = styled(Box)(({ theme }: { theme: Theme }) => ({
61
62
 
62
63
  export const NamespaceDetail: FunctionComponent<NamespaceDetailProps> = props => {
63
64
  const [changeDialogIsOpen, setChangeDialogIsOpen] = useState(false);
65
+ const [deleteDialogIsOpen, setDeleteDialogIsOpen] = useState(false);
64
66
  const { pathname } = useLocation();
65
67
 
66
68
  const handleCloseChangeDialog = async () => {
@@ -70,6 +72,19 @@ export const NamespaceDetail: FunctionComponent<NamespaceDetailProps> = props =>
70
72
  setChangeDialogIsOpen(true);
71
73
  };
72
74
 
75
+ const handleCloseDeleteDialog = async () => {
76
+ setDeleteDialogIsOpen(false);
77
+ };
78
+ const handleDeletedNamespace = async () => {
79
+ setDeleteDialogIsOpen(false);
80
+ if (props.onDelete !== undefined) {
81
+ props.onDelete();
82
+ }
83
+ };
84
+
85
+ const handleOpenDeleteDialog = () => {
86
+ setDeleteDialogIsOpen(true);
87
+ };
73
88
  const warningColor = props.theme === 'dark' ? '#fff' : '#151515';
74
89
  return <>
75
90
  <NamespaceDetailContainer container direction='column' spacing={4}>
@@ -101,9 +116,20 @@ export const NamespaceDetail: FunctionComponent<NamespaceDetailProps> = props =>
101
116
  <NamespaceHeader>
102
117
  <Typography variant='h4'>{props.namespace.name}</Typography>
103
118
  { pathname.startsWith(AdminDashboardRoutes.NAMESPACE_ADMIN)
104
- ? <Button sx={{ ml: { xs: 2, sm: 2, md: 2, lg: 0, xl: 0 } }} variant='outlined' onClick={handleOpenChangeDialog}>
105
- Change Namespace
106
- </Button>
119
+ ?
120
+ <Box>
121
+ <Button sx={{ ml: { xs: 2, sm: 2, md: 2, lg: 0, xl: 0 } }} variant='outlined' onClick={handleOpenChangeDialog}>
122
+ Change Namespace
123
+ </Button>
124
+ { Object.keys(props.namespace.extensions).length === 0 &&
125
+ <Button
126
+ variant='outlined'
127
+ sx={{ color: 'error.main', height: 36, ml: { xs: 2 } }}
128
+ onClick={handleOpenDeleteDialog}>
129
+ Delete
130
+ </Button>
131
+ }
132
+ </Box>
107
133
  : null
108
134
  }
109
135
  </NamespaceHeader>
@@ -137,6 +163,12 @@ export const NamespaceDetail: FunctionComponent<NamespaceDetailProps> = props =>
137
163
  onClose={handleCloseChangeDialog}
138
164
  namespace={props.namespace}
139
165
  setLoadingState={props.setLoadingState} />
166
+ <NamespaceDeleteDialog
167
+ open={deleteDialogIsOpen}
168
+ onClose={handleCloseDeleteDialog}
169
+ onDelete={handleDeletedNamespace}
170
+ namespace={props.namespace}
171
+ setLoadingState={props.setLoadingState} />
140
172
  </>;
141
173
  };
142
174
 
@@ -147,4 +179,5 @@ export interface NamespaceDetailProps {
147
179
  setLoadingState: (loading: boolean) => void;
148
180
  namespaceAccessUrl?: string;
149
181
  theme?: string;
150
- }
182
+ onDelete?: () => void;
183
+ }
@@ -117,7 +117,7 @@ export const UserSettingsNamespaces: FunctionComponent = () => {
117
117
  filterUsers={(foundUser: UserData) => foundUser.provider !== user?.provider || foundUser.loginName !== user?.loginName}
118
118
  fixSelf={true}
119
119
  namespaceAccessUrl={namespaceAccessUrl}
120
- theme={pageSettings.themeType}/>
120
+ theme={pageSettings.themeType} />
121
121
  </Box>;
122
122
  } else if (!loading) {
123
123
  namespaceContainer = <Typography variant='body1'>No namespaces available. Read <Link color='secondary' href={namespaceAccessUrl} target='_blank'>here</Link> about claiming namespaces.</Typography>;