refactor(azure/aci): migrate dashboard view to react [EE-2189] (#6518)
* refactor(azure/aci): migrate dashboard view to react [EE-2189] * move aggregate function to azure utils file * fix type * introduce dashboard item component * add error notificatons * hide resource groups widget if failed to load * make dashboard a default export * revert mistake * refactor based on suggestions * use object for error data instead of array * return unused utils file * move length calculations out of return statement * only return first error of resource groups queries * refactor imports/exports, fix bug with errors & add test * WIP dashboard tests * allow mocking multiple resource groups * test for total number of resource groups * update lock file to fix lint action issue * finish dashboard tests * dashboarditem story * fix(auth): remove caching of user * add option for link to dashboard item * rename dashboard test case to match file * remove optional link and update storybook * create aria label based on already provided text * change param name to be clearerpull/6595/head^2
parent
ff7847aaa5
commit
a894e3182a
|
@ -0,0 +1,144 @@
|
|||
import { renderWithQueryClient, within } from '@/react-tools/test-utils';
|
||||
import { UserContext } from '@/portainer/hooks/useUser';
|
||||
import { UserViewModel } from '@/portainer/models/user';
|
||||
import { server, rest } from '@/setup-tests/server';
|
||||
import {
|
||||
createMockResourceGroups,
|
||||
createMockSubscriptions,
|
||||
} from '@/react-tools/test-mocks';
|
||||
|
||||
import { DashboardView } from './DashboardView';
|
||||
|
||||
jest.mock('@uirouter/react', () => ({
|
||||
...jest.requireActual('@uirouter/react'),
|
||||
useCurrentStateAndParams: jest.fn(() => ({
|
||||
params: { endpointId: 1 },
|
||||
})),
|
||||
}));
|
||||
|
||||
test('dashboard items should render correctly', async () => {
|
||||
const { getByLabelText } = await renderComponent();
|
||||
|
||||
const subscriptionsItem = getByLabelText('Subscriptions');
|
||||
expect(subscriptionsItem).toBeVisible();
|
||||
|
||||
const subscriptionElements = within(subscriptionsItem);
|
||||
expect(subscriptionElements.getByLabelText('value')).toBeVisible();
|
||||
expect(subscriptionElements.getByLabelText('icon')).toHaveClass('fa-th-list');
|
||||
expect(subscriptionElements.getByLabelText('resourceType')).toHaveTextContent(
|
||||
'Subscriptions'
|
||||
);
|
||||
|
||||
const resourceGroupsItem = getByLabelText('Resource groups');
|
||||
expect(resourceGroupsItem).toBeVisible();
|
||||
|
||||
const resourceGroupElements = within(resourceGroupsItem);
|
||||
expect(resourceGroupElements.getByLabelText('value')).toBeVisible();
|
||||
expect(resourceGroupElements.getByLabelText('icon')).toHaveClass(
|
||||
'fa-th-list'
|
||||
);
|
||||
expect(
|
||||
resourceGroupElements.getByLabelText('resourceType')
|
||||
).toHaveTextContent('Resource groups');
|
||||
});
|
||||
|
||||
test('when there are no subscriptions, should show 0 subscriptions and 0 resource groups', async () => {
|
||||
const { getByLabelText } = await renderComponent();
|
||||
|
||||
const subscriptionElements = within(getByLabelText('Subscriptions'));
|
||||
expect(subscriptionElements.getByLabelText('value')).toHaveTextContent('0');
|
||||
|
||||
const resourceGroupElements = within(getByLabelText('Resource groups'));
|
||||
expect(resourceGroupElements.getByLabelText('value')).toHaveTextContent('0');
|
||||
});
|
||||
|
||||
test('when there is subscription & resource group data, should display these', async () => {
|
||||
const { getByLabelText } = await renderComponent(1, { 'subscription-1': 2 });
|
||||
|
||||
const subscriptionElements = within(getByLabelText('Subscriptions'));
|
||||
expect(subscriptionElements.getByLabelText('value')).toHaveTextContent('1');
|
||||
|
||||
const resourceGroupElements = within(getByLabelText('Resource groups'));
|
||||
expect(resourceGroupElements.getByLabelText('value')).toHaveTextContent('2');
|
||||
});
|
||||
|
||||
test('should correctly show total number of resource groups across multiple subscriptions', async () => {
|
||||
const { getByLabelText } = await renderComponent(2, {
|
||||
'subscription-1': 2,
|
||||
'subscription-2': 3,
|
||||
});
|
||||
|
||||
const resourceGroupElements = within(getByLabelText('Resource groups'));
|
||||
expect(resourceGroupElements.getByLabelText('value')).toHaveTextContent('5');
|
||||
});
|
||||
|
||||
test('when only subscriptions fail to load, dont show the dashboard', async () => {
|
||||
const { queryByLabelText } = await renderComponent(
|
||||
1,
|
||||
{ 'subscription-1': 1 },
|
||||
500,
|
||||
200
|
||||
);
|
||||
expect(queryByLabelText('Subscriptions')).not.toBeInTheDocument();
|
||||
expect(queryByLabelText('Resource groups')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('when only resource groups fail to load, still show the subscriptions', async () => {
|
||||
const { queryByLabelText } = await renderComponent(
|
||||
1,
|
||||
{ 'subscription-1': 1 },
|
||||
200,
|
||||
500
|
||||
);
|
||||
expect(queryByLabelText('Subscriptions')).toBeInTheDocument();
|
||||
expect(queryByLabelText('Resource groups')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
async function renderComponent(
|
||||
subscriptionsCount = 0,
|
||||
resourceGroups: Record<string, number> = {},
|
||||
subscriptionsStatus = 200,
|
||||
resourceGroupsStatus = 200
|
||||
) {
|
||||
const user = new UserViewModel({ Username: 'user' });
|
||||
const state = { user };
|
||||
|
||||
server.use(
|
||||
rest.get(
|
||||
'/api/endpoints/:endpointId/azure/subscriptions',
|
||||
(req, res, ctx) =>
|
||||
res(
|
||||
ctx.json(createMockSubscriptions(subscriptionsCount)),
|
||||
ctx.status(subscriptionsStatus)
|
||||
)
|
||||
),
|
||||
rest.get(
|
||||
'/api/endpoints/:endpointId/azure/subscriptions/:subscriptionId/resourcegroups',
|
||||
(req, res, ctx) => {
|
||||
if (typeof req.params.subscriptionId !== 'string') {
|
||||
throw new Error("Provided subscriptionId must be of type: 'string'");
|
||||
}
|
||||
|
||||
const { subscriptionId } = req.params;
|
||||
return res(
|
||||
ctx.json(
|
||||
createMockResourceGroups(
|
||||
req.params.subscriptionId,
|
||||
resourceGroups[subscriptionId] || 0
|
||||
)
|
||||
),
|
||||
ctx.status(resourceGroupsStatus)
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
const renderResult = renderWithQueryClient(
|
||||
<UserContext.Provider value={state}>
|
||||
<DashboardView />
|
||||
</UserContext.Provider>
|
||||
);
|
||||
|
||||
await expect(renderResult.findByText(/Home/)).resolves.toBeVisible();
|
||||
|
||||
return renderResult;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
import { useEnvironmentId } from '@/portainer/hooks/useEnvironmentId';
|
||||
import { PageHeader } from '@/portainer/components/PageHeader';
|
||||
import { DashboardItem } from '@/portainer/components/Dashboard/DashboardItem';
|
||||
import { error as notifyError } from '@/portainer/services/notifications';
|
||||
import PortainerError from '@/portainer/error';
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
|
||||
import { useResourceGroups, useSubscriptions } from '../queries';
|
||||
|
||||
export function DashboardView() {
|
||||
const environmentId = useEnvironmentId();
|
||||
|
||||
const subscriptionsQuery = useSubscriptions(environmentId);
|
||||
useEffect(() => {
|
||||
if (subscriptionsQuery.isError) {
|
||||
notifyError(
|
||||
'Failure',
|
||||
subscriptionsQuery.error as PortainerError,
|
||||
'Unable to retrieve subscriptions'
|
||||
);
|
||||
}
|
||||
}, [subscriptionsQuery.error, subscriptionsQuery.isError]);
|
||||
|
||||
const resourceGroupsQuery = useResourceGroups(
|
||||
environmentId,
|
||||
subscriptionsQuery.data
|
||||
);
|
||||
useEffect(() => {
|
||||
if (resourceGroupsQuery.isError && resourceGroupsQuery.error) {
|
||||
notifyError(
|
||||
'Failure',
|
||||
resourceGroupsQuery.error as PortainerError,
|
||||
`Unable to retrieve resource groups`
|
||||
);
|
||||
}
|
||||
}, [resourceGroupsQuery.error, resourceGroupsQuery.isError]);
|
||||
|
||||
const isLoading =
|
||||
subscriptionsQuery.isLoading || resourceGroupsQuery.isLoading;
|
||||
if (isLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const subscriptionsCount = subscriptionsQuery?.data?.length;
|
||||
const resourceGroupsCount = Object.values(
|
||||
resourceGroupsQuery?.resourceGroups
|
||||
).flatMap((x) => Object.values(x)).length;
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader title="Home" breadcrumbs={[{ label: 'Dashboard' }]} />
|
||||
|
||||
{!subscriptionsQuery.isError && (
|
||||
<div className="row">
|
||||
<DashboardItem
|
||||
value={subscriptionsCount as number}
|
||||
icon="fa fa-th-list"
|
||||
type="Subscriptions"
|
||||
/>
|
||||
{!resourceGroupsQuery.isError && (
|
||||
<DashboardItem
|
||||
value={resourceGroupsCount as number}
|
||||
icon="fa fa-th-list"
|
||||
type="Resource groups"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const DashboardViewAngular = r2a(DashboardView, []);
|
|
@ -0,0 +1 @@
|
|||
export { DashboardViewAngular, DashboardView } from './DashboardView';
|
|
@ -1,82 +1,85 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { DashboardViewAngular } from './Dashboard/DashboardView';
|
||||
import { containerInstancesModule } from './ContainerInstances';
|
||||
|
||||
angular.module('portainer.azure', ['portainer.app', containerInstancesModule]).config([
|
||||
'$stateRegistryProvider',
|
||||
function ($stateRegistryProvider) {
|
||||
'use strict';
|
||||
angular
|
||||
.module('portainer.azure', ['portainer.app', containerInstancesModule])
|
||||
.config([
|
||||
'$stateRegistryProvider',
|
||||
function ($stateRegistryProvider) {
|
||||
'use strict';
|
||||
|
||||
var azure = {
|
||||
name: 'azure',
|
||||
url: '/azure',
|
||||
parent: 'endpoint',
|
||||
abstract: true,
|
||||
onEnter: /* @ngInject */ function onEnter($async, $state, endpoint, EndpointProvider, Notifications, StateManager) {
|
||||
return $async(async () => {
|
||||
if (endpoint.Type !== 3) {
|
||||
$state.go('portainer.home');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
|
||||
EndpointProvider.setOfflineModeFromStatus(endpoint.Status);
|
||||
await StateManager.updateEndpointState(endpoint);
|
||||
} catch (e) {
|
||||
Notifications.error('Failed loading environment', e);
|
||||
$state.go('portainer.home', {}, { reload: true });
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
var containerInstances = {
|
||||
name: 'azure.containerinstances',
|
||||
url: '/containerinstances',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/containerinstances/containerinstances.html',
|
||||
controller: 'AzureContainerInstancesController',
|
||||
var azure = {
|
||||
name: 'azure',
|
||||
url: '/azure',
|
||||
parent: 'endpoint',
|
||||
abstract: true,
|
||||
onEnter: /* @ngInject */ function onEnter($async, $state, endpoint, EndpointProvider, Notifications, StateManager) {
|
||||
return $async(async () => {
|
||||
if (endpoint.Type !== 3) {
|
||||
$state.go('portainer.home');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
|
||||
EndpointProvider.setOfflineModeFromStatus(endpoint.Status);
|
||||
await StateManager.updateEndpointState(endpoint, []);
|
||||
} catch (e) {
|
||||
Notifications.error('Failed loading environment', e);
|
||||
$state.go('portainer.home', {}, { reload: true });
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
var containerInstance = {
|
||||
name: 'azure.containerinstances.container',
|
||||
url: '/:id',
|
||||
views: {
|
||||
'content@': {
|
||||
component: 'containerInstanceDetails',
|
||||
var containerInstances = {
|
||||
name: 'azure.containerinstances',
|
||||
url: '/containerinstances',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/containerinstances/containerinstances.html',
|
||||
controller: 'AzureContainerInstancesController',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
var containerInstanceCreation = {
|
||||
name: 'azure.containerinstances.new',
|
||||
url: '/new/',
|
||||
views: {
|
||||
'content@': {
|
||||
component: 'createContainerInstanceView',
|
||||
var containerInstance = {
|
||||
name: 'azure.containerinstances.container',
|
||||
url: '/:id',
|
||||
views: {
|
||||
'content@': {
|
||||
component: 'containerInstanceDetails',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
var dashboard = {
|
||||
name: 'azure.dashboard',
|
||||
url: '/dashboard',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/dashboard/dashboard.html',
|
||||
controller: 'AzureDashboardController',
|
||||
var containerInstanceCreation = {
|
||||
name: 'azure.containerinstances.new',
|
||||
url: '/new/',
|
||||
views: {
|
||||
'content@': {
|
||||
component: 'createContainerInstanceView',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
$stateRegistryProvider.register(azure);
|
||||
$stateRegistryProvider.register(containerInstances);
|
||||
$stateRegistryProvider.register(containerInstance);
|
||||
$stateRegistryProvider.register(containerInstanceCreation);
|
||||
$stateRegistryProvider.register(dashboard);
|
||||
},
|
||||
]);
|
||||
var dashboard = {
|
||||
name: 'azure.dashboard',
|
||||
url: '/dashboard',
|
||||
views: {
|
||||
'content@': {
|
||||
component: 'dashboardView',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
$stateRegistryProvider.register(azure);
|
||||
$stateRegistryProvider.register(containerInstances);
|
||||
$stateRegistryProvider.register(containerInstance);
|
||||
$stateRegistryProvider.register(containerInstanceCreation);
|
||||
$stateRegistryProvider.register(dashboard);
|
||||
},
|
||||
])
|
||||
.component('dashboardView', DashboardViewAngular).name;
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import _ from 'lodash';
|
||||
import { useQueries, useQuery } from 'react-query';
|
||||
|
||||
import { EnvironmentId } from '@/portainer/environments/types';
|
||||
|
||||
import { getResourceGroups } from './services/resource-groups.service';
|
||||
import { getSubscriptions } from './services/subscription.service';
|
||||
import { Subscription } from './types';
|
||||
|
||||
export function useSubscriptions(environmentId: EnvironmentId) {
|
||||
return useQuery(
|
||||
'azure.subscriptions',
|
||||
() => getSubscriptions(environmentId),
|
||||
{
|
||||
meta: {
|
||||
error: {
|
||||
title: 'Failure',
|
||||
message: 'Unable to retrieve Azure subscriptions',
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function useResourceGroups(
|
||||
environmentId: EnvironmentId,
|
||||
subscriptions: Subscription[] = []
|
||||
) {
|
||||
const queries = useQueries(
|
||||
subscriptions.map((subscription) => ({
|
||||
queryKey: [
|
||||
'azure',
|
||||
environmentId,
|
||||
'subscriptions',
|
||||
subscription.subscriptionId,
|
||||
'resourceGroups',
|
||||
],
|
||||
queryFn: async () => {
|
||||
const groups = await getResourceGroups(
|
||||
environmentId,
|
||||
subscription.subscriptionId
|
||||
);
|
||||
return [subscription.subscriptionId, groups] as const;
|
||||
},
|
||||
meta: {
|
||||
error: {
|
||||
title: 'Failure',
|
||||
message: 'Unable to retrieve Azure resource groups',
|
||||
},
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
return {
|
||||
resourceGroups: Object.fromEntries(
|
||||
_.compact(
|
||||
queries.map((q) => {
|
||||
if (q.data) {
|
||||
return q.data;
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
)
|
||||
),
|
||||
isLoading: queries.some((q) => q.isLoading),
|
||||
isError: queries.some((q) => q.isError),
|
||||
error: queries.find((q) => q.error)?.error || null,
|
||||
};
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Home"></rd-header-title>
|
||||
<rd-header-content>Dashboard</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row" ng-if="subscriptions">
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<a ui-sref="azure.subscriptions">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<div class="widget-icon blue pull-left">
|
||||
<i class="fa fa-th-list"></i>
|
||||
</div>
|
||||
<div class="title">{{ subscriptions.length }}</div>
|
||||
<div class="comment">Subscriptions</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6" ng-if="resourceGroups">
|
||||
<a ui-sref="azure.resourceGroups">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<div class="widget-icon blue pull-left">
|
||||
<i class="fa fa-th-list"></i>
|
||||
</div>
|
||||
<div class="title">{{ resourceGroups.length }}</div>
|
||||
<div class="comment">Resource groups</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
|
@ -1,23 +0,0 @@
|
|||
angular.module('portainer.azure').controller('AzureDashboardController', [
|
||||
'$scope',
|
||||
'AzureService',
|
||||
'Notifications',
|
||||
function ($scope, AzureService, Notifications) {
|
||||
function initView() {
|
||||
AzureService.subscriptions()
|
||||
.then(function success(data) {
|
||||
var subscriptions = data;
|
||||
$scope.subscriptions = subscriptions;
|
||||
return AzureService.resourceGroups(subscriptions);
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.resourceGroups = AzureService.aggregate(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to load dashboard data');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -0,0 +1,36 @@
|
|||
import { Meta, Story } from '@storybook/react';
|
||||
|
||||
import { Link } from '@/portainer/components/Link';
|
||||
|
||||
import { DashboardItem } from './DashboardItem';
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'Components/DashboardItem',
|
||||
component: DashboardItem,
|
||||
};
|
||||
export default meta;
|
||||
|
||||
interface StoryProps {
|
||||
value: number;
|
||||
icon: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
function Template({ value, icon, type }: StoryProps) {
|
||||
return <DashboardItem value={value} icon={icon} type={type} />;
|
||||
}
|
||||
|
||||
export const Primary: Story<StoryProps> = Template.bind({});
|
||||
Primary.args = {
|
||||
value: 1,
|
||||
icon: 'fa fa-th-list',
|
||||
type: 'Example resource',
|
||||
};
|
||||
|
||||
export function WithLink() {
|
||||
return (
|
||||
<Link to="example.page">
|
||||
<DashboardItem value={1} icon="fa fa-th-list" type="Example resource" />
|
||||
</Link>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import { render } from '@/react-tools/test-utils';
|
||||
|
||||
import { DashboardItem } from './DashboardItem';
|
||||
|
||||
test('should show provided resource value', async () => {
|
||||
const { getByLabelText } = renderComponent(1);
|
||||
const value = getByLabelText('value');
|
||||
|
||||
expect(value).toBeVisible();
|
||||
expect(value).toHaveTextContent('1');
|
||||
});
|
||||
|
||||
test('should show provided icon', async () => {
|
||||
const { getByLabelText } = renderComponent(0, 'fa fa-th-list');
|
||||
const icon = getByLabelText('icon');
|
||||
expect(icon).toHaveClass('fa-th-list');
|
||||
});
|
||||
|
||||
test('should show provided resource type', async () => {
|
||||
const { getByLabelText } = renderComponent(0, '', 'Test');
|
||||
const title = getByLabelText('resourceType');
|
||||
|
||||
expect(title).toBeVisible();
|
||||
expect(title).toHaveTextContent('Test');
|
||||
});
|
||||
|
||||
test('should have accessibility label created from the provided resource type', async () => {
|
||||
const { getByLabelText } = renderComponent(0, '', 'testLabel');
|
||||
|
||||
expect(getByLabelText('testLabel')).toBeTruthy();
|
||||
});
|
||||
|
||||
function renderComponent(value = 0, icon = '', type = '') {
|
||||
return render(<DashboardItem value={value} icon={icon} type={type} />);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { Widget, WidgetBody } from '@/portainer/components/widget';
|
||||
|
||||
interface Props {
|
||||
value: number;
|
||||
icon: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export function DashboardItem({ value, icon, type }: Props) {
|
||||
return (
|
||||
<div className="col-sm-12 col-md-6" aria-label={type}>
|
||||
<Widget>
|
||||
<WidgetBody>
|
||||
<div className="widget-icon blue pull-left">
|
||||
<i className={icon} aria-hidden="true" aria-label="icon" />
|
||||
</div>
|
||||
<div className="title" aria-label="value">
|
||||
{value}
|
||||
</div>
|
||||
<div className="comment" aria-label="resourceType">
|
||||
{type}
|
||||
</div>
|
||||
</WidgetBody>
|
||||
</Widget>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { useCurrentStateAndParams } from '@uirouter/react';
|
||||
|
||||
export function useEnvironmentId() {
|
||||
const {
|
||||
params: { endpointId: environmentId },
|
||||
} = useCurrentStateAndParams();
|
||||
|
||||
if (!environmentId) {
|
||||
throw new Error('endpointId url param is required');
|
||||
}
|
||||
|
||||
return environmentId;
|
||||
}
|
|
@ -19,8 +19,6 @@ interface State {
|
|||
user?: UserViewModel | null;
|
||||
}
|
||||
|
||||
const state: State = {};
|
||||
|
||||
export const UserContext = createContext<State | null>(null);
|
||||
|
||||
export function useUser() {
|
||||
|
@ -93,9 +91,7 @@ export function UserProvider({ children }: UserProviderProps) {
|
|||
const [user, setUser] = useState<UserViewModel | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.user) {
|
||||
setUser(state.user);
|
||||
} else if (jwt !== '') {
|
||||
if (jwt !== '') {
|
||||
const tokenPayload = jwtDecode(jwt) as { id: number };
|
||||
|
||||
loadUser(tokenPayload.id);
|
||||
|
@ -120,7 +116,6 @@ export function UserProvider({ children }: UserProviderProps) {
|
|||
|
||||
async function loadUser(id: number) {
|
||||
const user = await getUser(id);
|
||||
state.user = user;
|
||||
setUser(user);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,3 +20,21 @@ export function createMockTeams(count: number) {
|
|||
Name: `team${value}`,
|
||||
}));
|
||||
}
|
||||
|
||||
export function createMockSubscriptions(count: number) {
|
||||
const subscriptions = _.range(1, count + 1).map((x) => ({
|
||||
id: `/subscriptions/subscription-${x}`,
|
||||
subscriptionId: `subscription-${x}`,
|
||||
}));
|
||||
|
||||
return { value: subscriptions };
|
||||
}
|
||||
|
||||
export function createMockResourceGroups(subscription: string, count: number) {
|
||||
const resourceGroups = _.range(1, count + 1).map((x) => ({
|
||||
id: `/subscriptions/${subscription}/resourceGroups/resourceGroup-${x}`,
|
||||
name: `resourcegroup-${x}`,
|
||||
}));
|
||||
|
||||
return { value: resourceGroups };
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue