@backstage/plugin-user-settings 0.8.5-next.1 → 0.8.6-next.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 (67) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/alpha/package.json +1 -1
  3. package/dist/alpha.esm.js +3 -5
  4. package/dist/alpha.esm.js.map +1 -1
  5. package/dist/apis/StorageApi/UserSettingsStorage.esm.js +154 -0
  6. package/dist/apis/StorageApi/UserSettingsStorage.esm.js.map +1 -0
  7. package/dist/components/AuthProviders/DefaultProviderSettings.esm.js +84 -0
  8. package/dist/components/AuthProviders/DefaultProviderSettings.esm.js.map +1 -0
  9. package/dist/components/AuthProviders/EmptyProviders.esm.js +41 -0
  10. package/dist/components/AuthProviders/EmptyProviders.esm.js.map +1 -0
  11. package/dist/components/AuthProviders/ProviderSettingsAvatar.esm.js +21 -0
  12. package/dist/components/AuthProviders/ProviderSettingsAvatar.esm.js.map +1 -0
  13. package/dist/components/AuthProviders/ProviderSettingsItem.esm.js +83 -0
  14. package/dist/components/AuthProviders/ProviderSettingsItem.esm.js.map +1 -0
  15. package/dist/components/AuthProviders/UserSettingsAuthProviders.esm.js +21 -0
  16. package/dist/components/AuthProviders/UserSettingsAuthProviders.esm.js.map +1 -0
  17. package/dist/components/DefaultSettingsPage/DefaultSettingsPage.esm.js +44 -0
  18. package/dist/components/DefaultSettingsPage/DefaultSettingsPage.esm.js.map +1 -0
  19. package/dist/components/FeatureFlags/EmptyFlags.esm.js +41 -0
  20. package/dist/components/FeatureFlags/EmptyFlags.esm.js.map +1 -0
  21. package/dist/components/FeatureFlags/FeatureFlagsItem.esm.js +17 -0
  22. package/dist/components/FeatureFlags/FeatureFlagsItem.esm.js.map +1 -0
  23. package/dist/components/FeatureFlags/UserSettingsFeatureFlags.esm.js +96 -0
  24. package/dist/components/FeatureFlags/UserSettingsFeatureFlags.esm.js.map +1 -0
  25. package/dist/components/General/UserSettingsAppearanceCard.esm.js +14 -0
  26. package/dist/components/General/UserSettingsAppearanceCard.esm.js.map +1 -0
  27. package/dist/components/General/UserSettingsGeneral.esm.js +12 -0
  28. package/dist/components/General/UserSettingsGeneral.esm.js.map +1 -0
  29. package/dist/components/General/UserSettingsIdentityCard.esm.js +18 -0
  30. package/dist/components/General/UserSettingsIdentityCard.esm.js.map +1 -0
  31. package/dist/components/General/UserSettingsLanguageToggle.esm.js +100 -0
  32. package/dist/components/General/UserSettingsLanguageToggle.esm.js.map +1 -0
  33. package/dist/components/General/UserSettingsMenu.esm.js +45 -0
  34. package/dist/components/General/UserSettingsMenu.esm.js.map +1 -0
  35. package/dist/components/General/UserSettingsPinToggle.esm.js +38 -0
  36. package/dist/components/General/UserSettingsPinToggle.esm.js.map +1 -0
  37. package/dist/components/General/UserSettingsProfileCard.esm.js +15 -0
  38. package/dist/components/General/UserSettingsProfileCard.esm.js.map +1 -0
  39. package/dist/components/General/UserSettingsSignInAvatar.esm.js +31 -0
  40. package/dist/components/General/UserSettingsSignInAvatar.esm.js.map +1 -0
  41. package/dist/components/General/UserSettingsThemeToggle.esm.js +126 -0
  42. package/dist/components/General/UserSettingsThemeToggle.esm.js.map +1 -0
  43. package/dist/components/Settings.esm.js +14 -0
  44. package/dist/components/Settings.esm.js.map +1 -0
  45. package/dist/components/SettingsLayout/SettingsLayout.esm.js +26 -0
  46. package/dist/components/SettingsLayout/SettingsLayout.esm.js.map +1 -0
  47. package/dist/components/SettingsPage/SettingsPage.esm.js +26 -0
  48. package/dist/components/SettingsPage/SettingsPage.esm.js.map +1 -0
  49. package/dist/components/SettingsPage/index.esm.js +2 -0
  50. package/dist/components/SettingsPage/index.esm.js.map +1 -0
  51. package/dist/components/UserSettingsTab/UserSettingsTab.esm.js +10 -0
  52. package/dist/components/UserSettingsTab/UserSettingsTab.esm.js.map +1 -0
  53. package/dist/components/useUserProfileInfo.esm.js +39 -0
  54. package/dist/components/useUserProfileInfo.esm.js.map +1 -0
  55. package/dist/index.esm.js +20 -199
  56. package/dist/index.esm.js.map +1 -1
  57. package/dist/plugin.esm.js +21 -0
  58. package/dist/plugin.esm.js.map +1 -0
  59. package/dist/translation.esm.js +26 -0
  60. package/dist/translation.esm.js.map +1 -0
  61. package/package.json +14 -14
  62. package/dist/esm/SettingsPage-CeSuiezj.esm.js +0 -753
  63. package/dist/esm/SettingsPage-CeSuiezj.esm.js.map +0 -1
  64. package/dist/esm/index-Cq4DqL_j.esm.js +0 -34
  65. package/dist/esm/index-Cq4DqL_j.esm.js.map +0 -1
  66. package/dist/esm/translation-CSRNIDi0.esm.js +0 -44
  67. package/dist/esm/translation-CSRNIDi0.esm.js.map +0 -1
