@balena/ui-shared-components 12.4.0-build-secure-boot-7e293588c1dc8956140c5f826aae02c456cd7887-1 → 13.0.0-build-remove-role-inheritance-cb07df8e4c7bad3712464f6cef505f1a18fed75f-1
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/dist/components/DownloadImageDialog/ApplicationInstructions.d.ts +0 -1
- package/dist/components/DownloadImageDialog/ApplicationInstructions.js +33 -26
- package/dist/components/DownloadImageDialog/ImageForm.d.ts +1 -3
- package/dist/components/DownloadImageDialog/ImageForm.js +125 -161
- package/dist/components/DownloadImageDialog/index.d.ts +0 -1
- package/dist/components/DownloadImageDialog/index.js +6 -12
- package/dist/components/RJST/Actions/Create.js +1 -1
- package/dist/components/RJST/Actions/Update.js +6 -2
- package/dist/components/RJST/models/example.d.ts +1 -1
- package/dist/components/RJST/models/example.js +1 -1
- package/dist/components/RJST/models/helpers.d.ts +1 -1
- package/dist/components/RJST/models/helpers.js +2 -2
- package/dist/components/RJST/schemaOps.d.ts +2 -2
- package/dist/components/RJST/utils.d.ts +1 -1
- package/dist/components/RJST/utils.js +8 -8
- package/package.json +3 -4
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import has from 'lodash/has';
|
|
2
3
|
import { interpolateMustache } from './utils';
|
|
3
4
|
import { Box, List, Tab, Tabs, Typography } from '@mui/material';
|
|
4
5
|
import { memo, useEffect, useMemo, useState } from 'react';
|
|
5
6
|
import { MUILinkWithTracking } from '../MUILinkWithTracking';
|
|
6
7
|
import { OrderedListItem } from '../OrderedListItem';
|
|
7
8
|
import { Markdown } from '../Markdown';
|
|
8
|
-
import { Chip } from '../Chip';
|
|
9
|
-
import { token } from '../../utils/token';
|
|
10
9
|
export const getUserOs = () => {
|
|
11
10
|
const platform = window.navigator.platform.toLowerCase();
|
|
12
11
|
if (platform.includes('win')) {
|
|
@@ -20,13 +19,12 @@ export const getUserOs = () => {
|
|
|
20
19
|
}
|
|
21
20
|
return 'Unknown';
|
|
22
21
|
};
|
|
23
|
-
const SECURE_BOOT_INSTRUCTION_TO_REPLACE_START = 'Press the F10 key while BIOS';
|
|
24
22
|
const dtJsonTocontractOsKeyMap = {
|
|
25
23
|
windows: 'Windows',
|
|
26
24
|
osx: 'MacOS',
|
|
27
25
|
linux: 'Linux',
|
|
28
26
|
};
|
|
29
|
-
export const ApplicationInstructions = memo(function ApplicationInstructions({ deviceType, templateData,
|
|
27
|
+
export const ApplicationInstructions = memo(function ApplicationInstructions({ deviceType, templateData, }) {
|
|
30
28
|
var _a;
|
|
31
29
|
const [currentOs, setCurrentOs] = useState(getUserOs());
|
|
32
30
|
const instructions = useMemo(() => {
|
|
@@ -66,28 +64,37 @@ export const ApplicationInstructions = memo(function ApplicationInstructions({ d
|
|
|
66
64
|
setCurrentOs(value !== null && value !== void 0 ? value : 'Unknown');
|
|
67
65
|
}, "aria-label": "os tabs", children: Object.keys(instructions).map((os) => {
|
|
68
66
|
return _jsx(Tab, { label: os, value: os }, os);
|
|
69
|
-
}) }) }) })), _jsx(InstructionsList, { instructions: finalInstructions
|
|
67
|
+
}) }) }) })), _jsx(InstructionsList, { instructions: finalInstructions }), _jsx(Box, { mt: 2, children: _jsxs(Typography, { children: ["For more details please refer to our", ' ', _jsx(MUILinkWithTracking, { href: `https://www.balena.io/docs/learn/getting-started/${deviceType.slug}/nodejs/`, children: "Getting Started Guide" }), "."] }) })] }));
|
|
70
68
|
});
|
|
71
|
-
const InstructionsItem = ({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
69
|
+
const InstructionsItem = ({ node, index }) => {
|
|
70
|
+
const hasChildren = has(node, 'children');
|
|
71
|
+
let text = null;
|
|
72
|
+
if (typeof node === 'string') {
|
|
73
|
+
text = node;
|
|
74
|
+
}
|
|
75
|
+
if (node === null || node === void 0 ? void 0 : node.text) {
|
|
76
|
+
text = node.text;
|
|
77
|
+
}
|
|
78
|
+
return (_jsxs(OrderedListItem, { index: index + 1, sx: { maxWidth: '100%' }, children: [_jsx(Markdown, { components: {
|
|
79
|
+
code: (codeProps) => {
|
|
80
|
+
return (_jsx("code", Object.assign({ style: {
|
|
81
|
+
maxWidth: '100%',
|
|
82
|
+
display: 'inline-block',
|
|
83
|
+
whiteSpace: 'normal',
|
|
84
|
+
wordBreak: 'break-all',
|
|
85
|
+
wordWrap: 'break-word',
|
|
86
|
+
} }, codeProps)));
|
|
87
|
+
},
|
|
88
|
+
br: () => {
|
|
89
|
+
return _jsx("p", {});
|
|
90
|
+
},
|
|
91
|
+
p: ({ children }) => (_jsx("p", { style: { marginTop: 0, marginBottom: 0 }, children: children })),
|
|
92
|
+
}, children: text }), hasChildren && (_jsx(List, { children: node.children.map((item, i) => {
|
|
93
|
+
return _jsx(InstructionsItem, { node: item, index: i }, i);
|
|
94
|
+
}) }))] }));
|
|
88
95
|
};
|
|
89
|
-
const InstructionsList = ({ instructions
|
|
90
|
-
return (_jsx(List, { children: instructions.map((
|
|
91
|
-
return
|
|
96
|
+
const InstructionsList = ({ instructions }) => {
|
|
97
|
+
return (_jsx(List, { children: instructions.map((item, i) => {
|
|
98
|
+
return _jsx(InstructionsItem, { node: item, index: i }, `${item}_${i}`);
|
|
92
99
|
}) }));
|
|
93
100
|
};
|
|
@@ -14,9 +14,7 @@ interface ImageFormProps {
|
|
|
14
14
|
model: DownloadImageFormModel;
|
|
15
15
|
hasEsrVersions?: boolean;
|
|
16
16
|
onSelectedOsTypeChange: (osType: string) => void;
|
|
17
|
-
onChange: (
|
|
17
|
+
onChange: (obj: Partial<DownloadImageFormModel>) => void;
|
|
18
18
|
}
|
|
19
|
-
export declare const GENERIC_X86_SLUG = "generic-amd64";
|
|
20
|
-
export declare const GENERIC_X86_MINIMUM_SUPPORTED_SECUREBOOT_VERSION = "5.3.15";
|
|
21
19
|
export declare const ImageForm: import("react").NamedExoticComponent<ImageFormProps>;
|
|
22
20
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __rest } from "tslib";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { Avatar, Box, Checkbox, Chip, Divider, FormControl, FormControlLabel, FormLabel, InputAdornment, Radio, RadioGroup, TextField, Tooltip, Typography, IconButton, Autocomplete, Stack, Accordion, AccordionSummary, AccordionDetails, accordionSummaryClasses,
|
|
3
|
+
import { Avatar, Box, Checkbox, Chip, Divider, FormControl, FormControlLabel, FormLabel, InputAdornment, Radio, RadioGroup, TextField, Tooltip, Typography, IconButton, Autocomplete, Stack, Accordion, AccordionSummary, AccordionDetails, accordionSummaryClasses, } from '@mui/material';
|
|
4
4
|
import HelpIcon from '@mui/icons-material/Help';
|
|
5
5
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
6
|
import { getPreferredVersionOpts, transformVersions } from './version';
|
|
@@ -12,14 +12,9 @@ import { Visibility, VisibilityOff } from '@mui/icons-material';
|
|
|
12
12
|
import { FALLBACK_LOGO_UNKNOWN_DEVICE } from './utils';
|
|
13
13
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
14
14
|
import { faChevronRight, faTriangleExclamation, } from '@fortawesome/free-solid-svg-icons';
|
|
15
|
-
import * as semver from 'balena-semver';
|
|
16
15
|
import { Callout } from '../Callout';
|
|
17
|
-
import { getFromLocalStorage, setToLocalStorage } from '../../utils/storage';
|
|
18
|
-
import { NewChip } from '../NewChip';
|
|
19
|
-
import { useAnalyticsContext } from '../../contexts/AnalyticsContext';
|
|
20
16
|
import { token } from '../../utils/token';
|
|
21
17
|
const POLL_INTERVAL_DOCS = 'https://www.balena.io/docs/reference/supervisor/bandwidth-reduction/#side-effects--warnings';
|
|
22
|
-
const SECURE_BOOT_AND_FULL_DISK_ENCRYPTION_DOCS = 'https://docs.balena.io/reference/OS/secure-boot-and-full-disk-encryption/overview/';
|
|
23
18
|
const getCategorizedVersions = (deviceTypeOsVersions, deviceType, osType) => {
|
|
24
19
|
var _a;
|
|
25
20
|
const osVersions = (_a = deviceTypeOsVersions[deviceType.slug]) !== null && _a !== void 0 ? _a : [];
|
|
@@ -39,14 +34,9 @@ const lineMap = {
|
|
|
39
34
|
sunset: 'yellow',
|
|
40
35
|
outdated: 'red',
|
|
41
36
|
};
|
|
42
|
-
export const GENERIC_X86_SLUG = 'generic-amd64';
|
|
43
|
-
export const GENERIC_X86_MINIMUM_SUPPORTED_SECUREBOOT_VERSION = '5.3.15';
|
|
44
37
|
export const ImageForm = memo(function ImageForm({ compatibleDeviceTypes, osVersions, isInitialDefault, osType, osTypes, hasEsrVersions, model, formElement, downloadUrl, applicationId, authToken, onSelectedOsTypeChange, onChange, }) {
|
|
45
38
|
var _a, _b;
|
|
46
|
-
const { state } = useAnalyticsContext();
|
|
47
39
|
const [showAdvancedSettings, setShowAdvancedSettings] = useState(false);
|
|
48
|
-
const [showSecureBootConfirmationDialog, setShowSecureBootConfirmationDialog,] = useState(false);
|
|
49
|
-
const [dontShowSecureBootWarningAgain, setDontShowSecureBootWarningAgain] = useState(false);
|
|
50
40
|
const [showPassword, setShowPassword] = useState(false);
|
|
51
41
|
const [version, setVersion] = useState();
|
|
52
42
|
const [variant, setVariant] = useState('prod');
|
|
@@ -54,36 +44,42 @@ export const ImageForm = memo(function ImageForm({ compatibleDeviceTypes, osVers
|
|
|
54
44
|
const { selectionOpts, preferredSelectionOpts } = useMemo(() => getCategorizedVersions(osVersions, model.deviceType, osType), [osVersions, model.deviceType, osType]);
|
|
55
45
|
const versionSelectionOpts = useMemo(() => (showAllVersions ? selectionOpts : preferredSelectionOpts), [preferredSelectionOpts, selectionOpts, showAllVersions]);
|
|
56
46
|
const showAllVersionsToggle = useMemo(() => preferredSelectionOpts.length < selectionOpts.length, [preferredSelectionOpts.length, selectionOpts.length]);
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
47
|
+
const handleVariantChange = useCallback((newVariant) => {
|
|
48
|
+
setVariant(newVariant);
|
|
49
|
+
const newState = {
|
|
50
|
+
developmentMode: newVariant === 'dev',
|
|
51
|
+
};
|
|
52
|
+
// For non-unified OS releases (ones w/ separate prod & dev releases)
|
|
53
|
+
// we also need to update raw version string, since it's different
|
|
54
|
+
// depending on the variant selected.
|
|
55
|
+
if (version === null || version === void 0 ? void 0 : version.hasPrebuiltVariants) {
|
|
56
|
+
const rawVersionForVariant = version.rawVersions[newVariant];
|
|
57
|
+
if (rawVersionForVariant != null) {
|
|
58
|
+
newState.version = rawVersionForVariant;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
onChange(newState);
|
|
62
|
+
}, [onChange, version]);
|
|
72
63
|
const handleVersionChange = useCallback((ver) => {
|
|
73
64
|
var _a;
|
|
74
65
|
ver !== null && ver !== void 0 ? ver : (ver = (_a = versionSelectionOpts.find((v) => v.isRecommended)) !== null && _a !== void 0 ? _a : versionSelectionOpts[0]);
|
|
66
|
+
const newState = {
|
|
67
|
+
developmentMode: variant === 'dev',
|
|
68
|
+
};
|
|
75
69
|
if (ver === null || ver === void 0 ? void 0 : ver.hasPrebuiltVariants) {
|
|
70
|
+
// For non-unified OS releases (ones w/ separate prod & dev releases)
|
|
71
|
+
// we need to set the correct raw version string based on the selected variant.
|
|
76
72
|
const rawVersionForVariant = ver.rawVersions[variant];
|
|
77
|
-
if (rawVersionForVariant) {
|
|
78
|
-
onChange('version', rawVersionForVariant);
|
|
79
|
-
setVersion(ver);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
73
|
+
if (!rawVersionForVariant) {
|
|
82
74
|
handleVariantChange(variant === 'dev' ? 'prod' : 'dev');
|
|
75
|
+
return;
|
|
83
76
|
}
|
|
84
|
-
|
|
77
|
+
newState.version = rawVersionForVariant;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
newState.version = ver === null || ver === void 0 ? void 0 : ver.rawVersion;
|
|
85
81
|
}
|
|
86
|
-
onChange(
|
|
82
|
+
onChange(newState);
|
|
87
83
|
setVersion(ver);
|
|
88
84
|
}, [versionSelectionOpts, variant, onChange, handleVariantChange]);
|
|
89
85
|
// TODO: Revisit this as it is clearly not using hooks as intended
|
|
@@ -115,138 +111,106 @@ export const ImageForm = memo(function ImageForm({ compatibleDeviceTypes, osVers
|
|
|
115
111
|
if (!newDeviceType) {
|
|
116
112
|
return;
|
|
117
113
|
}
|
|
118
|
-
onChange(
|
|
114
|
+
onChange({ deviceType: newDeviceType });
|
|
119
115
|
}, [compatibleDeviceTypes, model.deviceType.slug, onChange]);
|
|
120
116
|
const recommendedVersion = useMemo(() => { var _a; return (_a = versionSelectionOpts.find((v) => { var _a; return !((_a = v.knownIssueList) === null || _a === void 0 ? void 0 : _a.length); })) === null || _a === void 0 ? void 0 : _a.value; }, [versionSelectionOpts]);
|
|
121
|
-
return (_jsxs(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}, onChange: (event) => {
|
|
174
|
-
onChange('wifiKey', event.target.value);
|
|
175
|
-
}, label: "Wifi Passphrase" })] }))] }), supportsSecureBoot && (_jsxs(_Fragment, { children: [_jsx(Divider, { variant: "fullWidth", sx: { my: 3, borderStyle: 'dashed' } }), _jsxs(FormControl, { children: [_jsx(FormLabel, { id: "secure-boot-and-full-disk-encryption-label", children: _jsxs(Stack, { direction: "row", alignItems: "center", gap: 1, children: [_jsx(Typography, { variant: "titleSm", children: "Secure Boot and Full Disk Encryption" }), _jsx(NewChip, { expiryTimestamp: "2025-06-01" })] }) }), _jsx(FormControlLabel, { control: _jsx(Switch, { onClick: (event) => {
|
|
176
|
-
if (!model.secureboot &&
|
|
177
|
-
!getFromLocalStorage(secureBootDontShowAgainKey)) {
|
|
117
|
+
return (_jsxs(Stack, { action: downloadUrl, method: "post", component: "form", noValidate: true, autoComplete: "off", ref: formElement, gap: 3, children: [_jsx("input", { type: "hidden", name: "deviceType", value: model.deviceType.slug }), _jsx("input", { type: "hidden", name: "_token", value: authToken }), _jsx("input", { type: "hidden", name: "appId", value: applicationId }), _jsx("input", { type: "hidden", name: "fileType", value: ".zip" }), _jsx("input", { type: "hidden", name: "version", value: model.version }), _jsxs(Stack, { direction: "row", flexWrap: "wrap", gap: 2, children: [compatibleDeviceTypes && compatibleDeviceTypes.length > 1 && (_jsx(Autocomplete, { fullWidth: true, value: model.deviceType, options: compatibleDeviceTypes, getOptionLabel: (option) => option.name, renderOption: (props, option) => {
|
|
118
|
+
var _a;
|
|
119
|
+
return (_jsxs(Box, Object.assign({ component: "li" }, props, { children: [_jsx(Avatar, { variant: "square", src: (_a = option.logo) !== null && _a !== void 0 ? _a : FALLBACK_LOGO_UNKNOWN_DEVICE, sx: { mr: 3, width: '20px', height: '20px' } }), _jsx(Typography, { noWrap: true, children: option.name })] })));
|
|
120
|
+
}, renderInput: (_a) => {
|
|
121
|
+
var _b;
|
|
122
|
+
var { InputProps } = _a, params = __rest(_a, ["InputProps"]);
|
|
123
|
+
return (_jsx(TextField, Object.assign({}, params, { label: _jsxs(Stack, { direction: "row", alignItems: "center", gap: 1, children: ["Device type", _jsx(Tooltip, { title: "Applications can support any devices that share the same architecture as their default device type.", children: _jsx(HelpIcon, { color: "info", sx: { fontSize: '1rem' } }) })] }), slotProps: {
|
|
124
|
+
input: Object.assign(Object.assign({}, InputProps), { startAdornment: (_jsx(Avatar, { variant: "square", src: (_b = model.deviceType.logo) !== null && _b !== void 0 ? _b : FALLBACK_LOGO_UNKNOWN_DEVICE, sx: { mr: 3, width: '20px', height: '20px' } })) }),
|
|
125
|
+
} })));
|
|
126
|
+
}, onChange: (_event, value) => {
|
|
127
|
+
if (!value) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
handleSelectedDeviceTypeChange(value);
|
|
131
|
+
}, disableClearable: true,
|
|
132
|
+
// TODO: consider whether there is a better solution than letting the width vary as you search
|
|
133
|
+
slotProps: {
|
|
134
|
+
popper: { sx: { width: 'fit-content' } },
|
|
135
|
+
}, sx: { flex: 1 } })), (!isInitialDefault || osType) &&
|
|
136
|
+
hasEsrVersions &&
|
|
137
|
+
model.deviceType && (_jsx(OsTypeSelector, { supportedOsTypes: osTypes, hasEsrVersions: hasEsrVersions !== null && hasEsrVersions !== void 0 ? hasEsrVersions : false, selectedOsTypeSlug: osType, onSelectedOsTypeChange: onSelectedOsTypeChange }))] }), !isInitialDefault && version && (_jsxs(Stack, { direction: "row", flexWrap: "wrap", maxWidth: "100%", gap: 2, alignItems: "center", children: [_jsx(Autocomplete, { fullWidth: true, id: "e2e-download-image-versions-list", value: version, getOptionLabel: (option) => option.value, isOptionEqualToValue: (option, value) => option.value === value.value, options: versionSelectionOpts, onChange: (_event, ver) => {
|
|
138
|
+
handleVersionChange(ver);
|
|
139
|
+
}, placeholder: "Choose a version...", renderOption: (props, option) => (_jsx(Box, Object.assign({ component: "li" }, props, { children: _jsx(VersionSelectItem, { option: option, isRecommended: option.value === recommendedVersion }) }))), renderInput: (_a) => {
|
|
140
|
+
var { InputProps } = _a, params = __rest(_a, ["InputProps"]);
|
|
141
|
+
return (_jsx(TextField, Object.assign({}, params, { slotProps: {
|
|
142
|
+
input: Object.assign(Object.assign({}, InputProps), { endAdornment: (_jsxs(_Fragment, { children: [version.value === recommendedVersion && (_jsx(Chip, { sx: { ml: 1 }, color: "green", label: "recommended" })), !!(version === null || version === void 0 ? void 0 : version.knownIssueList) && (_jsx(Tooltip, { title: version.knownIssueList, children: _jsx(FontAwesomeIcon, { icon: faTriangleExclamation, color: token('color.icon.warning') }) })), InputProps.endAdornment] })) }),
|
|
143
|
+
}, label: "OS version" })));
|
|
144
|
+
}, disableClearable: true, sx: { flex: 1 } }), showAllVersionsToggle && (_jsx(FormControlLabel, { control: _jsx(Checkbox, { id: "e2e-show-all-versions-check", checked: showAllVersions, onChange: handleShowAllVersions }), label: "Show outdated versions",
|
|
145
|
+
// TODO: Find a better way to center the checkbox with the input only (without the label)
|
|
146
|
+
sx: { mt: 3 } }))] })), _jsx(Divider, { variant: "fullWidth", flexItem: true, sx: { borderStyle: 'dashed' } }), (!isInitialDefault || !variant) && (_jsx(VariantSelector, { version: version, variant: variant, onVariantChange: (v) => {
|
|
147
|
+
handleVariantChange(v ? 'dev' : 'prod');
|
|
148
|
+
} })), _jsx(Divider, { variant: "fullWidth", flexItem: true, sx: { borderStyle: 'dashed' } }), _jsxs(Stack, { children: [_jsxs(FormControl, { children: [_jsx(FormLabel, { id: "network-radio-buttons-group-label", children: _jsx(Typography, { variant: "titleSm", children: "Network" }) }), _jsxs(RadioGroup, { "aria-labelledby": "network-radio-buttons-group-label", value: model.network, name: "network", onChange: (event) => {
|
|
149
|
+
onChange({
|
|
150
|
+
network: event.target
|
|
151
|
+
.value,
|
|
152
|
+
});
|
|
153
|
+
}, children: [_jsx(FormControlLabel, { value: "ethernet", control: _jsx(Radio, {}), label: "Ethernet only" }), _jsx(FormControlLabel, { value: "wifi", control: _jsx(Radio, {}), label: "Wifi + Ethernet" })] })] }), model.network === 'wifi' && (_jsxs(Stack, { gap: 3, children: [_jsx(TextField, { value: model.wifiSsid, id: "device-wifi-ssid", slotProps: {
|
|
154
|
+
htmlInput: {
|
|
155
|
+
name: 'wifiSsid',
|
|
156
|
+
autoComplete: 'wifiSsid-auto-complete',
|
|
157
|
+
},
|
|
158
|
+
}, onChange: (event) => {
|
|
159
|
+
onChange({ wifiSsid: event.target.value });
|
|
160
|
+
}, label: "WiFi SSID" }), _jsx(TextField, { type: showPassword ? 'text' : 'password', id: "device-wifi-password", value: model.wifiKey, slotProps: {
|
|
161
|
+
htmlInput: {
|
|
162
|
+
name: 'wifiKey',
|
|
163
|
+
},
|
|
164
|
+
// input and htmlInput are different https://mui.com/material-ui/api/text-field/#text-field-prop-slotProps
|
|
165
|
+
input: {
|
|
166
|
+
endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { onClick: () => {
|
|
167
|
+
setShowPassword((show) => !show);
|
|
168
|
+
}, onMouseDown: (event) => {
|
|
178
169
|
event.preventDefault();
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
},
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
onChange('provisioningKeyName', event.target.value);
|
|
222
|
-
}, label: "Provisioning Key name" }), _jsx(TextField, { type: "date", value: (_b = model.provisioningKeyExpiryDate) !== null && _b !== void 0 ? _b : '', slotProps: {
|
|
223
|
-
htmlInput: {
|
|
224
|
-
name: 'provisioningKeyExpiryDate',
|
|
225
|
-
autoComplete: 'provisioningKeyExpiryDate-auto-complete',
|
|
226
|
-
},
|
|
227
|
-
}, onChange: (event) => {
|
|
228
|
-
onChange('provisioningKeyExpiryDate', event.target.value);
|
|
229
|
-
}, label: "Provisioning Key expiring on" })] })] })] }), _jsxs(Dialog, { open: showSecureBootConfirmationDialog, onClose: () => {
|
|
230
|
-
dismissSecureBootWarning(false, dontShowSecureBootWarningAgain);
|
|
231
|
-
}, children: [_jsx(DialogTitle, { children: "Enabling Secure Boot and Full Disk Encryption" }), _jsx(DialogContent, { children: _jsxs(Stack, { children: ["Enabling Secure Boot and Full Disk Encryption has important implications:", _jsxs("ul", { children: [_jsxs("li", { children: ["The image you are about to download is", ' ', _jsx(MUILinkWithTracking, { eventProperties: {
|
|
232
|
-
source: "Application Add Device Modal Secure Boot Warning signed with balena's main platform key Link",
|
|
233
|
-
}, href: "https://docs.balena.io/reference/OS/secure-boot-and-full-disk-encryption/overview/#keys-and-certificates-in-secure-boot", children: "signed with balena's main platform key" })] }), _jsx("li", { children: "Full Disk Encryption (FDE): All data on the disk will be encrypted with a unique key per device, ensuring that data extraction or retrieval from a powered-off device is impossible." }), _jsxs("li", { children: ["Secure Boot: Ensures only OS images signed by balena can unlock the disks and access data stored on the device. Hardened Mode Limitations:", _jsxs("ul", { children: [_jsx("li", { children: "Unsigned kernel modules cannot be loaded." }), _jsx("li", { children: "Boot parameters cannot be modified." }), _jsx("li", { children: "Debugging early boot processes is practically impossible." })] })] })] }), _jsxs(Typography, { children: ["If you need to load out-of-tree kernel drivers or require a unique signing key,", ' ', _jsx(MUILinkWithTracking, { eventProperties: {
|
|
234
|
-
source: 'Application Add Device Modal Secure Boot Warning Contact Us Link',
|
|
235
|
-
}, href: "mailto:sales@balena.io", target: "_self", children: "contact us" }), ' ', "to discuss your specific requirements."] }), _jsx(MUILinkWithTracking, { eventProperties: {
|
|
236
|
-
source: 'Application Add Device Modal Secure Boot Warning Learn More Link',
|
|
237
|
-
}, href: "https://docs.balena.io/reference/OS/secure-boot-and-full-disk-encryption/overview/", children: "Learn more about Secure Boot and Full Disk Encryption." }), _jsx(FormControlLabel, { control: _jsx(Checkbox, { onChange: (event) => {
|
|
238
|
-
setDontShowSecureBootWarningAgain(event.target.checked);
|
|
239
|
-
} }), label: "Don't show me this warning again for this device type", sx: { mt: 2 } })] }) }), _jsxs(DialogActions, { children: [_jsx(Button, { "aria-label": "Go back and do not acknowledge Secure Boot and Full Disk Encryption warning", onClick: () => {
|
|
240
|
-
dismissSecureBootWarning(false, dontShowSecureBootWarningAgain);
|
|
241
|
-
}, variant: "outlined", color: "secondary", children: "Cancel" }), _jsx(Button, { "aria-label": "Acknowledge Secure Boot and Full Disk Encryption warning", onClick: () => {
|
|
242
|
-
onChange('secureboot', true);
|
|
243
|
-
if (dontShowSecureBootWarningAgain) {
|
|
244
|
-
setToLocalStorage(secureBootDontShowAgainKey, 'true');
|
|
245
|
-
}
|
|
246
|
-
dismissSecureBootWarning(true, dontShowSecureBootWarningAgain);
|
|
247
|
-
}, children: "I understand and acknowledge" })] })] })] }));
|
|
170
|
+
}, edge: "end", children: showPassword ? _jsx(VisibilityOff, {}) : _jsx(Visibility, {}) }) })),
|
|
171
|
+
},
|
|
172
|
+
}, onChange: (event) => {
|
|
173
|
+
onChange({ wifiKey: event.target.value });
|
|
174
|
+
}, label: "Wifi Passphrase" })] }))] }), _jsx(Divider, { variant: "fullWidth", sx: { borderStyle: 'dashed' } }), _jsxs(Accordion, { disableGutters: true, elevation: 0, expanded: showAdvancedSettings, onChange: () => {
|
|
175
|
+
setShowAdvancedSettings(!showAdvancedSettings);
|
|
176
|
+
}, sx: {
|
|
177
|
+
border: 'none',
|
|
178
|
+
'&::before': {
|
|
179
|
+
display: 'none',
|
|
180
|
+
},
|
|
181
|
+
[`& .${accordionSummaryClasses.expandIconWrapper}.${accordionSummaryClasses.expanded}`]: {
|
|
182
|
+
transform: 'rotate(90deg)',
|
|
183
|
+
},
|
|
184
|
+
}, children: [_jsx(AccordionSummary, { expandIcon: _jsx(FontAwesomeIcon, { icon: faChevronRight }), sx: { flexDirection: 'row-reverse', gap: 2 }, children: _jsx(Typography, { variant: "titleSm", children: "Advanced settings" }) }), _jsxs(AccordionDetails, { sx: { display: 'flex', flexDirection: 'column', gap: 3 }, children: [_jsx(TextField, { value: model.appUpdatePollInterval, slotProps: {
|
|
185
|
+
htmlInput: {
|
|
186
|
+
name: 'appUpdatePollInterval',
|
|
187
|
+
autoComplete: 'appUpdatePollInterval-auto-complete',
|
|
188
|
+
},
|
|
189
|
+
}, onChange: (event) => {
|
|
190
|
+
onChange({
|
|
191
|
+
appUpdatePollInterval: parseInt(event.target.value, 10),
|
|
192
|
+
});
|
|
193
|
+
}, label: _jsxs(Stack, { direction: "row", alignItems: "center", gap: 1, children: ["Check for updates every X minutes", ' ', _jsx(MUILinkWithTracking, { href: POLL_INTERVAL_DOCS, sx: {
|
|
194
|
+
display: 'flex',
|
|
195
|
+
alignItems: 'center',
|
|
196
|
+
height: '1.5rem',
|
|
197
|
+
}, children: _jsx(ArticleIcon, { sx: { ml: 1, fontSize: '1.15rem' } }) })] }) }), _jsx(TextField, { name: "provisioningKeyName", value: (_a = model.provisioningKeyName) !== null && _a !== void 0 ? _a : '', slotProps: {
|
|
198
|
+
htmlInput: {
|
|
199
|
+
name: 'provisioningKeyName',
|
|
200
|
+
autoComplete: 'provisioningKeyName-auto-complete',
|
|
201
|
+
},
|
|
202
|
+
}, onChange: (event) => {
|
|
203
|
+
onChange({ provisioningKeyName: event.target.value });
|
|
204
|
+
}, label: "Provisioning Key name" }), _jsx(TextField, { type: "date", value: (_b = model.provisioningKeyExpiryDate) !== null && _b !== void 0 ? _b : '', slotProps: {
|
|
205
|
+
htmlInput: {
|
|
206
|
+
name: 'provisioningKeyExpiryDate',
|
|
207
|
+
autoComplete: 'provisioningKeyExpiryDate-auto-complete',
|
|
208
|
+
},
|
|
209
|
+
}, onChange: (event) => {
|
|
210
|
+
onChange({ provisioningKeyExpiryDate: event.target.value });
|
|
211
|
+
}, label: "Provisioning Key expiring on" })] })] })] }));
|
|
248
212
|
});
|
|
249
213
|
// TODO: We need a better way than just copying the styling. Consider creating a component to export
|
|
250
214
|
const VersionSelectItem = ({ option, isRecommended, }) => {
|
|
251
|
-
return (_jsxs(Stack, { flexWrap: "wrap", maxWidth: "100%", rowGap: 1, children: [_jsxs(Typography, { noWrap: true, maxWidth: "100%", variant: "titleSm", children: [option.title, !!option.line && (_jsx(Chip, { sx: { ml: 1 }, label: option.line, color: lineMap[option.line] })), isRecommended && (_jsx(Chip, { sx: { ml: 1 }, color: "green", label: "recommended" }))] }), !!option.knownIssueList && (_jsx(Callout, { severity: "warning", size: "sm", children: option.knownIssueList }))] }));
|
|
215
|
+
return (_jsxs(Stack, { direction: "column", flexWrap: "wrap", maxWidth: "100%", rowGap: 1, children: [_jsxs(Typography, { noWrap: true, maxWidth: "100%", variant: "titleSm", children: [option.title, !!option.line && (_jsx(Chip, { sx: { ml: 1 }, label: option.line, color: lineMap[option.line] })), isRecommended && (_jsx(Chip, { sx: { ml: 1 }, color: "green", label: "recommended" }))] }), !!option.knownIssueList && (_jsx(Callout, { severity: "warning", size: "sm", children: option.knownIssueList }))] }));
|
|
252
216
|
};
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { Avatar, DialogActions, DialogContent, Divider, Grid2 as Grid, Stack, Typography, } from '@mui/material';
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { FALLBACK_LOGO_UNKNOWN_DEVICE, isUrlAccessible, stripVersionBuild, } from './utils';
|
|
5
|
-
import {
|
|
5
|
+
import { ImageForm } from './ImageForm';
|
|
6
6
|
import { ApplicationInstructions } from './ApplicationInstructions';
|
|
7
7
|
import { DropDownButton } from '../DropDownButton';
|
|
8
8
|
import DownloadIcon from '@mui/icons-material/Download';
|
|
@@ -15,7 +15,6 @@ import { enqueueSnackbar } from 'notistack';
|
|
|
15
15
|
import { DialogWithCloseButton } from '../DialogWithCloseButton';
|
|
16
16
|
import { Callout } from '../Callout';
|
|
17
17
|
import { Spinner } from '../Spinner';
|
|
18
|
-
import * as semver from 'balena-semver';
|
|
19
18
|
const etcherLogoBase64 = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAwIiBoZWlnaHQ9IjYwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KIDxnPgogIDx0aXRsZT5FdGNoZXI8L3RpdGxlPgogIDxnIGlkPSJzdmdfMSIgc3Ryb2tlPSJudWxsIj4KICAgPHBhdGggaWQ9InN2Z18yIiBjbGFzcz0ic3QxIiBkPSJtNDEyLjkwMzgzLDM1OC4wNjcxM2wwLDE3MS40OTU4M2M3LjQ5MjU0LC0xLjY2NTAxIDE0LjE1MjU3LC0zLjMzMDAyIDIwLjgxMjYsLTcuNDkyNTRsMTQyLjM1ODE5LC04MS41ODUzOWMyMC44MTI2LC0xMS42NTUwNiAzMy4zMDAxNiwtMzQuMTMyNjYgMzMuMzAwMTYsLTU4LjI3NTI4bDAsLTE2Mi4zMzgyOGMwLC02LjY2MDAzIC0wLjgzMjUsLTEzLjMyMDA2IC0zLjMzMDAyLC0xOS4xNDc1OWwtMTU0LjAxMzI0LDg5LjA3NzkzYy0zMi40Njc2NiwyMi40Nzc2MSAtMzkuMTI3NjksNDMuMjkwMjEgLTM5LjEyNzY5LDY4LjI2NTMzbDAsLTAuMDAwMDF6IiBmaWxsPSIjQTVERTM3IiBzdHJva2U9Im51bGwiLz4KICAgPHBhdGggaWQ9InN2Z18zIiBjbGFzcz0ic3QyIiBkPSJtNjYyLjY1NTAzLDE2Ny40MjM3MWwtNTYuNjEwMjcsMzIuNDY3NjZjMS42NjUwMSw1LjgyNzUzIDMuMzMwMDIsMTIuNDg3NTYgMy4zMzAwMiwxOS4xNDc1OWwwLDE2My4xNzA3OWMwLDI0LjE0MjYyIC0xMy4zMjAwNiw0Ni42MjAyMiAtMzMuMzAwMTYsNTguMjc1MjhsLTE0Mi4zNTgxOSw4MS41ODUzOWMtNi42NjAwMywzLjMzMDAyIC0xMy4zMjAwNiw1LjgyNzUzIC0yMC44MTI2LDcuNDkyNTRsMCw2NC45MzUzMWM5Ljk5MDA1LC0xLjY2NTAxIDE5Ljk4MDEsLTQuOTk1MDIgMjguMzA1MTQsLTkuOTkwMDVsMTg0LjgxNTg5LC0xMDUuNzI4MDFjMjUuODA3NjIsLTE0Ljk4NTA3IDQxLjYyNTIsLTQyLjQ1NzcgNDEuNjI1MiwtNzIuNDI3ODVsMCwtMjExLjQ1NjAyYzAsLTkuMTU3NTQgLTEuNjY1MDEsLTE4LjMxNTA5IC00Ljk5NTAyLC0yNy40NzI2M2wtMC4wMDAwMSwweiIgZmlsbD0iI0M4RjE3OCIgc3Ryb2tlPSJudWxsIi8+CiAgIDxwYXRoIGlkPSJzdmdfNCIgY2xhc3M9InN0MSIgZD0ibTM5OS41ODM3NiwzMDMuOTU0MzZjOC4zMjUwNCwtMTMuMzIwMDYgMjAuODEyNiwtMjUuODA3NjIgMzkuMTI3NjksLTM2LjYzMDE4bDE1NS42NzgyNSwtODkuOTEwNDNjLTQuOTk1MDIsLTYuNjYwMDMgLTExLjY1NTA2LC0xMi40ODc1NiAtMTguMzE1MDksLTE2LjY1MDA4bC0xNDIuMzU4MTksLTgxLjU4NTM5Yy0yMC44MTI2LC0xMS42NTUwNiAtNDYuNjIwMjIsLTExLjY1NTA2IC02Ny40MzI4MiwwbC0xNDEuNTI1NjgsODEuNTg1MzljLTcuNDkyNTQsNC4xNjI1MiAtMTMuMzIwMDYsOS45OTAwNSAtMTkuMTQ3NTksMTYuNjUwMDhsMTU0Ljg0NTc1LDg5LjkxMDQzYzE4LjMxNTA5LDExLjY1NTA2IDMwLjgwMjY1LDIzLjMxMDExIDM5LjEyNzY5LDM2LjYzMDE4bC0wLjAwMDAxLDB6IiBmaWxsPSIjQTVERTM3IiBzdHJva2U9Im51bGwiLz4KICAgPHBhdGggaWQ9InN2Z181IiBjbGFzcz0ic3QyIiBkPSJtMjI0Ljc1NzkyLDE2MS41OTYxOGwxNDEuNTI1NjgsLTgxLjU4NTM5YzIwLjgxMjYsLTExLjY1NTA2IDQ2LjYyMDIyLC0xMS42NTUwNiA2Ny40MzI4MiwwbDE0Mi4zNTgxOSw4MS41ODUzOWM3LjQ5MjU0LDQuMTYyNTIgMTMuMzIwMDYsOS45OTAwNSAxOC4zMTUwOSwxNi42NTAwOGw1Ni42MTAyNywtMzIuNDY3NjZjLTYuNjYwMDMsLTkuMTU3NTQgLTE0Ljk4NTA3LC0xNi42NTAwOCAtMjQuOTc1MTIsLTIxLjY0NTFsLTE4NC44MTU4OSwtMTA3LjM5MzAyYy0yNS44MDc2MiwtMTQuOTg1MDcgLTU3LjQ0Mjc4LC0xNC45ODUwNyAtODMuMjUwNCwwbC0xODMuMTUwODgsMTA2LjU2MDUxYy05Ljk5MDA1LDUuODI3NTMgLTE4LjMxNTA5LDEzLjMyMDA2IC0yNC45NzUxMiwyMi40Nzc2MWw1Ni42MTAyNywzMi40Njc2NmM0LjE2MjUyLC02LjY2MDAzIDEwLjgyMjU1LC0xMi40ODc1NiAxOC4zMTUwOSwtMTYuNjUwMDh6IiBmaWxsPSIjQzhGMTc4IiBzdHJva2U9Im51bGwiLz4KICAgPHBhdGggaWQ9InN2Z182IiBjbGFzcz0ic3QyIiBkPSJtMzY2LjI4MzYsNTIyLjA3MDQxbC0xNDEuNTI1NjgsLTgxLjU4NTM5Yy0yMC44MTI2LC0xMS42NTUwNiAtMzMuMzAwMTYsLTM0LjEzMjY2IC0zMy4zMDAxNiwtNTguMjc1MjhsMCwtMTYzLjE3MDc5YzAsLTYuNjYwMDMgMC44MzI1LC0xMi40ODc1NiAyLjQ5NzUxLC0xOC4zMTUwOWwtNTYuNjEwMjcsLTMyLjQ2NzY2Yy0zLjMzMDAyLDkuMTU3NTQgLTQuOTk1MDIsMTcuNDgyNTggLTQuOTk1MDIsMjYuNjQwMTNsMCwyMTIuMjg4NTJjMCwyOS45NzAxNCAxNS44MTc1OCw1Ny40NDI3OCA0MS42MjUyLDcxLjU5NTM0bDE4My45ODMzOSwxMDUuNzI4MDFjOC4zMjUwNCw0Ljk5NTAyIDE4LjMxNTA5LDguMzI1MDQgMjguMzA1MTQsOS45OTAwNWwwLC02NC45MzUzMWMtNi42NjAwMywtMC44MzI1IC0xMy4zMjAwNiwtMy4zMzAwMiAtMTkuOTgwMSwtNy40OTI1NGwtMC4wMDAwMSwwLjAwMDAxeiIgZmlsbD0iI0M4RjE3OCIgc3Ryb2tlPSJudWxsIi8+CiAgIDxwYXRoIGlkPSJzdmdfNyIgY2xhc3M9InN0MSIgZD0ibTM0Ny4xMzYwMSwyODguOTY5MjlsLTE1My4xODA3NCwtODguMjQ1NDJjLTEuNjY1MDEsNS44Mjc1MyAtMi40OTc1MSwxMi40ODc1NiAtMi40OTc1MSwxOC4zMTUwOWwwLDE2My4xNzA3OWMwLDI0LjE0MjYyIDEyLjQ4NzU2LDQ2LjYyMDIyIDMzLjMwMDE2LDU4LjI3NTI4bDE0MS41MjU2OCw4MS41ODUzOWM2LjY2MDAzLDMuMzMwMDIgMTMuMzIwMDYsNS44Mjc1MyAyMC44MTI2LDcuNDkyNTRsMCwtMTcxLjQ5NTgzYy0wLjgzMjUsLTI0Ljk3NTEyIC03LjQ5MjU0LC00NS43ODc3MiAtMzkuOTYwMTksLTY5LjA5NzgzbDAsLTAuMDAwMDF6IiBmaWxsPSIjQTVERTM3IiBzdHJva2U9Im51bGwiLz4KICA8L2c+CiA8L2c+Cgo8L3N2Zz4=';
|
|
20
19
|
const ETCHER_OPEN_IMAGE_URL = 'https://www.balena.io/etcher/open-image-url';
|
|
21
20
|
export var ActionType;
|
|
@@ -65,7 +64,6 @@ const getInitialState = (deviceType, applicationId, releaseId) => ({
|
|
|
65
64
|
deviceType,
|
|
66
65
|
version: '',
|
|
67
66
|
network: 'ethernet',
|
|
68
|
-
secureboot: false,
|
|
69
67
|
developmentMode: false,
|
|
70
68
|
appUpdatePollInterval: 10,
|
|
71
69
|
wifiSsid: undefined,
|
|
@@ -244,17 +242,13 @@ export const DownloadImageDialog = ({ open, applicationId, releaseId, compatible
|
|
|
244
242
|
const setOsTypeCallback = useCallback((osT) => {
|
|
245
243
|
setOsType(osT);
|
|
246
244
|
}, []);
|
|
247
|
-
const handleChange = useCallback((
|
|
245
|
+
const handleChange = useCallback((updatedProps) => {
|
|
248
246
|
let newFormModelState;
|
|
249
|
-
if (
|
|
250
|
-
newFormModelState = getInitialState(
|
|
247
|
+
if (updatedProps.deviceType != null) {
|
|
248
|
+
newFormModelState = getInitialState(updatedProps.deviceType, applicationId, releaseId);
|
|
251
249
|
}
|
|
252
250
|
else {
|
|
253
|
-
newFormModelState = Object.assign(Object.assign({}, formModel),
|
|
254
|
-
formModel.deviceType.slug === GENERIC_X86_SLUG &&
|
|
255
|
-
semver.lt(value, GENERIC_X86_MINIMUM_SUPPORTED_SECUREBOOT_VERSION)
|
|
256
|
-
? false
|
|
257
|
-
: formModel.secureboot, [key]: value });
|
|
251
|
+
newFormModelState = Object.assign(Object.assign({}, formModel), updatedProps);
|
|
258
252
|
onFieldChange === null || onFieldChange === void 0 ? void 0 : onFieldChange(newFormModelState);
|
|
259
253
|
}
|
|
260
254
|
setFormModel(newFormModelState);
|
|
@@ -281,7 +275,7 @@ export const DownloadImageDialog = ({ open, applicationId, releaseId, compatible
|
|
|
281
275
|
dockerImage: formModel.version
|
|
282
276
|
? getDockerArtifact(formModel.deviceType.slug, stripVersionBuild(formModel.version))
|
|
283
277
|
: '',
|
|
284
|
-
}
|
|
278
|
+
} })] }), ((_d = formModel.deviceType.imageDownloadAlerts) !== null && _d !== void 0 ? _d : []).map((alert) => {
|
|
285
279
|
return (_jsx(Grid, { pt: 0, size: 12, children: _jsx(Callout, { severity: alert.type, children: alert.message }, alert.message) }, alert.message));
|
|
286
280
|
})] }) }) }), _jsx(DialogActions, { children: _jsx(DropDownButton, { className: "e2e-download-image-submit", items: actions, disabled: isValidatingUrl }) })] }));
|
|
287
281
|
};
|
|
@@ -33,7 +33,7 @@ export const Create = ({ model, rjstContext, hasOngoingAction, onActionTriggered
|
|
|
33
33
|
return (_jsx(Box, { display: "flex", children: _jsx(Tooltip, { title: typeof disabledReason === 'string' ? disabledReason : undefined, children: _jsx(Button, { "data-action": `create-${model.resource}`, variant: "contained", onClick: () => {
|
|
34
34
|
onActionTriggered({
|
|
35
35
|
action,
|
|
36
|
-
schema: rjstJsonSchemaPick(model.schema, model.permissions.create),
|
|
36
|
+
schema: rjstJsonSchemaPick(model.schema, model.permissions.flatMap((p) => p.create)),
|
|
37
37
|
});
|
|
38
38
|
}, startIcon: _jsx(FontAwesomeIcon, { icon: faMagic }), disabled: !!disabledReason, children: _jsx(ActionContent, { action: action, getDisabledReason: action.isDisabled, affectedEntries: undefined, checkedState: undefined, onDisabledReady: (result) => {
|
|
39
39
|
setDisabledReasonsByAction((disabledReasonsState) => (Object.assign(Object.assign({}, disabledReasonsState), { [action.title]: result })));
|
|
@@ -45,7 +45,9 @@ export const Update = ({ model, rjstContext, selected, hasOngoingAction, onActio
|
|
|
45
45
|
action,
|
|
46
46
|
schema: action.type === 'delete'
|
|
47
47
|
? {}
|
|
48
|
-
: rjstJsonSchemaPick(model.schema, model.permissions
|
|
48
|
+
: rjstJsonSchemaPick(model.schema, model.permissions.flatMap(
|
|
49
|
+
// TODO: Why is this cast necessary?
|
|
50
|
+
(p) => p[action.type])),
|
|
49
51
|
affectedEntries: selected,
|
|
50
52
|
});
|
|
51
53
|
},
|
|
@@ -86,7 +88,9 @@ export const Update = ({ model, rjstContext, selected, hasOngoingAction, onActio
|
|
|
86
88
|
action,
|
|
87
89
|
schema: action.type === 'delete'
|
|
88
90
|
? {}
|
|
89
|
-
: rjstJsonSchemaPick(model.schema, model.permissions
|
|
91
|
+
: rjstJsonSchemaPick(model.schema, model.permissions.flatMap(
|
|
92
|
+
// TODO: Why is this cast necessary?
|
|
93
|
+
(p) => p[action.type])),
|
|
90
94
|
affectedEntries: selected,
|
|
91
95
|
});
|
|
92
96
|
}, disabled: !!disabledUpdateReason, color: action.isDangerous ? 'error' : 'secondary', children: _jsx(ActionContent, { action: action, getDisabledReason: action.isDisabled, affectedEntries: selected, checkedState: checkedState, onDisabledReady: (result) => {
|
|
@@ -8,7 +8,7 @@ export interface AugmentedSshKey extends RJSTBaseResource<AugmentedSshKey> {
|
|
|
8
8
|
}
|
|
9
9
|
export declare const model: RJSTRawModel<AugmentedSshKey>;
|
|
10
10
|
export declare const transformers: {
|
|
11
|
-
__permissions: () => import("../schemaOps").Permissions<AugmentedSshKey
|
|
11
|
+
__permissions: () => import("../schemaOps").Permissions<AugmentedSshKey>[];
|
|
12
12
|
};
|
|
13
13
|
export declare const dataExample: {
|
|
14
14
|
id: number;
|
|
@@ -10,6 +10,6 @@ export declare const rjstDefaultPermissions: {
|
|
|
10
10
|
};
|
|
11
11
|
export declare const rjstRunTransformers: <T extends Dictionary<any>, TResult extends T, TContext = null>(data: T | undefined, transformers: Transformers<T, Omit<TResult, keyof T>, TContext>, context?: TContext) => TResult | undefined;
|
|
12
12
|
export declare const rjstGetModelForCollection: <T>(model: RJSTRawModel<T>, context?: {
|
|
13
|
-
accessRole?: string | null;
|
|
13
|
+
accessRole?: string[] | null;
|
|
14
14
|
}) => RJSTModel<T>;
|
|
15
15
|
export {};
|
|
@@ -38,6 +38,6 @@ export const rjstGetModelForCollection = (model, context) => {
|
|
|
38
38
|
...model.priorities.tertiary,
|
|
39
39
|
])
|
|
40
40
|
: model.schema;
|
|
41
|
-
return Object.assign(Object.assign({}, model), { permissions: (accessRole
|
|
42
|
-
model.permissions['default'], schema });
|
|
41
|
+
return Object.assign(Object.assign({}, model), { permissions: (!!(accessRole === null || accessRole === void 0 ? void 0 : accessRole.length) &&
|
|
42
|
+
accessRole.map((a) => { var _a; return (_a = model.permissions[a]) !== null && _a !== void 0 ? _a : model.permissions['default']; })) || [model.permissions['default']], schema });
|
|
43
43
|
};
|
|
@@ -4,7 +4,7 @@ import type { CheckedState } from './components/Table/utils';
|
|
|
4
4
|
import type { PineFilterObject } from './oData/jsonToOData';
|
|
5
5
|
export interface RJSTBaseResource<T> {
|
|
6
6
|
id: number;
|
|
7
|
-
__permissions: Permissions<T
|
|
7
|
+
__permissions: Array<Permissions<T>>;
|
|
8
8
|
}
|
|
9
9
|
type XHeaderLink = {
|
|
10
10
|
tooltip: string;
|
|
@@ -30,7 +30,7 @@ export interface RJSTRawModel<T> {
|
|
|
30
30
|
export interface RJSTModel<T> {
|
|
31
31
|
resource: string;
|
|
32
32
|
schema: JSONSchema;
|
|
33
|
-
permissions: Permissions<T
|
|
33
|
+
permissions: Array<Permissions<T>>;
|
|
34
34
|
priorities?: Priorities<T>;
|
|
35
35
|
}
|
|
36
36
|
export type RJSTEntityPropertyDefinition<T> = {
|
|
@@ -9,6 +9,6 @@ export declare const setToLocalStorage: (key: string, value: unknown) => void;
|
|
|
9
9
|
export declare const findInObject: (obj: Record<string, any>, key: string) => any;
|
|
10
10
|
export declare const ObjectFromEntries: (entries: Array<[string, any]>) => Record<string, any>;
|
|
11
11
|
export declare const getTagsDisabledReason: <T extends RJSTBaseResource<T>>(selected: T[] | undefined, tagField: keyof T, checkedState: CheckedState | undefined, tagsSdk: RJSTTagsSdk<T>, t: TFunction) => Promise<string | null>;
|
|
12
|
-
export declare const getCreateDisabledReason: <T extends RJSTBaseResource<T>>(permissions: Permissions<T
|
|
12
|
+
export declare const getCreateDisabledReason: <T extends RJSTBaseResource<T>>(permissions: Array<Permissions<T>>, hasOngoingAction: boolean, t: TFunction) => string | undefined;
|
|
13
13
|
export declare const rjstGetDisabledReason: <T extends RJSTBaseResource<T>>(selected: T[] | undefined, checkedState: CheckedState | undefined, hasOngoingAction: boolean, actionType: "update" | "delete" | null, t: TFunction) => string | undefined;
|
|
14
14
|
export declare const getSortingFunction: <T>(schemaKey: keyof T, schemaValue: JSONSchema) => (a: T, b: T) => number;
|
|
@@ -60,9 +60,9 @@ export const getTagsDisabledReason = async (selected, tagField, checkedState, ta
|
|
|
60
60
|
const lacksPermissionsOnSelected = tagsSdk && 'canAccess' in tagsSdk
|
|
61
61
|
? !(await tagsSdk.canAccess({ checkedState, selected }))
|
|
62
62
|
: selected === null || selected === void 0 ? void 0 : selected.some((entry) => {
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
return !entry.__permissions.some((p) => p.delete ||
|
|
64
|
+
p.create.includes(tagField) ||
|
|
65
|
+
p.update.includes(tagField));
|
|
66
66
|
});
|
|
67
67
|
if (lacksPermissionsOnSelected) {
|
|
68
68
|
return t('info.edit_tag_no_permissions', { resource: 'item' });
|
|
@@ -70,11 +70,11 @@ export const getTagsDisabledReason = async (selected, tagField, checkedState, ta
|
|
|
70
70
|
return null;
|
|
71
71
|
};
|
|
72
72
|
export const getCreateDisabledReason = (permissions, hasOngoingAction, t) => {
|
|
73
|
-
var _a;
|
|
74
73
|
if (hasOngoingAction) {
|
|
75
74
|
return t('info.ongoing_action_wait');
|
|
76
75
|
}
|
|
77
|
-
if (!
|
|
76
|
+
if (!permissions.length ||
|
|
77
|
+
!permissions.filter((p) => { var _a; return (_a = p.create) === null || _a === void 0 ? void 0 : _a.length; }).length) {
|
|
78
78
|
return t('info.create_item_no_permissions', { resource: 'item' });
|
|
79
79
|
}
|
|
80
80
|
};
|
|
@@ -89,9 +89,9 @@ export const rjstGetDisabledReason = (selected, checkedState, hasOngoingAction,
|
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
const lacksPermissionsOnSelected = selected.some((entry) => {
|
|
92
|
-
return
|
|
93
|
-
(Array.isArray(
|
|
94
|
-
|
|
92
|
+
return entry.__permissions.every((p) => !p[actionType] ||
|
|
93
|
+
(Array.isArray(p[actionType]) &&
|
|
94
|
+
p[actionType].length <= 0));
|
|
95
95
|
});
|
|
96
96
|
if (lacksPermissionsOnSelected) {
|
|
97
97
|
return t('info.update_item_no_permissions', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@balena/ui-shared-components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0-build-remove-role-inheritance-cb07df8e4c7bad3712464f6cef505f1a18fed75f-1",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"files": [
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
"ajv": "^8.17.1",
|
|
33
33
|
"ajv-formats": "^3.0.1",
|
|
34
34
|
"ajv-keywords": "^5.1.0",
|
|
35
|
-
"analytics-client": "^
|
|
36
|
-
"balena-semver": "^3.0.5",
|
|
35
|
+
"analytics-client": "^3.0.0",
|
|
37
36
|
"color": "^5.0.0",
|
|
38
37
|
"color-hash": "^2.0.2",
|
|
39
38
|
"date-fns": "^4.1.0",
|
|
@@ -139,6 +138,6 @@
|
|
|
139
138
|
},
|
|
140
139
|
"homepage": "https://github.com/balena-io/ui-shared-components#readme",
|
|
141
140
|
"versionist": {
|
|
142
|
-
"publishedAt": "2025-04-
|
|
141
|
+
"publishedAt": "2025-04-30T15:02:38.870Z"
|
|
143
142
|
}
|
|
144
143
|
}
|