openvsx-webui-test 0.19.0-dev.0 → 0.19.0-dev.2

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 (84) hide show
  1. package/lib/components/error-dialog.d.ts.map +1 -1
  2. package/lib/components/error-dialog.js +3 -5
  3. package/lib/components/error-dialog.js.map +1 -1
  4. package/lib/components/scan-admin/scan-card/scan-card-content.d.ts +3 -3
  5. package/lib/components/scan-admin/scan-card/scan-card-content.d.ts.map +1 -1
  6. package/lib/components/scan-admin/scan-card/scan-card-content.js +146 -169
  7. package/lib/components/scan-admin/scan-card/scan-card-content.js.map +1 -1
  8. package/lib/components/scan-admin/scan-card/scan-card-header.d.ts.map +1 -1
  9. package/lib/components/scan-admin/scan-card/scan-card-header.js +17 -5
  10. package/lib/components/scan-admin/scan-card/scan-card-header.js.map +1 -1
  11. package/lib/components/scan-admin/scan-card/scan-card.js +1 -1
  12. package/lib/components/scan-admin/scan-card/scan-card.js.map +1 -1
  13. package/lib/components/timestamp.d.ts +1 -0
  14. package/lib/components/timestamp.d.ts.map +1 -1
  15. package/lib/components/timestamp.js +3 -2
  16. package/lib/components/timestamp.js.map +1 -1
  17. package/lib/default/menu-content.d.ts +1 -4
  18. package/lib/default/menu-content.d.ts.map +1 -1
  19. package/lib/default/menu-content.js +8 -16
  20. package/lib/default/menu-content.js.map +1 -1
  21. package/lib/extension-registry-service.js +1 -1
  22. package/lib/extension-registry-service.js.map +1 -1
  23. package/lib/extension-registry-types.d.ts +2 -0
  24. package/lib/extension-registry-types.d.ts.map +1 -1
  25. package/lib/extension-registry-types.js.map +1 -1
  26. package/lib/hooks/scan-admin/use-query-params-state.d.ts +15 -0
  27. package/lib/hooks/scan-admin/use-query-params-state.d.ts.map +1 -0
  28. package/lib/hooks/scan-admin/use-query-params-state.js +44 -0
  29. package/lib/hooks/scan-admin/use-query-params-state.js.map +1 -0
  30. package/lib/pages/admin-dashboard/admin-dashboard.d.ts.map +1 -1
  31. package/lib/pages/admin-dashboard/admin-dashboard.js +2 -2
  32. package/lib/pages/admin-dashboard/admin-dashboard.js.map +1 -1
  33. package/lib/pages/admin-dashboard/extension-admin.d.ts.map +1 -1
  34. package/lib/pages/admin-dashboard/extension-admin.js +4 -3
  35. package/lib/pages/admin-dashboard/extension-admin.js.map +1 -1
  36. package/lib/pages/admin-dashboard/namespace-input.d.ts +1 -0
  37. package/lib/pages/admin-dashboard/namespace-input.d.ts.map +1 -1
  38. package/lib/pages/admin-dashboard/namespace-input.js +2 -2
  39. package/lib/pages/admin-dashboard/namespace-input.js.map +1 -1
  40. package/lib/pages/extension-detail/extension-detail-overview.js +1 -1
  41. package/lib/pages/extension-detail/extension-detail-overview.js.map +1 -1
  42. package/lib/pages/extension-detail/extension-review-dialog.d.ts.map +1 -1
  43. package/lib/pages/extension-detail/extension-review-dialog.js +2 -11
  44. package/lib/pages/extension-detail/extension-review-dialog.js.map +1 -1
  45. package/lib/pages/user/avatar.d.ts.map +1 -1
  46. package/lib/pages/user/avatar.js +9 -9
  47. package/lib/pages/user/avatar.js.map +1 -1
  48. package/lib/pages/user/logout.d.ts +3 -2
  49. package/lib/pages/user/logout.d.ts.map +1 -1
  50. package/lib/pages/user/logout.js +5 -4
  51. package/lib/pages/user/logout.js.map +1 -1
  52. package/lib/pages/user/user-settings-extensions.js +1 -1
  53. package/lib/pages/user/user-settings-extensions.js.map +1 -1
  54. package/lib/pages/user/user-settings-tokens.d.ts.map +1 -1
  55. package/lib/pages/user/user-settings-tokens.js +1 -1
  56. package/lib/pages/user/user-settings-tokens.js.map +1 -1
  57. package/lib/server-request.d.ts.map +1 -1
  58. package/lib/server-request.js +5 -1
  59. package/lib/server-request.js.map +1 -1
  60. package/lib/utils.d.ts +1 -1
  61. package/lib/utils.d.ts.map +1 -1
  62. package/lib/utils.js +56 -22
  63. package/lib/utils.js.map +1 -1
  64. package/package.json +5 -4
  65. package/src/components/error-dialog.tsx +3 -5
  66. package/src/components/scan-admin/scan-card/scan-card-content.tsx +380 -408
  67. package/src/components/scan-admin/scan-card/scan-card-header.tsx +35 -6
  68. package/src/components/scan-admin/scan-card/scan-card.tsx +2 -2
  69. package/src/components/timestamp.tsx +3 -1
  70. package/src/default/menu-content.tsx +70 -96
  71. package/src/extension-registry-service.ts +1 -1
  72. package/src/extension-registry-types.ts +2 -0
  73. package/src/hooks/scan-admin/use-query-params-state.ts +55 -0
  74. package/src/pages/admin-dashboard/admin-dashboard.tsx +2 -1
  75. package/src/pages/admin-dashboard/extension-admin.tsx +4 -2
  76. package/src/pages/admin-dashboard/namespace-input.tsx +3 -1
  77. package/src/pages/extension-detail/extension-detail-overview.tsx +1 -1
  78. package/src/pages/extension-detail/extension-review-dialog.tsx +2 -12
  79. package/src/pages/user/avatar.tsx +30 -35
  80. package/src/pages/user/logout.tsx +6 -4
  81. package/src/pages/user/user-settings-extensions.tsx +1 -1
  82. package/src/pages/user/user-settings-tokens.tsx +1 -0
  83. package/src/server-request.ts +5 -1
  84. package/src/utils.ts +47 -19
@@ -12,8 +12,9 @@
12
12
  ********************************************************************************/
13
13
 
14
14
  import { FC, useState } from 'react';
15
- import { Box, Typography, Chip, CircularProgress } from '@mui/material';
15
+ import { Box, Typography, Chip, CircularProgress, Link } from '@mui/material';
16
16
  import {
17
+ AdminPanelSettings as AdminPanelIcon,
17
18
  CheckCircle as CheckCircleIcon,
18
19
  GppMaybe as WarningIcon,
19
20
  Block as BlockIcon,
@@ -30,6 +31,9 @@ import {
30
31
  getHypotheticalStatus,
31
32
  getStatusColorSx,
32
33
  } from './utils';
34
+ import { createRoute } from '../../../utils';
35
+ import { AdminDashboardRoutes } from '../../../pages/admin-dashboard/admin-dashboard';
36
+ import { ExtensionDetailRoutes } from '../../../pages/extension-detail/extension-detail';
33
37
 
34
38
  interface ScanCardHeaderProps {
35
39
  scan: ScanResult;
@@ -61,6 +65,12 @@ export const ScanCardHeader: FC<ScanCardHeaderProps> = ({ scan }) => {
61
65
  const [imageError, setImageError] = useState(false);
62
66
 
63
67
  const hasValidIcon = scan.extensionIcon && !imageError;
68
+ const extensionRoute = createRoute([ExtensionDetailRoutes.ROOT, scan.namespace, scan.extensionName]);
69
+ const adminRoute = createRoute([AdminDashboardRoutes.ROOT, 'extensions'],
70
+ [
71
+ { key: "namespace", value: scan.namespace },
72
+ { key: "extension", value: scan.extensionName }
73
+ ]);
64
74
 
65
75
  return (
66
76
  <>
@@ -97,8 +107,8 @@ export const ScanCardHeader: FC<ScanCardHeaderProps> = ({ scan }) => {
97
107
  )}
98
108
  </Box>
99
109
 
100
- {/* Columns 2-4: Display Name and Namespace */}
101
- <Box sx={{ gridRow: '1', gridColumn: '2 / 5', minWidth: 0 }}>
110
+ {/* Columns 2-5: Display Name and Namespace */}
111
+ <Box sx={{ gridRow: '1', gridColumn: 'span 4', minWidth: 0 }}>
102
112
  <ConditionalTooltip title={scan.displayName} arrow>
103
113
  <Typography
104
114
  variant='h6'
@@ -122,15 +132,34 @@ export const ScanCardHeader: FC<ScanCardHeaderProps> = ({ scan }) => {
122
132
  whiteSpace: 'nowrap',
123
133
  }}
124
134
  >
125
- {scan.namespace}.{scan.extensionName}
135
+ <Link
136
+ href={extensionRoute || undefined}
137
+ rel='noopener noreferrer'
138
+ variant='body2'
139
+ >
140
+ {scan.namespace}.{scan.extensionName}
141
+ </Link>
142
+
143
+ {adminRoute !== undefined &&
144
+ <Link
145
+ href={adminRoute}
146
+ rel='noopener noreferrer'
147
+ variant='body2'
148
+ >
149
+ <AdminPanelIcon sx={{
150
+ verticalAlign: 'bottom',
151
+ ml: '3px'
152
+ }} />
153
+ </Link>
154
+ }
126
155
  </Typography>
127
156
  </ConditionalTooltip>
128
157
  </Box>
129
158
 
130
- {/* Column 5: Status Badge */}
159
+ {/* Column 6: Status Badge */}
131
160
  <Box sx={{
132
161
  gridRow: '1',
133
- gridColumn: '5',
162
+ gridColumn: '6',
134
163
  display: 'flex',
135
164
  flexDirection: 'column',
136
165
  alignItems: 'flex-end',
@@ -110,10 +110,10 @@ export const ScanCard: FunctionComponent<ScanCardProps> = ({
110
110
  '&:last-child': { pb: showExpandButton ? 0 : 5 },
111
111
  }}
112
112
  >
113
- {/* 3 Row x 5 Column Grid Layout */}
113
+ {/* 3 Row x 6 Column Grid Layout */}
114
114
  <Box sx={{
115
115
  display: 'grid',
116
- gridTemplateColumns: `${ICON_SIZE}px 1fr 1fr 1fr 180px`,
116
+ gridTemplateColumns: `${ICON_SIZE}px 1fr 1fr 1fr 1fr 180px`,
117
117
  gridTemplateRows: 'auto auto auto',
118
118
  gap: 2,
119
119
  alignItems: 'start',
@@ -15,16 +15,18 @@ import { toRelativeTime, toLocalTime } from '../utils';
15
15
 
16
16
  export const Timestamp: FunctionComponent<TimestampProps> = props => {
17
17
  const sx = props.sx ?? [];
18
+ const isFutureTime = props.isFutureTime ?? false;
18
19
  const timestamp = props.value;
19
20
  return <Box
20
21
  component='span'
21
22
  title={toLocalTime(timestamp)}
22
23
  sx={[...(Array.isArray(sx) ? sx : [sx])]}>
23
- {toRelativeTime(timestamp)}
24
+ {toRelativeTime(timestamp, isFutureTime)}
24
25
  </Box>;
25
26
  };
26
27
 
27
28
  export interface TimestampProps {
28
29
  value: string;
30
+ isFutureTime?: boolean;
29
31
  sx?: SxProps<Theme>;
30
32
  }
@@ -8,10 +8,9 @@
8
8
  * SPDX-License-Identifier: EPL-2.0
9
9
  ********************************************************************************/
10
10
 
11
- import { FunctionComponent, PropsWithChildren, useContext } from 'react';
11
+ import { FunctionComponent, PropsWithChildren, useContext, useRef } from 'react';
12
12
  import { Typography, MenuItem, Link, Button, IconButton, Accordion, AccordionSummary, Avatar, AccordionDetails } from '@mui/material';
13
- import { useLocation } from 'react-router-dom';
14
- import { Link as RouteLink } from 'react-router-dom';
13
+ import { useLocation, Link as RouteLink } from 'react-router-dom';
15
14
  import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
16
15
  import GitHubIcon from '@mui/icons-material/GitHub';
17
16
  import MenuBookIcon from '@mui/icons-material/MenuBook';
@@ -31,21 +30,13 @@ import { LogoutForm } from '../pages/user/logout';
31
30
  import { LoginComponent } from './login';
32
31
 
33
32
  //-------------------- Mobile View --------------------//
34
-
35
- export const MobileMenuItem = styled(MenuItem)({
36
- cursor: 'auto',
37
- '&>a': {
38
- textDecoration: 'none'
39
- }
40
- });
41
-
42
33
  export const itemIcon = {
43
34
  mr: 1,
44
35
  width: '16px',
45
36
  height: '16px',
46
37
  };
47
38
 
48
- export const MobileMenuItemText: FunctionComponent<PropsWithChildren> = ({ children }) => {
39
+ export const MenuItemText: FunctionComponent<PropsWithChildren> = ({ children }) => {
49
40
  return (
50
41
  <Typography variant='body2' color='text.primary' sx={{ display: 'flex', alignItems: 'center', textTransform: 'none' }}>
51
42
  {children}
@@ -56,6 +47,7 @@ export const MobileMenuItemText: FunctionComponent<PropsWithChildren> = ({ child
56
47
  export const MobileUserAvatar: FunctionComponent = () => {
57
48
  const context = useContext(MainContext);
58
49
  const user = context.user;
50
+ const logoutFormRef = useRef<HTMLFormElement>(null);
59
51
  if (!user) {
60
52
  return null;
61
53
  }
@@ -66,52 +58,46 @@ export const MobileUserAvatar: FunctionComponent = () => {
66
58
  aria-controls='user-actions'
67
59
  id='user-avatar'
68
60
  >
69
- <MobileMenuItemText>
61
+ <MenuItemText>
70
62
  <Avatar
71
63
  src={user.avatarUrl}
72
64
  alt={user.loginName}
73
65
  variant='rounded'
74
66
  sx={itemIcon} />
75
67
  {user.loginName}
76
- </MobileMenuItemText>
68
+ </MenuItemText>
77
69
  </AccordionSummary>
78
70
  <AccordionDetails>
79
- <MobileMenuItem>
80
- <Link href={user.homepage}>
81
- <MobileMenuItemText>
82
- <GitHubIcon sx={itemIcon} />
83
- {user.loginName}
84
- </MobileMenuItemText>
85
- </Link>
86
- </MobileMenuItem>
87
- <MobileMenuItem>
88
- <RouteLink to={UserSettingsRoutes.PROFILE}>
89
- <MobileMenuItemText>
90
- <SettingsIcon sx={itemIcon} />
91
- Settings
92
- </MobileMenuItemText>
93
- </RouteLink>
94
- </MobileMenuItem>
71
+ <MenuItem component={Link} href={user.homepage}>
72
+ <MenuItemText>
73
+ <GitHubIcon sx={itemIcon} />
74
+ {user.loginName}
75
+ </MenuItemText>
76
+ </MenuItem>
77
+ <MenuItem component={RouteLink} to={UserSettingsRoutes.PROFILE}>
78
+ <MenuItemText>
79
+ <SettingsIcon sx={itemIcon} />
80
+ Settings
81
+ </MenuItemText>
82
+ </MenuItem>
95
83
  {
96
84
  user.role === 'admin'
97
- ? <MobileMenuItem>
98
- <RouteLink to={AdminDashboardRoutes.MAIN}>
99
- <MobileMenuItemText>
100
- <AdminPanelSettingsIcon sx={itemIcon} />
101
- Admin Dashboard
102
- </MobileMenuItemText>
103
- </RouteLink>
104
- </MobileMenuItem>
85
+ ? <MenuItem component={RouteLink} to={AdminDashboardRoutes.MAIN}>
86
+ <MenuItemText>
87
+ <AdminPanelSettingsIcon sx={itemIcon} />
88
+ Admin Dashboard
89
+ </MenuItemText>
90
+ </MenuItem>
105
91
  : null
106
92
  }
107
- <MobileMenuItem>
108
- <LogoutForm>
109
- <MobileMenuItemText>
93
+ <MenuItem onClick={() => logoutFormRef.current?.submit()}>
94
+ <LogoutForm ref={logoutFormRef}>
95
+ <MenuItemText>
110
96
  <LogoutIcon sx={itemIcon} />
111
97
  Log Out
112
- </MobileMenuItemText>
98
+ </MenuItemText>
113
99
  </LogoutForm>
114
- </MobileMenuItem>
100
+ </MenuItem>
115
101
  </AccordionDetails>
116
102
  </Accordion>;
117
103
  };
@@ -125,63 +111,51 @@ export const MobileMenuContent: FunctionComponent = () => {
125
111
  user ? (
126
112
  <MobileUserAvatar />
127
113
  ) : (
128
- <MobileMenuItem>
129
- <LoginComponent
130
- loginProviders={loginProviders}
131
- renderButton={(href, onClick) => {
132
- return (<Link href={href} onClick={onClick}>
133
- <MobileMenuItemText>
134
- <AccountBoxIcon sx={itemIcon} />
135
- Log In
136
- </MobileMenuItemText>
137
- </Link>);
138
- }}
139
- />
140
- </MobileMenuItem>
114
+ <LoginComponent
115
+ loginProviders={loginProviders}
116
+ renderButton={(href, onClick) => (
117
+ <MenuItem component={Link} href={href} onClick={onClick}>
118
+ <MenuItemText>
119
+ <AccountBoxIcon sx={itemIcon} />
120
+ Log In
121
+ </MenuItemText>
122
+ </MenuItem>
123
+ )}
124
+ />
141
125
  )
142
126
  )}
143
127
  {loginProviders && !location.pathname.startsWith(UserSettingsRoutes.ROOT) && (
144
- <MobileMenuItem>
145
- <RouteLink to='/user-settings/extensions'>
146
- <MobileMenuItemText>
147
- <PublishIcon sx={itemIcon} />
148
- Publish Extension
149
- </MobileMenuItemText>
150
- </RouteLink>
151
- </MobileMenuItem>
128
+ <MenuItem component={RouteLink} to='/user-settings/extensions'>
129
+ <MenuItemText>
130
+ <PublishIcon sx={itemIcon} />
131
+ Publish Extension
132
+ </MenuItemText>
133
+ </MenuItem>
152
134
  )}
153
- <MobileMenuItem>
154
- <Link target='_blank' href='https://github.com/eclipse/openvsx'>
155
- <MobileMenuItemText>
156
- <GitHubIcon sx={itemIcon} />
157
- Source Code
158
- </MobileMenuItemText>
159
- </Link>
160
- </MobileMenuItem>
161
- <MobileMenuItem>
162
- <Link href='https://github.com/eclipse/openvsx/wiki'>
163
- <MobileMenuItemText>
164
- <MenuBookIcon sx={itemIcon} />
165
- Documentation
166
- </MobileMenuItemText>
167
- </Link>
168
- </MobileMenuItem>
169
- <MobileMenuItem>
170
- <Link href='https://join.slack.com/t/openvsxworkinggroup/shared_invite/zt-2y07y1ggy-ct3IfJljjGI6xWUQ9llv6A'>
171
- <MobileMenuItemText>
172
- <ForumIcon sx={itemIcon} />
173
- Slack Workspace
174
- </MobileMenuItemText>
175
- </Link>
176
- </MobileMenuItem>
177
- <MobileMenuItem>
178
- <RouteLink to='/about'>
179
- <MobileMenuItemText>
180
- <InfoIcon sx={itemIcon} />
181
- About This Service
182
- </MobileMenuItemText>
183
- </RouteLink>
184
- </MobileMenuItem>
135
+ <MenuItem component={Link} href='https://github.com/eclipse/openvsx' target='_blank'>
136
+ <MenuItemText>
137
+ <GitHubIcon sx={itemIcon} />
138
+ Source Code
139
+ </MenuItemText>
140
+ </MenuItem>
141
+ <MenuItem component={Link} href='https://github.com/eclipse/openvsx/wiki'>
142
+ <MenuItemText>
143
+ <MenuBookIcon sx={itemIcon} />
144
+ Documentation
145
+ </MenuItemText>
146
+ </MenuItem>
147
+ <MenuItem component={Link} href='https://join.slack.com/t/openvsxworkinggroup/shared_invite/zt-2y07y1ggy-ct3IfJljjGI6xWUQ9llv6A'>
148
+ <MenuItemText>
149
+ <ForumIcon sx={itemIcon} />
150
+ Slack Workspace
151
+ </MenuItemText>
152
+ </MenuItem>
153
+ <MenuItem component={RouteLink} to='/about'>
154
+ <MenuItemText>
155
+ <InfoIcon sx={itemIcon} />
156
+ About This Service
157
+ </MenuItemText>
158
+ </MenuItem>
185
159
  </>;
186
160
  };
187
161
 
@@ -397,7 +397,7 @@ export class ExtensionRegistryService {
397
397
  payload: extensionPackage,
398
398
  headers: headers,
399
399
  endpoint: createAbsoluteURL([this.serverUrl, 'api', 'user', 'publish'])
400
- });
400
+ }, false); // do not retry publishing an extension but show the explicit error received
401
401
  }
402
402
 
403
403
  async createNamespace(abortController: AbortController, name: string): Promise<Readonly<SuccessResult | ErrorResult>> {
@@ -180,6 +180,8 @@ export interface PersonalAccessToken {
180
180
  value?: string;
181
181
  createdTimestamp: TimestampString;
182
182
  accessedTimestamp?: TimestampString;
183
+ expiresTimestamp?: TimestampString;
184
+ notified?: boolean;
183
185
  description: string;
184
186
  deleteTokenUrl: UrlString;
185
187
  }
@@ -0,0 +1,55 @@
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 { useState } from 'react';
15
+
16
+ const getQuery = () => {
17
+ if (typeof window !== 'undefined') {
18
+ return new URLSearchParams(window.location.search);
19
+ }
20
+ return new URLSearchParams();
21
+ };
22
+
23
+ const getQueryStringVal = (key: string): string | null => {
24
+ return getQuery().get(key);
25
+ };
26
+
27
+ const useQueryParam = (
28
+ key: string,
29
+ defaultVal: string
30
+ ): [string, (val: string) => void] => {
31
+ const [query, setQuery] = useState(getQueryStringVal(key) || defaultVal);
32
+
33
+ const updateUrl = (newVal: string) => {
34
+ setQuery(newVal);
35
+
36
+ const query = getQuery();
37
+
38
+ if (newVal.trim() !== '') {
39
+ query.set(key, newVal);
40
+ } else {
41
+ query.delete(key);
42
+ }
43
+
44
+ // This check is necessary if using the hook with Gatsby
45
+ if (typeof window !== 'undefined') {
46
+ const { protocol, pathname, host } = window.location;
47
+ const newUrl = `${protocol}//${host}${pathname}?${query.toString()}`;
48
+ window.history.pushState({}, '', newUrl);
49
+ }
50
+ };
51
+
52
+ return [query, updateUrl];
53
+ };
54
+
55
+ export default useQueryParam;
@@ -34,7 +34,7 @@ import { Customers } from './customers/customers';
34
34
  import { UsageStatsView } from './usage-stats/usage-stats';
35
35
  import { Logs } from './logs/logs';
36
36
  import { LoginComponent } from "../../default/login";
37
- import AccountBoxIcon from "@mui/icons-material/AccountBox";
37
+ import AccountBoxIcon from '@mui/icons-material/AccountBox';
38
38
 
39
39
  export namespace AdminDashboardRoutes {
40
40
  export const ROOT = 'admin-dashboard';
@@ -109,6 +109,7 @@ export const AdminDashboard: FunctionComponent<AdminDashboardProps> = props => {
109
109
  <Routes>
110
110
  <Route path='/namespaces' element={<NamespaceAdmin/>} />
111
111
  <Route path='/extensions' element={<ExtensionAdmin/>} />
112
+ <Route path='/extensions/:namespace/:extension' element={<ExtensionAdmin/>} />
112
113
  <Route path='/publisher' element={<PublisherAdmin/>} />
113
114
  <Route path='/scans' element={<ScanAdmin/>} />
114
115
  <Route path='/tiers' element={<Tiers/>} />
@@ -16,6 +16,7 @@ import { MainContext } from '../../context';
16
16
  import { isError, Extension, TargetPlatformVersion } from '../../extension-registry-types';
17
17
  import { ExtensionVersionContainer } from './extension-version-container';
18
18
  import { StyledInput } from './namespace-input';
19
+ import useQueryParam from '../../hooks/scan-admin/use-query-params-state';
19
20
 
20
21
  export const ExtensionAdmin: FunctionComponent = props => {
21
22
  const abortController = useRef<AbortController>(new AbortController());
@@ -27,12 +28,12 @@ export const ExtensionAdmin: FunctionComponent = props => {
27
28
 
28
29
  const [loading, setLoading] = useState(false);
29
30
 
30
- const [extensionValue, setExtensionValue] = useState('');
31
+ const [extensionValue, setExtensionValue] = useQueryParam('extension', '');
31
32
  const handleExtensionChange = (value: string) => {
32
33
  setExtensionValue(value);
33
34
  };
34
35
 
35
- const [namespaceValue, setNamespaceValue] = useState('');
36
+ const [namespaceValue, setNamespaceValue] = useQueryParam('namespace', '');
36
37
  const handleNamespaceChange = (value: string) => {
37
38
  setNamespaceValue(value);
38
39
  };
@@ -93,6 +94,7 @@ export const ExtensionAdmin: FunctionComponent = props => {
93
94
  error={namespaceFieldError}
94
95
  key='nsi'
95
96
  onChange={handleNamespaceChange}
97
+ value={namespaceValue}
96
98
  hideIconButton={true}
97
99
  autoFocus={true} />,
98
100
  <ExtensionListSearchfield
@@ -16,6 +16,7 @@ import { MainContext } from '../../context';
16
16
  interface InputProps {
17
17
  onSubmit?: (inputValue: string) => void;
18
18
  onChange: (inputValue: string) => void;
19
+ value?: string;
19
20
  hideIconButton?: boolean;
20
21
  error?: boolean;
21
22
  autoFocus?: boolean;
@@ -23,7 +24,7 @@ interface InputProps {
23
24
  }
24
25
 
25
26
  export const StyledInput: FunctionComponent<InputProps> = props => {
26
- const [inputValue, setInputValue] = useState('');
27
+ const [inputValue, setInputValue] = useState(props.value || '');
27
28
  const { pageSettings } = useContext(MainContext);
28
29
  const onChangeInputValue = (ev: ChangeEvent<HTMLInputElement>) => {
29
30
  const inputValue = ev.target.value;
@@ -53,6 +54,7 @@ export const StyledInput: FunctionComponent<InputProps> = props => {
53
54
  sx={{ flex: 1, pl: 1 }}
54
55
  placeholder={props.placeholder}
55
56
  onChange={onChangeInputValue}
57
+ value={inputValue}
56
58
  onKeyDown={(e: KeyboardEvent) => {
57
59
  if (e.key === 'Enter' && props.onSubmit) {
58
60
  props.onSubmit(inputValue);
@@ -46,7 +46,7 @@ export const ExtensionDetailOverview: FunctionComponent<ExtensionDetailOverviewP
46
46
  name: getEngineDisplayName(engine),
47
47
  version: engines[engine]
48
48
  }))
49
- .filter((d) => d.name != null);
49
+ .filter((d) => d.name); // only display engines with a known display name
50
50
 
51
51
  return (<>
52
52
  <Grid item xs='auto'>
@@ -27,11 +27,7 @@ export const ExtensionReviewDialog: FunctionComponent<ExtensionReviewDialogProps
27
27
  const abortController = useRef<AbortController>(new AbortController());
28
28
 
29
29
  useEffect(() => {
30
- document.addEventListener('keydown', handleEnter);
31
- return () => {
32
- abortController.current.abort();
33
- document.removeEventListener('keydown', handleEnter);
34
- };
30
+ return () => abortController.current.abort();
35
31
  }, []);
36
32
 
37
33
  const handleOpenButton = () => {
@@ -70,12 +66,6 @@ export const ExtensionReviewDialog: FunctionComponent<ExtensionReviewDialogProps
70
66
  setCommentError(commentError);
71
67
  };
72
68
 
73
- const handleEnter = (e: KeyboardEvent) => {
74
- if (e.code === 'Enter') {
75
- handlePost();
76
- }
77
- };
78
-
79
69
  if (!context.user) {
80
70
  return null;
81
71
  }
@@ -109,7 +99,7 @@ export const ExtensionReviewDialog: FunctionComponent<ExtensionReviewDialogProps
109
99
  helperText={commentError}
110
100
  onChange={handleCommentChange} />
111
101
  </DialogContent>
112
- <DialogActions sx={{ justifyContent: { xs: 'center', sm: 'normal', md: 'normal', lg: 'normal', xl: 'normal' } }}>
102
+ <DialogActions sx={{ justifyContent: { xs: 'center', sm: 'end', md: 'end', lg: 'end', xl: 'end' } }}>
113
103
  <Button
114
104
  onClick={handleCancel}
115
105
  color='secondary' >
@@ -9,7 +9,6 @@
9
9
  ********************************************************************************/
10
10
 
11
11
  import { FunctionComponent, useContext, useRef, useState } from 'react';
12
- import { styled } from '@mui/material/styles';
13
12
  import { Avatar, Menu, Typography, MenuItem, Link, Divider, IconButton } from '@mui/material';
14
13
  import { Link as RouteLink } from 'react-router-dom';
15
14
  import { UserSettingsRoutes } from './user-settings';
@@ -17,18 +16,12 @@ import { AdminDashboardRoutes } from '../admin-dashboard/admin-dashboard';
17
16
  import { MainContext } from '../../context';
18
17
  import { LogoutForm } from './logout';
19
18
 
20
- const AvatarRouteLink = styled(RouteLink)({
21
- cursor: 'pointer',
22
- textDecoration: 'none'
23
- });
24
-
25
- const AvatarMenuItem = styled(MenuItem)({ cursor: 'auto' });
26
-
27
19
 
28
20
  export const UserAvatar: FunctionComponent = () => {
29
21
  const [open, setOpen] = useState<boolean>(false);
30
22
  const context = useContext(MainContext);
31
23
  const avatarButton = useRef<any>();
24
+ const logoutFormRef = useRef<HTMLFormElement>(null);
32
25
 
33
26
  const handleAvatarClick = () => {
34
27
  setOpen(!open);
@@ -60,43 +53,45 @@ export const UserAvatar: FunctionComponent = () => {
60
53
  anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
61
54
  transformOrigin={{ vertical: 'top', horizontal: 'right' }}
62
55
  onClose={handleClose} >
63
- <AvatarMenuItem>
64
- <Link href={user.homepage} underline='hover'>
65
- <Typography variant='body2' color='text.primary'>
66
- Logged in as
67
- </Typography>
68
- <Typography variant='overline' color='text.primary'>
69
- {user.loginName}
70
- </Typography>
71
- </Link>
72
- </AvatarMenuItem>
56
+ <MenuItem
57
+ component={Link}
58
+ href={user.homepage}
59
+ sx={{
60
+ display: 'block',
61
+ '&:hover': {
62
+ textDecoration: 'underline'
63
+ }
64
+ }} >
65
+ <Typography variant='body2' color='text.primary' sx={{ mr: 1 }}>
66
+ Logged in as
67
+ </Typography>
68
+ <Typography variant='overline' color='text.primary'>
69
+ {user.loginName}
70
+ </Typography>
71
+ </MenuItem>
73
72
  <Divider />
74
- <AvatarMenuItem>
75
- <AvatarRouteLink onClick={handleClose} to={UserSettingsRoutes.PROFILE}>
76
- <Typography variant='button' color='text.primary'>
77
- Settings
78
- </Typography>
79
- </AvatarRouteLink>
80
- </AvatarMenuItem>
73
+ <MenuItem component={RouteLink} to={UserSettingsRoutes.PROFILE} onClick={handleClose}>
74
+ <Typography variant='button' color='text.primary'>
75
+ Settings
76
+ </Typography>
77
+ </MenuItem>
81
78
  {
82
79
  user.role && user.role === 'admin' ?
83
- <AvatarMenuItem>
84
- <AvatarRouteLink onClick={handleClose} to={AdminDashboardRoutes.MAIN}>
85
- <Typography variant='button' color='text.primary'>
86
- Admin Dashboard
87
- </Typography>
88
- </AvatarRouteLink>
89
- </AvatarMenuItem>
80
+ <MenuItem component={RouteLink} to={AdminDashboardRoutes.MAIN} onClick={handleClose}>
81
+ <Typography variant='button' color='text.primary'>
82
+ Admin Dashboard
83
+ </Typography>
84
+ </MenuItem>
90
85
  :
91
86
  ''
92
87
  }
93
- <AvatarMenuItem>
94
- <LogoutForm>
88
+ <MenuItem onClick={() => logoutFormRef.current?.submit()}>
89
+ <LogoutForm ref={logoutFormRef}>
95
90
  <Typography variant='button' sx={{ color: 'primary.dark' }}>
96
91
  Log Out
97
92
  </Typography>
98
93
  </LogoutForm>
99
- </AvatarMenuItem>
94
+ </MenuItem>
100
95
  </Menu>
101
96
  </>;
102
97
  };