Add settings page and remove popup
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-router": "6.0.0-beta.0",
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+5
-2
@@ -16,11 +16,14 @@
|
||||
|
||||
import React from 'react';
|
||||
import { List } from '@material-ui/core';
|
||||
import { InfoCard } from '@backstage/core';
|
||||
|
||||
type Props = {
|
||||
providers: React.ReactNode;
|
||||
};
|
||||
|
||||
export const AuthProvidersList = ({ providers }: Props) => (
|
||||
<List dense>{providers}</List>
|
||||
export const AuthProviders = ({ providers }: Props) => (
|
||||
<InfoCard>
|
||||
<List dense>{providers}</List>
|
||||
</InfoCard>
|
||||
);
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
} from '@backstage/core';
|
||||
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { DefaultProviderSettings } from './DefaultProviderSettings';
|
||||
|
||||
const mockSignInHandler = jest.fn().mockReturnValue('');
|
||||
|
||||
+17
-14
@@ -22,12 +22,13 @@ import {
|
||||
FeatureFlagName,
|
||||
FeatureFlagState,
|
||||
FeatureFlagsRegistryItem,
|
||||
InfoCard,
|
||||
} from '@backstage/core';
|
||||
import { FlagItem } from './FeatureFlagsItem';
|
||||
|
||||
type Props = { featureFlags: FeatureFlagsRegistryItem[] };
|
||||
|
||||
export const FeatureFlagsList = ({ featureFlags }: Props) => {
|
||||
export const FeatureFlags = ({ featureFlags }: Props) => {
|
||||
const featureFlagApi = useApi(featureFlagsApiRef);
|
||||
const initialFlagState = featureFlags.reduce(
|
||||
(result, featureFlag: FeatureFlagsRegistryItem) => {
|
||||
@@ -57,19 +58,21 @@ export const FeatureFlagsList = ({ featureFlags }: Props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<List dense>
|
||||
{featureFlags.map(featureFlag => {
|
||||
const enabled = Boolean(state[featureFlag.name]);
|
||||
<InfoCard>
|
||||
<List dense>
|
||||
{featureFlags.map(featureFlag => {
|
||||
const enabled = Boolean(state[featureFlag.name]);
|
||||
|
||||
return (
|
||||
<FlagItem
|
||||
key={featureFlag.name}
|
||||
flag={featureFlag}
|
||||
enabled={enabled}
|
||||
toggleHandler={toggleFlag}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
return (
|
||||
<FlagItem
|
||||
key={featureFlag.name}
|
||||
flag={featureFlag}
|
||||
enabled={enabled}
|
||||
toggleHandler={toggleFlag}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</InfoCard>
|
||||
);
|
||||
};
|
||||
+18
-7
@@ -13,14 +13,25 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { InfoCard } from '@backstage/core';
|
||||
import { Grid, List } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { List } from '@material-ui/core';
|
||||
import { ThemeToggle } from './ThemeToggle';
|
||||
import { PinButton } from './PinButton';
|
||||
import { Profile } from './Profile';
|
||||
import { ThemeToggle } from './ThemeToggle';
|
||||
|
||||
export const AppSettingsList = () => (
|
||||
<List dense>
|
||||
<ThemeToggle />
|
||||
<PinButton />
|
||||
</List>
|
||||
export const General = () => (
|
||||
<Grid container spacing={3}>
|
||||
<Grid item md={12}>
|
||||
<Profile />
|
||||
</Grid>
|
||||
<Grid item md={12}>
|
||||
<InfoCard>
|
||||
<List dense>
|
||||
<ThemeToggle />
|
||||
<PinButton />
|
||||
</List>
|
||||
</InfoCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
@@ -17,7 +17,7 @@
|
||||
import { SidebarPinStateContext } from '@backstage/core';
|
||||
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { PinButton } from './PinButton';
|
||||
|
||||
describe('<PinButton />', () => {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { InfoCard } from '@backstage/core';
|
||||
import { Grid, Typography } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { SignInAvatar } from './SignInAvatar';
|
||||
import { UserSettingsMenu } from './UserSettingsMenu';
|
||||
import { useUserProfile } from './useUserProfileInfo';
|
||||
|
||||
export const Profile = () => {
|
||||
const { profile, displayName } = useUserProfile();
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<Grid item md={12}>
|
||||
<InfoCard title="Profile">
|
||||
<Grid container spacing={6}>
|
||||
<Grid item>
|
||||
<SignInAvatar size={96} />
|
||||
</Grid>
|
||||
<Grid item xs={12} sm container>
|
||||
<Grid item xs container direction="column" spacing={2}>
|
||||
<Grid item xs>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
{displayName}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
{profile.email}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<UserSettingsMenu />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</InfoCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
+17
-9
@@ -14,13 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { UserSettings } from './UserSettings';
|
||||
import React from 'react';
|
||||
import { SidebarItem } from '@backstage/core';
|
||||
import { SignInAvatar } from './SignInAvatar';
|
||||
import { useUserProfile } from './useUserProfileInfo';
|
||||
import { settingsRouteRef } from '../plugin';
|
||||
|
||||
describe('<UserSettings />', () => {
|
||||
it('renders', async () => {
|
||||
const rendered = await renderWithEffects(wrapInTestApp(<UserSettings />));
|
||||
expect(rendered.getByText('G')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
export const Settings = () => {
|
||||
const { displayName } = useUserProfile();
|
||||
const SidebarAvatar = () => <SignInAvatar />;
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
text={displayName}
|
||||
to={settingsRouteRef.path}
|
||||
icon={SidebarAvatar}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
FeatureFlags,
|
||||
featureFlagsApiRef,
|
||||
} from '@backstage/core';
|
||||
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { SettingsDialog } from './SettingsDialog';
|
||||
|
||||
const apiRegistry = ApiRegistry.from([
|
||||
[featureFlagsApiRef, new FeatureFlags()],
|
||||
]);
|
||||
|
||||
describe('<SettingsDialog />', () => {
|
||||
const mockFn = jest.fn();
|
||||
|
||||
it('displays the users name and email, and the tabs and titles and updates the position', async () => {
|
||||
const rendered = await renderWithEffects(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<SettingsDialog updatePosition={mockFn} />
|
||||
</ApiProvider>,
|
||||
),
|
||||
);
|
||||
|
||||
expect(rendered.getByText('Guest')).toBeInTheDocument();
|
||||
expect(rendered.getByText('guest@example.com')).toBeInTheDocument();
|
||||
expect(rendered.getByText('App Settings')).toBeInTheDocument();
|
||||
expect(rendered.getByText('Additional Settings')).toBeInTheDocument();
|
||||
expect(rendered.getByText('Auth Providers')).toBeInTheDocument();
|
||||
expect(rendered.getByText('Feature Flags')).toBeInTheDocument();
|
||||
|
||||
const flagButton = rendered.getByText('Feature Flags');
|
||||
expect(mockFn).toBeCalledTimes(1);
|
||||
fireEvent.click(flagButton);
|
||||
expect(mockFn).toBeCalledTimes(2);
|
||||
});
|
||||
});
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ChangeEvent, useEffect, useState } from 'react';
|
||||
import { Card, CardContent, CardHeader, makeStyles } from '@material-ui/core';
|
||||
import { AppSettingsList } from './AppSettingsList';
|
||||
import { AuthProvidersList } from './AuthProviderList';
|
||||
import { FeatureFlagsList } from './FeatureFlagsList';
|
||||
import { SignInAvatar } from './SignInAvatar';
|
||||
import { UserSettingsMenu } from './UserSettingsMenu';
|
||||
import { useUserProfile } from './useUserProfileInfo';
|
||||
import {
|
||||
useApi,
|
||||
featureFlagsApiRef,
|
||||
TabbedCard,
|
||||
CardTab,
|
||||
InfoCard,
|
||||
} from '@backstage/core';
|
||||
import { DefaultProviderSettings } from './DefaultProviderSettings';
|
||||
import { ProviderSettings } from './UserSettings';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
minWidth: 400,
|
||||
maxWidth: 500,
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
providerSettings?: ProviderSettings;
|
||||
updatePosition: () => void;
|
||||
};
|
||||
|
||||
export const SettingsDialog = ({ providerSettings, updatePosition }: Props) => {
|
||||
const classes = useStyles();
|
||||
const { profile, displayName } = useUserProfile();
|
||||
const featureFlagsApi = useApi(featureFlagsApiRef);
|
||||
const featureFlags = featureFlagsApi.getRegisteredFlags();
|
||||
const [selectedTab, setSelectedTab] = useState<string | number>('auth');
|
||||
|
||||
const providers = providerSettings ?? <DefaultProviderSettings />;
|
||||
const ref = React.useRef(updatePosition);
|
||||
|
||||
const handleChange = (
|
||||
_ev: ChangeEvent<{}>,
|
||||
newSelectedTab: string | number,
|
||||
) => {
|
||||
setSelectedTab(newSelectedTab);
|
||||
};
|
||||
|
||||
// Update the position of the popover to handle different heights
|
||||
useEffect(() => {
|
||||
ref.current?.();
|
||||
}, [selectedTab]);
|
||||
|
||||
return (
|
||||
<Card className={classes.root}>
|
||||
<CardHeader
|
||||
avatar={<SignInAvatar size={48} />}
|
||||
action={<UserSettingsMenu />}
|
||||
title={displayName}
|
||||
subheader={profile.email}
|
||||
/>
|
||||
<CardContent>
|
||||
<InfoCard
|
||||
title="App Settings"
|
||||
subheader="General settings related to how the app feels and looks"
|
||||
variant="flat"
|
||||
noPadding
|
||||
>
|
||||
<AppSettingsList />
|
||||
</InfoCard>
|
||||
<TabbedCard
|
||||
title="Additional Settings"
|
||||
subheader="Settings that are specific to a plugin, or feature of the app"
|
||||
variant="flat"
|
||||
value={selectedTab}
|
||||
onChange={handleChange}
|
||||
noPadding
|
||||
>
|
||||
<CardTab value="auth" label="Auth Providers">
|
||||
<AuthProvidersList providers={providers} />
|
||||
</CardTab>
|
||||
<CardTab value="flags" label="Feature Flags">
|
||||
{featureFlags.length > 0 ? (
|
||||
<FeatureFlagsList featureFlags={featureFlags} />
|
||||
) : (
|
||||
// TODO(marcuseide): Replace with empty state component?
|
||||
<Alert severity="info">No registered Feature Flags found</Alert>
|
||||
)}
|
||||
</CardTab>
|
||||
</TabbedCard>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Content,
|
||||
featureFlagsApiRef,
|
||||
Header,
|
||||
HeaderTabs,
|
||||
Page,
|
||||
pageTheme,
|
||||
useApi,
|
||||
} from '@backstage/core';
|
||||
import { General } from './General';
|
||||
import { AuthProviders } from './AuthProviders';
|
||||
import { DefaultProviderSettings } from './DefaultProviderSettings';
|
||||
import { FeatureFlags } from './FeatureFlags';
|
||||
|
||||
type Props = {
|
||||
providerSettings?: JSX.Element;
|
||||
};
|
||||
|
||||
export const SettingsPage = ({ providerSettings }: Props) => {
|
||||
const [activeTab, setActiveTab] = useState<number>(0);
|
||||
const onTabChange = (index: number) => {
|
||||
setActiveTab(index);
|
||||
};
|
||||
const featureFlagsApi = useApi(featureFlagsApiRef);
|
||||
const featureFlags = featureFlagsApi.getRegisteredFlags();
|
||||
const providers = providerSettings ?? <DefaultProviderSettings />;
|
||||
|
||||
const tabs = [
|
||||
{ id: 'general', label: 'General' },
|
||||
{ id: 'auth-providers', label: 'Authentication Providers' },
|
||||
{ id: 'feature-flags', label: 'Feature Flags' },
|
||||
];
|
||||
|
||||
const content = [
|
||||
<General />,
|
||||
<AuthProviders providers={providers} />,
|
||||
<FeatureFlags featureFlags={featureFlags} />,
|
||||
];
|
||||
|
||||
return (
|
||||
<Page theme={pageTheme.home}>
|
||||
<Header title="Settings" />
|
||||
<HeaderTabs tabs={tabs} onChange={onTabChange} />
|
||||
<Content>{content[activeTab]}</Content>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
@@ -20,23 +20,21 @@ import { makeStyles, Avatar } from '@material-ui/core';
|
||||
import { useUserProfile } from './useUserProfileInfo';
|
||||
import { sidebarConfig } from '@backstage/core';
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme, { size: number }>({
|
||||
const useStyles = makeStyles<BackstageTheme, { size: number }>(theme => ({
|
||||
avatar: {
|
||||
width: ({ size }) => size,
|
||||
height: ({ size }) => size,
|
||||
fontSize: ({ size }) => size * 0.7,
|
||||
border: `1px solid ${theme.palette.textSubtle}`,
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
type Props = { size?: number };
|
||||
|
||||
export const SignInAvatar = ({ size }: Props) => {
|
||||
const { iconSize } = sidebarConfig;
|
||||
const classes = useStyles(size ? { size } : { size: iconSize });
|
||||
const { profile, displayName } = useUserProfile();
|
||||
const { profile } = useUserProfile();
|
||||
|
||||
return (
|
||||
<Avatar src={profile.picture} className={classes.avatar}>
|
||||
{displayName[0]}
|
||||
</Avatar>
|
||||
);
|
||||
return <Avatar src={profile.picture} className={classes.avatar} />;
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { ThemeToggle } from './ThemeToggle';
|
||||
|
||||
const mockTheme = {
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useContext, useCallback } from 'react';
|
||||
import { Popover, PopoverActions } from '@material-ui/core';
|
||||
import { SignInAvatar } from './SignInAvatar';
|
||||
import { SettingsDialog } from './SettingsDialog';
|
||||
import { SidebarItem, SidebarContext } from '@backstage/core';
|
||||
import { useUserProfile } from './useUserProfileInfo';
|
||||
|
||||
export type ProviderSettings = JSX.Element;
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
* Available auth providers. Providers will be rendered as children
|
||||
* of a MUI List.
|
||||
*
|
||||
* If undefined, providers that are configured in app-config.yaml
|
||||
* will be displayed instead.
|
||||
*/
|
||||
providerSettings?: ProviderSettings;
|
||||
};
|
||||
|
||||
export const UserSettings = ({ providerSettings }: Props) => {
|
||||
const { isOpen: sidebarOpen } = useContext(SidebarContext);
|
||||
const { displayName } = useUserProfile();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const popoverActionRef = React.useRef<PopoverActions | null>(null);
|
||||
|
||||
const updatePosition = useCallback(() => {
|
||||
popoverActionRef?.current?.updatePosition();
|
||||
}, []);
|
||||
|
||||
const handleOpen = (event?: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event?.currentTarget ?? undefined);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(undefined);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!sidebarOpen && open) setOpen(false);
|
||||
}, [open, sidebarOpen]);
|
||||
|
||||
const SidebarAvatar = () => <SignInAvatar />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarItem
|
||||
text={displayName}
|
||||
onClick={handleOpen}
|
||||
icon={SidebarAvatar}
|
||||
/>
|
||||
<Popover
|
||||
action={popoverActionRef}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'center',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
>
|
||||
<SettingsDialog
|
||||
updatePosition={updatePosition}
|
||||
providerSettings={providerSettings}
|
||||
/>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { UserSettingsMenu } from './UserSettingsMenu';
|
||||
|
||||
describe('<UserSettingsMenu />', () => {
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { plugin } from './plugin';
|
||||
export { UserSettings } from './components/UserSettings';
|
||||
export { Settings } from './components/Settings';
|
||||
export { SettingsPage as Router } from './components/SettingsPage';
|
||||
|
||||
@@ -13,7 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
|
||||
export const settingsRouteRef = createRouteRef({
|
||||
path: '/settings',
|
||||
title: 'Settings',
|
||||
});
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'user-settings',
|
||||
|
||||
Reference in New Issue
Block a user