@@ -1,753 +0,0 @@
1
- import { useOutlet } from 'react-router-dom';
2
- import React, { useState, useEffect, useCallback, cloneElement } from 'react';
3
- import List from '@material-ui/core/List';
4
- import Button from '@material-ui/core/Button';
5
- import Typography from '@material-ui/core/Typography';
6
- import { EmptyState, CodeSnippet, sidebarConfig, InfoCard, useSidebarPinState, Select, Page, Header, RoutedTabs } from '@backstage/core-components';
7
- import Star from '@material-ui/icons/Star';
8
- import Grid from '@material-ui/core/Grid';
9
- import ListItem from '@material-ui/core/ListItem';
10
- import ListItemIcon from '@material-ui/core/ListItemIcon';
11
- import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
12
- import ListItemText from '@material-ui/core/ListItemText';
13
- import Tooltip from '@material-ui/core/Tooltip';
14
- import { useApi, errorApiRef, SessionState, googleAuthApiRef, microsoftAuthApiRef, githubAuthApiRef, gitlabAuthApiRef, oktaAuthApiRef, bitbucketAuthApiRef, oneloginAuthApiRef, atlassianAuthApiRef, bitbucketServerAuthApiRef, configApiRef, featureFlagsApiRef, FeatureFlagState, identityApiRef, alertApiRef, appThemeApiRef, attachComponentData, useElementFilter } from '@backstage/core-plugin-api';
15
- import Avatar from '@material-ui/core/Avatar';
16
- import { makeStyles } from '@material-ui/core/styles';
17
- import TextField from '@material-ui/core/TextField';
18
- import IconButton from '@material-ui/core/IconButton';
19
- import Switch from '@material-ui/core/Switch';
20
- import ClearIcon from '@material-ui/icons/Clear';
21
- import useAsync from 'react-use/esm/useAsync';
22
- import Menu from '@material-ui/core/Menu';
23
- import MenuItem from '@material-ui/core/MenuItem';
24
- import SignOutIcon from '@material-ui/icons/MeetingRoom';
25
- import MoreVertIcon from '@material-ui/icons/MoreVert';
26
- import useObservable from 'react-use/esm/useObservable';
27
- import AutoIcon from '@material-ui/icons/BrightnessAuto';
28
- import ToggleButton from '@material-ui/lab/ToggleButton';
29
- import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup';
30
- import { useTranslationRef, appLanguageApiRef } from '@backstage/core-plugin-api/alpha';
31
- import { u as userSettingsTranslationRef } from './translation-CSRNIDi0.esm.js';
32
- import { EntityRefLinks } from '@backstage/plugin-catalog-react';
33
-
34
- const EXAMPLE$1 = `auth:
35
- providers:
36
- google:
37
- development:
38
- clientId: \${AUTH_GOOGLE_CLIENT_ID}
39
- clientSecret: \${AUTH_GOOGLE_CLIENT_SECRET}
40
- `;
41
- const EmptyProviders = () => /* @__PURE__ */ React.createElement(
42
- EmptyState,
43
- {
44
- missing: "content",
45
- title: "No Authentication Providers",
46
- description: "You can add Authentication Providers to Backstage which allows you to use these providers to authenticate yourself.",
47
- action: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, "Open ", /* @__PURE__ */ React.createElement("code", null, "app-config.yaml"), " and make the changes as highlighted below:"), /* @__PURE__ */ React.createElement(
48
- CodeSnippet,
49
- {
50
- text: EXAMPLE$1,
51
- language: "yaml",
52
- showLineNumbers: true,
53
- highlightedNumbers: [3, 4, 5, 6, 7, 8],
54
- customStyle: { background: "inherit", fontSize: "115%" }
55
- }
56
- ), /* @__PURE__ */ React.createElement(
57
- Button,
58
- {
59
- variant: "contained",
60
- color: "primary",
61
- href: "https://backstage.io/docs/auth/add-auth-provider"
62
- },
63
- "Read More"
64
- ))
65
- }
66
- );
67
-
68
- const useStyles$3 = makeStyles((theme) => ({
69
- avatar: {
70
- width: ({ size }) => size,
71
- height: ({ size }) => size,
72
- fontSize: ({ size }) => size * 0.7,
73
- border: `1px solid ${theme.palette.textSubtle}`
74
- }
75
- }));
76
- const ProviderSettingsAvatar = ({ size, picture }) => {
77
- const { iconSize } = sidebarConfig;
78
- const classes = useStyles$3(size ? { size } : { size: iconSize });
79
- return /* @__PURE__ */ React.createElement(Avatar, { src: picture, className: classes.avatar });
80
- };
81
-
82
- const emptyProfile = {};
83
- const ProviderSettingsItem = (props) => {
84
- const { title, description, icon: Icon, apiRef } = props;
85
- const api = useApi(apiRef);
86
- const errorApi = useApi(errorApiRef);
87
- const [signedIn, setSignedIn] = useState(false);
88
- const [profile, setProfile] = useState(emptyProfile);
89
- useEffect(() => {
90
- let didCancel = false;
91
- const subscription = api.sessionState$().subscribe((sessionState) => {
92
- if (sessionState !== SessionState.SignedIn) {
93
- setProfile(emptyProfile);
94
- setSignedIn(false);
95
- }
96
- if (!didCancel) {
97
- api.getProfile({ optional: true }).then((profileResponse) => {
98
- if (!didCancel) {
99
- if (sessionState === SessionState.SignedIn) {
100
- setSignedIn(true);
101
- }
102
- if (profileResponse) {
103
- setProfile(profileResponse);
104
- }
105
- }
106
- });
107
- }
108
- });
109
- return () => {
110
- didCancel = true;
111
- subscription.unsubscribe();
112
- };
113
- }, [api]);
114
- return /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Icon, null)), /* @__PURE__ */ React.createElement(
115
- ListItemText,
116
- {
117
- primary: title,
118
- secondary: /* @__PURE__ */ React.createElement(Tooltip, { placement: "top", arrow: true, title: description }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 6 }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(ProviderSettingsAvatar, { size: 48, picture: profile.picture })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, sm: true, container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true, container: true, direction: "column", spacing: 2 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true }, profile.displayName && /* @__PURE__ */ React.createElement(
119
- Typography,
120
- {
121
- variant: "subtitle1",
122
- color: "textPrimary",
123
- gutterBottom: true
124
- },
125
- profile.displayName
126
- ), profile.email && /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, profile.email), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, description)))))),
127
- secondaryTypographyProps: { noWrap: true, style: { width: "80%" } }
128
- }
129
- ), /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(
130
- Tooltip,
131
- {
132
- placement: "top",
133
- arrow: true,
134
- title: signedIn ? `Sign out from ${title}` : `Sign in to ${title}`
135
- },
136
- /* @__PURE__ */ React.createElement(
137
- Button,
138
- {
139
- variant: "outlined",
140
- color: "primary",
141
- onClick: () => {
142
- const action = signedIn ? api.signOut() : api.signIn();
143
- action.catch((error) => errorApi.post(error));
144
- }
145
- },
146
- signedIn ? `Sign out` : `Sign in`
147
- )
148
- )));
149
- };
150
-
151
- const DefaultProviderSettings = (props) => {
152
- const { configuredProviders } = props;
153
- return /* @__PURE__ */ React.createElement(React.Fragment, null, configuredProviders.includes("google") && /* @__PURE__ */ React.createElement(
154
- ProviderSettingsItem,
155
- {
156
- title: "Google",
157
- description: "Provides authentication towards Google APIs and identities",
158
- apiRef: googleAuthApiRef,
159
- icon: Star
160
- }
161
- ), configuredProviders.includes("microsoft") && /* @__PURE__ */ React.createElement(
162
- ProviderSettingsItem,
163
- {
164
- title: "Microsoft",
165
- description: "Provides authentication towards Microsoft APIs and identities",
166
- apiRef: microsoftAuthApiRef,
167
- icon: Star
168
- }
169
- ), configuredProviders.includes("github") && /* @__PURE__ */ React.createElement(
170
- ProviderSettingsItem,
171
- {
172
- title: "GitHub",
173
- description: "Provides authentication towards GitHub APIs",
174
- apiRef: githubAuthApiRef,
175
- icon: Star
176
- }
177
- ), configuredProviders.includes("gitlab") && /* @__PURE__ */ React.createElement(
178
- ProviderSettingsItem,
179
- {
180
- title: "GitLab",
181
- description: "Provides authentication towards GitLab APIs",
182
- apiRef: gitlabAuthApiRef,
183
- icon: Star
184
- }
185
- ), configuredProviders.includes("okta") && /* @__PURE__ */ React.createElement(
186
- ProviderSettingsItem,
187
- {
188
- title: "Okta",
189
- description: "Provides authentication towards Okta APIs",
190
- apiRef: oktaAuthApiRef,
191
- icon: Star
192
- }
193
- ), configuredProviders.includes("bitbucket") && /* @__PURE__ */ React.createElement(
194
- ProviderSettingsItem,
195
- {
196
- title: "Bitbucket",
197
- description: "Provides authentication towards Bitbucket APIs",
198
- apiRef: bitbucketAuthApiRef,
199
- icon: Star
200
- }
201
- ), configuredProviders.includes("onelogin") && /* @__PURE__ */ React.createElement(
202
- ProviderSettingsItem,
203
- {
204
- title: "OneLogin",
205
- description: "Provides authentication towards OneLogin APIs",
206
- apiRef: oneloginAuthApiRef,
207
- icon: Star
208
- }
209
- ), configuredProviders.includes("atlassian") && /* @__PURE__ */ React.createElement(
210
- ProviderSettingsItem,
211
- {
212
- title: "Atlassian",
213
- description: "Provides authentication towards Atlassian APIs",
214
- apiRef: atlassianAuthApiRef,
215
- icon: Star
216
- }
217
- ), configuredProviders.includes("bitbucketServer") && /* @__PURE__ */ React.createElement(
218
- ProviderSettingsItem,
219
- {
220
- title: "Bitbucket Server",
221
- description: "Provides authentication towards Bitbucket Server APIs",
222
- apiRef: bitbucketServerAuthApiRef,
223
- icon: Star
224
- }
225
- ));
226
- };
227
-
228
- const UserSettingsAuthProviders = (props) => {
229
- const { providerSettings } = props;
230
- const configApi = useApi(configApiRef);
231
- const providersConfig = configApi.getOptionalConfig("auth.providers");
232
- const configuredProviders = (providersConfig == null ? void 0 : providersConfig.keys()) || [];
233
- const providers = providerSettings != null ? providerSettings : /* @__PURE__ */ React.createElement(DefaultProviderSettings, { configuredProviders });
234
- if (!providerSettings && !(configuredProviders == null ? void 0 : configuredProviders.length)) {
235
- return /* @__PURE__ */ React.createElement(EmptyProviders, null);
236
- }
237
- return /* @__PURE__ */ React.createElement(InfoCard, { title: "Available Providers" }, /* @__PURE__ */ React.createElement(List, { dense: true }, providers));
238
- };
239
-
240
- const EXAMPLE = `import { createPlugin } from '@backstage/core-plugin-api';
241
-
242
- export default createPlugin({
243
- id: 'plugin-name',
244
- featureFlags: [{ name: 'enable-example-feature' }],
245
- });
246
- `;
247
- const EmptyFlags = () => /* @__PURE__ */ React.createElement(
248
- EmptyState,
249
- {
250
- missing: "content",
251
- title: "No Feature Flags",
252
- description: "Feature Flags make it possible for plugins to register features in Backstage for users to opt into. You can use this to split out logic in your code for manual A/B testing, etc.",
253
- action: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, "An example for how to add a feature flag is highlighted below:"), /* @__PURE__ */ React.createElement(
254
- CodeSnippet,
255
- {
256
- text: EXAMPLE,
257
- language: "typescript",
258
- showLineNumbers: true,
259
- highlightedNumbers: [6],
260
- customStyle: { background: "inherit", fontSize: "115%" }
261
- }
262
- ), /* @__PURE__ */ React.createElement(
263
- Button,
264
- {
265
- variant: "contained",
266
- color: "primary",
267
- href: "https://backstage.io/docs/api/utility-apis"
268
- },
269
- "Read More"
270
- ))
271
- }
272
- );
273
-
274
- const getSecondaryText = (flag) => {
275
- if (flag.description) {
276
- return flag.description;
277
- }
278
- return flag.pluginId ? `Registered in ${flag.pluginId} plugin` : "Registered in the application";
279
- };
280
- const FlagItem = ({ flag, enabled, toggleHandler }) => /* @__PURE__ */ React.createElement(ListItem, { divider: true, button: true, onClick: () => toggleHandler(flag.name) }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Tooltip, { placement: "top", arrow: true, title: enabled ? "Disable" : "Enable" }, /* @__PURE__ */ React.createElement(Switch, { color: "primary", checked: enabled, name: flag.name }))), /* @__PURE__ */ React.createElement(ListItemText, { primary: flag.name, secondary: getSecondaryText(flag) }));
281
-
282
- const sortFlags = (flags, featureFlagsApi) => {
283
- const activeFlags = flags.filter((flag) => featureFlagsApi.isActive(flag.name));
284
- const idleFlags = flags.filter((flag) => !featureFlagsApi.isActive(flag.name));
285
- return [...activeFlags, ...idleFlags];
286
- };
287
- const UserSettingsFeatureFlags = () => {
288
- const featureFlagsApi = useApi(featureFlagsApiRef);
289
- const inputRef = React.useRef();
290
- const initialFeatureFlags = featureFlagsApi.getRegisteredFlags();
291
- const initialFeatureFlagsSorted = sortFlags(
292
- initialFeatureFlags,
293
- featureFlagsApi
294
- );
295
- const [featureFlags] = useState(initialFeatureFlagsSorted);
296
- const initialFlagState = Object.fromEntries(
297
- featureFlags.map(({ name }) => [name, featureFlagsApi.isActive(name)])
298
- );
299
- const [state, setState] = useState(initialFlagState);
300
- const [filterInput, setFilterInput] = useState("");
301
- const toggleFlag = useCallback(
302
- (flagName) => {
303
- const newState = featureFlagsApi.isActive(flagName) ? FeatureFlagState.None : FeatureFlagState.Active;
304
- featureFlagsApi.save({
305
- states: { [flagName]: newState },
306
- merge: true
307
- });
308
- setState((prevState) => ({
309
- ...prevState,
310
- [flagName]: newState === FeatureFlagState.Active
311
- }));
312
- },
313
- [featureFlagsApi]
314
- );
315
- if (!featureFlags.length) {
316
- return /* @__PURE__ */ React.createElement(EmptyFlags, null);
317
- }
318
- const clearFilterInput = () => {
319
- var _a;
320
- setFilterInput("");
321
- (_a = inputRef == null ? void 0 : inputRef.current) == null ? void 0 : _a.focus();
322
- };
323
- const filteredFeatureFlags = featureFlags.filter((featureFlag) => {
324
- const featureFlagName = featureFlag.name.toLocaleLowerCase("en-US");
325
- return featureFlagName.includes(filterInput.toLocaleLowerCase("en-US"));
326
- });
327
- const Header = () => /* @__PURE__ */ React.createElement(Grid, { container: true, style: { justifyContent: "space-between" } }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6, md: 8 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, "Feature Flags"), /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "Please refresh the page when toggling feature flags")), featureFlags.length >= 10 && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6, md: 4 }, /* @__PURE__ */ React.createElement(
328
- TextField,
329
- {
330
- label: "Filter",
331
- style: { display: "flex", justifyContent: "flex-end" },
332
- inputRef: (ref) => ref && ref.focus(),
333
- InputProps: {
334
- ...filterInput.length && {
335
- endAdornment: /* @__PURE__ */ React.createElement(
336
- IconButton,
337
- {
338
- "aria-label": "Clear filter",
339
- onClick: clearFilterInput,
340
- edge: "end"
341
- },
342
- /* @__PURE__ */ React.createElement(ClearIcon, null)
343
- )
344
- }
345
- },
346
- onChange: (e) => setFilterInput(e.target.value),
347
- value: filterInput
348
- }
349
- )));
350
- return /* @__PURE__ */ React.createElement(InfoCard, { title: /* @__PURE__ */ React.createElement(Header, null) }, /* @__PURE__ */ React.createElement(List, { dense: true }, filteredFeatureFlags.map((featureFlag) => {
351
- const enabled = Boolean(state[featureFlag.name]);
352
- return /* @__PURE__ */ React.createElement(
353
- FlagItem,
354
- {
355
- key: featureFlag.name,
356
- flag: featureFlag,
357
- enabled,
358
- toggleHandler: toggleFlag
359
- }
360
- );
361
- })));
362
- };
363
-
364
- const useUserProfile = () => {
365
- var _a;
366
- const identityApi = useApi(identityApiRef);
367
- const alertApi = useApi(alertApiRef);
368
- const { value, loading, error } = useAsync(async () => {
369
- return {
370
- profile: await identityApi.getProfileInfo(),
371
- identity: await identityApi.getBackstageIdentity()
372
- };
373
- }, []);
374
- useEffect(() => {
375
- if (error) {
376
- alertApi.post({
377
- message: `Failed to load user identity: ${error}`,
378
- severity: "error"
379
- });
380
- }
381
- }, [error, alertApi]);
382
- if (loading || error) {
383
- return {
384
- profile: {},
385
- displayName: "",
386
- loading
387
- };
388
- }
389
- return {
390
- profile: value.profile,
391
- backstageIdentity: value.identity,
392
- displayName: (_a = value.profile.displayName) != null ? _a : value.identity.userEntityRef,
393
- loading
394
- };
395
- };
396
-
397
- const useStyles$2 = makeStyles((theme) => ({
398
- avatar: {
399
- width: ({ size }) => size,
400
- height: ({ size }) => size,
401
- fontSize: ({ size }) => size * 0.7,
402
- border: `1px solid ${theme.palette.textSubtle}`
403
- }
404
- }));
405
- const UserSettingsSignInAvatar = (props) => {
406
- const { size } = props;
407
- const { iconSize } = sidebarConfig;
408
- const classes = useStyles$2(size ? { size } : { size: iconSize });
409
- const { profile } = useUserProfile();
410
- return /* @__PURE__ */ React.createElement(
411
- Avatar,
412
- {
413
- src: profile.picture,
414
- className: classes.avatar,
415
- alt: "Profile picture"
416
- }
417
- );
418
- };
419
-
420
- const UserSettingsMenu = () => {
421
- const errorApi = useApi(errorApiRef);
422
- const identityApi = useApi(identityApiRef);
423
- const [open, setOpen] = React.useState(false);
424
- const [anchorEl, setAnchorEl] = React.useState(
425
- void 0
426
- );
427
- const handleOpen = (event) => {
428
- setAnchorEl(event.currentTarget);
429
- setOpen(true);
430
- };
431
- const handleClose = () => {
432
- setAnchorEl(void 0);
433
- setOpen(false);
434
- };
435
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
436
- IconButton,
437
- {
438
- "data-testid": "user-settings-menu",
439
- "aria-label": "more",
440
- onClick: handleOpen
441
- },
442
- /* @__PURE__ */ React.createElement(MoreVertIcon, null)
443
- ), /* @__PURE__ */ React.createElement(Menu, { anchorEl, open, onClose: handleClose }, /* @__PURE__ */ React.createElement(
444
- MenuItem,
445
- {
446
- "data-testid": "sign-out",
447
- onClick: () => identityApi.signOut().catch((error) => errorApi.post(error))
448
- },
449
- /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(SignOutIcon, null)),
450
- "Sign Out"
451
- )));
452
- };
453
-
454
- const UserSettingsProfileCard = () => {
455
- const { profile, displayName } = useUserProfile();
456
- return /* @__PURE__ */ React.createElement(InfoCard, { title: "Profile", variant: "gridItem" }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 6 }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(UserSettingsSignInAvatar, { size: 96 })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, sm: true, container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true, container: true, direction: "column", spacing: 2 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1", gutterBottom: true }, displayName), profile.email && /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, profile.email))), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(UserSettingsMenu, null)))));
457
- };
458
-
459
- const UserSettingsPinToggle = () => {
460
- const { isPinned, toggleSidebarPinState } = useSidebarPinState();
461
- return /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(
462
- ListItemText,
463
- {
464
- primary: "Pin Sidebar",
465
- secondary: "Prevent the sidebar from collapsing"
466
- }
467
- ), /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(
468
- Tooltip,
469
- {
470
- placement: "top",
471
- arrow: true,
472
- title: `${isPinned ? "Unpin" : "Pin"} Sidebar`
473
- },
474
- /* @__PURE__ */ React.createElement(
475
- Switch,
476
- {
477
- color: "primary",
478
- checked: isPinned,
479
- onChange: () => toggleSidebarPinState(),
480
- name: "pin",
481
- inputProps: { "aria-label": "Pin Sidebar Switch" }
482
- }
483
- )
484
- )));
485
- };
486
-
487
- const ThemeIcon = ({ id, activeId, icon }) => icon ? cloneElement(icon, {
488
- color: activeId === id ? "primary" : void 0
489
- }) : /* @__PURE__ */ React.createElement(AutoIcon, { color: activeId === id ? "primary" : void 0 });
490
- const useStyles$1 = makeStyles((theme) => ({
491
- container: {
492
- display: "flex",
493
- flexWrap: "wrap",
494
- width: "100%",
495
- justifyContent: "space-between",
496
- alignItems: "center",
497
- paddingBottom: 8,
498
- paddingRight: 16
499
- },
500
- list: {
501
- width: "initial",
502
- [theme.breakpoints.down("xs")]: {
503
- width: "100%",
504
- padding: `0 0 12px`
505
- }
506
- },
507
- listItemText: {
508
- paddingRight: 0,
509
- paddingLeft: 0
510
- },
511
- listItemSecondaryAction: {
512
- position: "relative",
513
- transform: "unset",
514
- top: "auto",
515
- right: "auto",
516
- paddingLeft: 16,
517
- [theme.breakpoints.down("xs")]: {
518
- paddingLeft: 0
519
- }
520
- }
521
- }));
522
- const TooltipToggleButton = ({
523
- children,
524
- title,
525
- value,
526
- ...props
527
- }) => /* @__PURE__ */ React.createElement(Tooltip, { placement: "top", arrow: true, title }, /* @__PURE__ */ React.createElement(ToggleButton, { value, ...props }, children));
528
- const UserSettingsThemeToggle = () => {
529
- const classes = useStyles$1();
530
- const appThemeApi = useApi(appThemeApiRef);
531
- const activeThemeId = useObservable(
532
- appThemeApi.activeThemeId$(),
533
- appThemeApi.getActiveThemeId()
534
- );
535
- const themeIds = appThemeApi.getInstalledThemes();
536
- const { t } = useTranslationRef(userSettingsTranslationRef);
537
- const handleSetTheme = (_event, newThemeId) => {
538
- if (themeIds.some((it) => it.id === newThemeId)) {
539
- appThemeApi.setActiveThemeId(newThemeId);
540
- } else {
541
- appThemeApi.setActiveThemeId(void 0);
542
- }
543
- };
544
- return /* @__PURE__ */ React.createElement(
545
- ListItem,
546
- {
547
- className: classes.list,
548
- classes: { container: classes.container }
549
- },
550
- /* @__PURE__ */ React.createElement(
551
- ListItemText,
552
- {
553
- className: classes.listItemText,
554
- primary: t("themeToggle.title"),
555
- secondary: t("themeToggle.description")
556
- }
557
- ),
558
- /* @__PURE__ */ React.createElement(ListItemSecondaryAction, { className: classes.listItemSecondaryAction }, /* @__PURE__ */ React.createElement(
559
- ToggleButtonGroup,
560
- {
561
- exclusive: true,
562
- size: "small",
563
- value: activeThemeId != null ? activeThemeId : "auto",
564
- onChange: handleSetTheme
565
- },
566
- themeIds.map((theme) => {
567
- const themeId = theme.id;
568
- const themeIcon = theme.icon;
569
- const themeTitle = theme.title || (themeId === "light" || themeId === "dark" ? t(`themeToggle.names.${themeId}`) : themeId);
570
- return /* @__PURE__ */ React.createElement(
571
- TooltipToggleButton,
572
- {
573
- key: themeId,
574
- title: t("themeToggle.select", { theme: themeTitle }),
575
- value: themeId
576
- },
577
- /* @__PURE__ */ React.createElement(React.Fragment, null, themeTitle, "\xA0", /* @__PURE__ */ React.createElement(
578
- ThemeIcon,
579
- {
580
- id: themeId,
581
- icon: themeIcon,
582
- activeId: activeThemeId
583
- }
584
- ))
585
- );
586
- }),
587
- /* @__PURE__ */ React.createElement(Tooltip, { placement: "top", arrow: true, title: t("themeToggle.selectAuto") }, /* @__PURE__ */ React.createElement(ToggleButton, { value: "auto", selected: activeThemeId === void 0 }, t("themeToggle.names.auto"), "\xA0", /* @__PURE__ */ React.createElement(
588
- AutoIcon,
589
- {
590
- color: activeThemeId === void 0 ? "primary" : void 0
591
- }
592
- )))
593
- ))
594
- );
595
- };
596
-
597
- const useStyles = makeStyles((theme) => ({
598
- container: {
599
- display: "flex",
600
- flexWrap: "wrap",
601
- width: "100%",
602
- justifyContent: "space-between",
603
- alignItems: "center",
604
- paddingBottom: 8,
605
- paddingRight: 16
606
- },
607
- list: {
608
- width: "initial",
609
- [theme.breakpoints.down("xs")]: {
610
- width: "100%",
611
- padding: `0 0 12px`
612
- }
613
- },
614
- listItemText: {
615
- paddingRight: 0,
616
- paddingLeft: 0
617
- },
618
- listItemSecondaryAction: {
619
- position: "relative",
620
- transform: "unset",
621
- top: "auto",
622
- right: "auto",
623
- paddingLeft: 16,
624
- [theme.breakpoints.down("xs")]: {
625
- paddingLeft: 0
626
- }
627
- }
628
- }));
629
- const UserSettingsLanguageToggle = () => {
630
- const classes = useStyles();
631
- const languageApi = useApi(appLanguageApiRef);
632
- const { t } = useTranslationRef(userSettingsTranslationRef);
633
- const [languageObservable] = useState(() => languageApi.language$());
634
- const { language: currentLanguage } = useObservable(
635
- languageObservable,
636
- languageApi.getLanguage()
637
- );
638
- const { languages } = languageApi.getAvailableLanguages();
639
- if (languages.length <= 1) {
640
- return null;
641
- }
642
- const handleSetLanguage = (newLanguage) => {
643
- languageApi.setLanguage(newLanguage);
644
- };
645
- const getLanguageDisplayName = (language) => {
646
- try {
647
- const names = new Intl.DisplayNames([language], {
648
- type: "language"
649
- });
650
- return names.of(language) || language;
651
- } catch (err) {
652
- return language;
653
- }
654
- };
655
- return /* @__PURE__ */ React.createElement(
656
- ListItem,
657
- {
658
- className: classes.list,
659
- classes: { container: classes.container }
660
- },
661
- /* @__PURE__ */ React.createElement(
662
- ListItemText,
663
- {
664
- className: classes.listItemText,
665
- primary: t("languageToggle.title"),
666
- secondary: t("languageToggle.description")
667
- }
668
- ),
669
- /* @__PURE__ */ React.createElement(ListItemSecondaryAction, { className: classes.listItemSecondaryAction }, /* @__PURE__ */ React.createElement(
670
- Select,
671
- {
672
- label: "",
673
- selected: currentLanguage,
674
- items: languages.map((language) => ({
675
- label: getLanguageDisplayName(language),
676
- value: language
677
- })),
678
- onChange: (selectedItems) => handleSetLanguage(selectedItems)
679
- }
680
- ))
681
- );
682
- };
683
-
684
- const UserSettingsAppearanceCard = () => {
685
- const { isMobile } = useSidebarPinState();
686
- return /* @__PURE__ */ React.createElement(InfoCard, { title: "Appearance", variant: "gridItem" }, /* @__PURE__ */ React.createElement(List, { dense: true }, /* @__PURE__ */ React.createElement(UserSettingsThemeToggle, null), /* @__PURE__ */ React.createElement(UserSettingsLanguageToggle, null), !isMobile && /* @__PURE__ */ React.createElement(UserSettingsPinToggle, null)));
687
- };
688
-
689
- const Contents = () => {
690
- const { backstageIdentity } = useUserProfile();
691
- if (!backstageIdentity) {
692
- return /* @__PURE__ */ React.createElement(Typography, null, "No Backstage Identity");
693
- }
694
- return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 1 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1", gutterBottom: true }, "User Entity:", " ", /* @__PURE__ */ React.createElement(EntityRefLinks, { entityRefs: [backstageIdentity.userEntityRef] }))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "Ownership Entities:", " ", /* @__PURE__ */ React.createElement(EntityRefLinks, { entityRefs: backstageIdentity.ownershipEntityRefs }))));
695
- };
696
- const UserSettingsIdentityCard = () => /* @__PURE__ */ React.createElement(InfoCard, { title: "Backstage Identity" }, /* @__PURE__ */ React.createElement(Contents, null));
697
-
698
- const UserSettingsGeneral = () => {
699
- return /* @__PURE__ */ React.createElement(Grid, { container: true, direction: "row", spacing: 3 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(UserSettingsProfileCard, null)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(UserSettingsAppearanceCard, null)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(UserSettingsIdentityCard, null)));
700
- };
701
-
702
- const LAYOUT_DATA_KEY = "plugin.user-settings.settingsLayout";
703
- const LAYOUT_ROUTE_DATA_KEY = "plugin.user-settings.settingsLayoutRoute";
704
- const Route = () => null;
705
- attachComponentData(Route, LAYOUT_ROUTE_DATA_KEY, true);
706
- attachComponentData(Route, "core.gatherMountPoints", true);
707
- const SettingsLayout = (props) => {
708
- const { title, children } = props;
709
- const { isMobile } = useSidebarPinState();
710
- const routes = useElementFilter(
711
- children,
712
- (elements) => elements.selectByComponentData({
713
- key: LAYOUT_ROUTE_DATA_KEY,
714
- withStrictError: "Child of SettingsLayout must be an SettingsLayout.Route"
715
- }).getElements().map((child) => child.props)
716
- );
717
- return /* @__PURE__ */ React.createElement(Page, { themeId: "home" }, !isMobile && /* @__PURE__ */ React.createElement(Header, { title: title != null ? title : "Settings" }), /* @__PURE__ */ React.createElement(RoutedTabs, { routes }));
718
- };
719
- attachComponentData(SettingsLayout, LAYOUT_DATA_KEY, true);
720
- SettingsLayout.Route = Route;
721
-
722
- const DefaultSettingsPage = (props) => {
723
- const { providerSettings, tabs } = props;
724
- return /* @__PURE__ */ React.createElement(SettingsLayout, null, /* @__PURE__ */ React.createElement(SettingsLayout.Route, { path: "general", title: "General" }, /* @__PURE__ */ React.createElement(UserSettingsGeneral, null)), /* @__PURE__ */ React.createElement(
725
- SettingsLayout.Route,
726
- {
727
- path: "auth-providers",
728
- title: "Authentication Providers"
729
- },
730
- /* @__PURE__ */ React.createElement(UserSettingsAuthProviders, { providerSettings })
731
- ), /* @__PURE__ */ React.createElement(SettingsLayout.Route, { path: "feature-flags", title: "Feature Flags" }, /* @__PURE__ */ React.createElement(UserSettingsFeatureFlags, null)), tabs);
732
- };
733
-
734
- const SettingsPage = (props) => {
735
- const { providerSettings } = props;
736
- const outlet = useOutlet();
737
- const layout = useElementFilter(
738
- outlet,
739
- (elements) => elements.selectByComponentData({
740
- key: LAYOUT_DATA_KEY
741
- }).getElements()
742
- );
743
- const tabs = useElementFilter(
744
- outlet,
745
- (elements) => elements.selectByComponentData({
746
- key: LAYOUT_ROUTE_DATA_KEY
747
- }).getElements()
748
- );
749
- return /* @__PURE__ */ React.createElement(React.Fragment, null, layout.length !== 0 && layout || /* @__PURE__ */ React.createElement(DefaultSettingsPage, { tabs, providerSettings }));
750
- };
751
-
752
- export { DefaultProviderSettings as D, LAYOUT_ROUTE_DATA_KEY as L, ProviderSettingsItem as P, SettingsPage as S, UserSettingsAuthProviders as U, SettingsLayout as a, UserSettingsGeneral as b, UserSettingsProfileCard as c, UserSettingsMenu as d, UserSettingsSignInAvatar as e, UserSettingsAppearanceCard as f, UserSettingsThemeToggle as g, UserSettingsPinToggle as h, UserSettingsIdentityCard as i, UserSettingsLanguageToggle as j, UserSettingsFeatureFlags as k, useUserProfile as u };
753
- //# sourceMappingURL=SettingsPage-CeSuiezj.esm.js.map