mirror of https://github.com/portainer/portainer
refactor(ui): remove global providers [EE-4128] (#7578)
parent
d3f094cb18
commit
fad376b415
|
@ -196,6 +196,10 @@ input:checked + .slider:before {
|
||||||
padding: 1.5%;
|
padding: 1.5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.widget .widget-icon .icon {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
.widget .widget-body table thead {
|
.widget .widget-body table thead {
|
||||||
border-top: 1px solid var(--border-table-color);
|
border-top: 1px solid var(--border-table-color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,25 @@ import { CreateView } from '@/react/azure/container-instances/CreateView';
|
||||||
import { ItemView } from '@/react/azure/container-instances/ItemView';
|
import { ItemView } from '@/react/azure/container-instances/ItemView';
|
||||||
import { ListView } from '@/react/azure/container-instances/ListView';
|
import { ListView } from '@/react/azure/container-instances/ListView';
|
||||||
import { DashboardView } from '@/react/azure/DashboardView';
|
import { DashboardView } from '@/react/azure/DashboardView';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
|
||||||
export const viewsModule = angular
|
export const viewsModule = angular
|
||||||
.module('portainer.azure.react.views', [])
|
.module('portainer.azure.react.views', [])
|
||||||
.component('containerInstanceView', r2a(ItemView, []))
|
.component(
|
||||||
.component('createContainerInstanceView', r2a(CreateView, []))
|
'containerInstanceView',
|
||||||
.component('containerInstancesView', r2a(ListView, []))
|
r2a(withUIRouter(withReactQuery(withCurrentUser(ItemView))), [])
|
||||||
.component('dashboardView', r2a(DashboardView, [])).name;
|
)
|
||||||
|
.component(
|
||||||
|
'createContainerInstanceView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(CreateView))), [])
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'containerInstancesView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(ListView))), [])
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'dashboardView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(DashboardView))), [])
|
||||||
|
).name;
|
||||||
|
|
|
@ -5,12 +5,16 @@ import { StackContainersDatatable } from '@/react/docker/stacks/ItemView/StackCo
|
||||||
import { ContainerQuickActions } from '@/react/docker/containers/components/ContainerQuickActions';
|
import { ContainerQuickActions } from '@/react/docker/containers/components/ContainerQuickActions';
|
||||||
import { TemplateListDropdownAngular } from '@/react/docker/app-templates/TemplateListDropdown';
|
import { TemplateListDropdownAngular } from '@/react/docker/app-templates/TemplateListDropdown';
|
||||||
import { TemplateListSortAngular } from '@/react/docker/app-templates/TemplateListSort';
|
import { TemplateListSortAngular } from '@/react/docker/app-templates/TemplateListSort';
|
||||||
|
import { Gpu } from '@/react/docker/containers/CreateView/Gpu';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
|
||||||
export const componentsModule = angular
|
export const componentsModule = angular
|
||||||
.module('portainer.docker.react.components', [])
|
.module('portainer.docker.react.components', [])
|
||||||
.component(
|
.component(
|
||||||
'containerQuickActions',
|
'containerQuickActions',
|
||||||
r2a(ContainerQuickActions, [
|
r2a(withUIRouter(withCurrentUser(ContainerQuickActions)), [
|
||||||
'containerId',
|
'containerId',
|
||||||
'nodeName',
|
'nodeName',
|
||||||
'state',
|
'state',
|
||||||
|
@ -22,5 +26,12 @@ export const componentsModule = angular
|
||||||
.component('templateListSort', TemplateListSortAngular)
|
.component('templateListSort', TemplateListSortAngular)
|
||||||
.component(
|
.component(
|
||||||
'stackContainersDatatable',
|
'stackContainersDatatable',
|
||||||
r2a(StackContainersDatatable, ['environment', 'stackName'])
|
r2a(
|
||||||
|
withUIRouter(withReactQuery(withCurrentUser(StackContainersDatatable))),
|
||||||
|
['environment', 'stackName']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'gpu',
|
||||||
|
r2a(Gpu, ['values', 'onChange', 'gpus', 'usedGpus', 'usedAllGpus'])
|
||||||
).name;
|
).name;
|
||||||
|
|
|
@ -3,10 +3,16 @@ import angular from 'angular';
|
||||||
|
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
import { ListView } from '@/react/docker/containers/ListView';
|
import { ListView } from '@/react/docker/containers/ListView';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
|
||||||
export const containersModule = angular
|
export const containersModule = angular
|
||||||
.module('portainer.docker.containers', [])
|
.module('portainer.docker.containers', [])
|
||||||
.component('containersView', r2a(ListView, ['endpoint']))
|
.component(
|
||||||
|
'containersView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(ListView))), ['endpoint'])
|
||||||
|
)
|
||||||
|
|
||||||
.config(config).name;
|
.config(config).name;
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import { Gpu } from 'Docker/react/views/gpu';
|
|
||||||
|
|
||||||
import { ItemView as NetworksItemView } from '@/react/docker/networks/ItemView';
|
import { ItemView as NetworksItemView } from '@/react/docker/networks/ItemView';
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
|
||||||
import { containersModule } from './containers';
|
import { containersModule } from './containers';
|
||||||
|
|
||||||
export const viewsModule = angular
|
export const viewsModule = angular
|
||||||
.module('portainer.docker.react.views', [containersModule])
|
.module('portainer.docker.react.views', [containersModule])
|
||||||
|
|
||||||
.component(
|
.component(
|
||||||
'gpu',
|
'networkDetailsView',
|
||||||
r2a(Gpu, ['values', 'onChange', 'gpus', 'usedGpus', 'usedAllGpus'])
|
r2a(withUIRouter(withReactQuery(withCurrentUser(NetworksItemView))), [])
|
||||||
)
|
).name;
|
||||||
.component('networkDetailsView', r2a(NetworksItemView, [])).name;
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { useState } from 'react';
|
||||||
|
|
||||||
import { useSettings } from '@/portainer/settings/queries';
|
import { useSettings } from '@/portainer/settings/queries';
|
||||||
import { useGroups } from '@/portainer/environment-groups/queries';
|
import { useGroups } from '@/portainer/environment-groups/queries';
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
|
||||||
|
|
||||||
import { PageHeader } from '@@/PageHeader';
|
import { PageHeader } from '@@/PageHeader';
|
||||||
import { ViewLoading } from '@@/ViewLoading';
|
import { ViewLoading } from '@@/ViewLoading';
|
||||||
|
@ -55,5 +54,3 @@ export function EdgeDevicesView() {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EdgeDevicesViewAngular = r2a(EdgeDevicesView, []);
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { EdgeDevicesView, EdgeDevicesViewAngular } from './EdgeDevicesView';
|
export { EdgeDevicesView } from './EdgeDevicesView';
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { useRouter } from '@uirouter/react';
|
import { useRouter } from '@uirouter/react';
|
||||||
|
|
||||||
import { useEnvironmentList } from '@/portainer/environments/queries/useEnvironmentList';
|
import { useEnvironmentList } from '@/portainer/environments/queries/useEnvironmentList';
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
|
||||||
import { EdgeTypes } from '@/portainer/environments/types';
|
import { EdgeTypes } from '@/portainer/environments/types';
|
||||||
|
|
||||||
import { InformationPanel } from '@@/InformationPanel';
|
import { InformationPanel } from '@@/InformationPanel';
|
||||||
|
@ -58,5 +57,3 @@ export function WaitingRoomView() {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WaitingRoomViewAngular = r2a(WaitingRoomView, []);
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { WaitingRoomView, WaitingRoomViewAngular } from './WaitingRoomView';
|
export { WaitingRoomView } from './WaitingRoomView';
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
|
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
import edgeStackModule from './views/edge-stacks';
|
import edgeStackModule from './views/edge-stacks';
|
||||||
import { componentsModule } from './components';
|
import { componentsModule } from './components';
|
||||||
import { WaitingRoomViewAngular } from './EdgeDevices/WaitingRoomView';
|
import { WaitingRoomView } from './EdgeDevices/WaitingRoomView';
|
||||||
import { reactModule } from './react';
|
import { reactModule } from './react';
|
||||||
import { EdgeDevicesViewAngular } from './EdgeDevices/EdgeDevicesView';
|
|
||||||
|
|
||||||
angular
|
angular
|
||||||
.module('portainer.edge', [edgeStackModule, componentsModule, reactModule])
|
.module('portainer.edge', [edgeStackModule, componentsModule, reactModule])
|
||||||
.component('waitingRoomView', WaitingRoomViewAngular)
|
.component('waitingRoomView', r2a(withUIRouter(withReactQuery(withCurrentUser(WaitingRoomView))), []))
|
||||||
.component('edgeDevicesView', EdgeDevicesViewAngular)
|
|
||||||
.config(function config($stateRegistryProvider) {
|
.config(function config($stateRegistryProvider) {
|
||||||
const edge = {
|
const edge = {
|
||||||
name: 'edge',
|
name: 'edge',
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useSettings } from '@/portainer/settings/queries';
|
import { useSettings } from '@/portainer/settings/queries';
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
|
||||||
import { FormControl } from '@@/form-components/FormControl';
|
import { FormControl } from '@@/form-components/FormControl';
|
||||||
import { Select } from '@@/form-components/Input';
|
import { Select } from '@@/form-components/Input';
|
||||||
|
@ -58,14 +59,10 @@ export function EdgeCheckinIntervalField({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EdgeCheckinIntervalFieldAngular = r2a(EdgeCheckinIntervalField, [
|
export const EdgeCheckinIntervalFieldAngular = r2a(
|
||||||
'value',
|
withReactQuery(EdgeCheckinIntervalField),
|
||||||
'onChange',
|
['value', 'onChange', 'isDefaultHidden', 'tooltip', 'label', 'readonly']
|
||||||
'isDefaultHidden',
|
);
|
||||||
'tooltip',
|
|
||||||
'label',
|
|
||||||
'readonly',
|
|
||||||
]);
|
|
||||||
|
|
||||||
function useOptions(isDefaultHidden: boolean) {
|
function useOptions(isDefaultHidden: boolean) {
|
||||||
const [options, setOptions] = useState(checkinIntervalOptions);
|
const [options, setOptions] = useState(checkinIntervalOptions);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import angular from 'angular';
|
||||||
|
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
import { EdgeScriptForm } from '@/react/edge/components/EdgeScriptForm';
|
import { EdgeScriptForm } from '@/react/edge/components/EdgeScriptForm';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
|
||||||
import { EdgeCheckinIntervalFieldAngular } from './EdgeCheckInIntervalField';
|
import { EdgeCheckinIntervalFieldAngular } from './EdgeCheckInIntervalField';
|
||||||
|
|
||||||
|
@ -9,6 +10,10 @@ export const componentsModule = angular
|
||||||
.module('app.edge.components', [])
|
.module('app.edge.components', [])
|
||||||
.component(
|
.component(
|
||||||
'edgeScriptForm',
|
'edgeScriptForm',
|
||||||
r2a(EdgeScriptForm, ['edgeInfo', 'commands', 'isNomadTokenVisible'])
|
r2a(withReactQuery(EdgeScriptForm), [
|
||||||
|
'edgeInfo',
|
||||||
|
'commands',
|
||||||
|
'isNomadTokenVisible',
|
||||||
|
])
|
||||||
)
|
)
|
||||||
.component('edgeCheckinIntervalField', EdgeCheckinIntervalFieldAngular).name;
|
.component('edgeCheckinIntervalField', EdgeCheckinIntervalFieldAngular).name;
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
|
|
||||||
export const viewsModule = angular.module(
|
import { EdgeDevicesView } from '@/edge/EdgeDevices/EdgeDevicesView';
|
||||||
'portainer.edge.react.views',
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
[]
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
).name;
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
|
||||||
|
export const viewsModule = angular
|
||||||
|
.module('portainer.edge.react.views', [])
|
||||||
|
.component(
|
||||||
|
'edgeDevicesView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(EdgeDevicesView))), [])
|
||||||
|
).name;
|
||||||
|
|
|
@ -6,7 +6,6 @@ import settingsModule from './settings';
|
||||||
import featureFlagModule from './feature-flags';
|
import featureFlagModule from './feature-flags';
|
||||||
import userActivityModule from './user-activity';
|
import userActivityModule from './user-activity';
|
||||||
import servicesModule from './services';
|
import servicesModule from './services';
|
||||||
import homeModule from './home';
|
|
||||||
import { reactModule } from './react';
|
import { reactModule } from './react';
|
||||||
import { sidebarModule } from './react/views/sidebar';
|
import { sidebarModule } from './react/views/sidebar';
|
||||||
import environmentsModule from './environments';
|
import environmentsModule from './environments';
|
||||||
|
@ -29,7 +28,6 @@ async function initAuthentication(authManager, Authentication, $rootScope, $stat
|
||||||
|
|
||||||
angular
|
angular
|
||||||
.module('portainer.app', [
|
.module('portainer.app', [
|
||||||
homeModule,
|
|
||||||
'portainer.oauth',
|
'portainer.oauth',
|
||||||
'portainer.rbac',
|
'portainer.rbac',
|
||||||
componentsModule,
|
componentsModule,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import { useRouter } from '@uirouter/react';
|
import { useRouter } from '@uirouter/react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
|
||||||
|
|
||||||
import { PageHeader } from '@@/PageHeader';
|
import { PageHeader } from '@@/PageHeader';
|
||||||
|
|
||||||
import * as notifications from '../services/notifications';
|
import * as notifications from '../services/notifications';
|
||||||
|
@ -73,8 +71,6 @@ export function HomeView() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HomeViewAngular = r2a(HomeView, []);
|
|
||||||
|
|
||||||
async function confirmEndpointSnapshot() {
|
async function confirmEndpointSnapshot() {
|
||||||
return confirmAsync({
|
return confirmAsync({
|
||||||
title: buildTitle('Are you sure?'),
|
title: buildTitle('Are you sure?'),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { useUIState } from '@/portainer/hooks/UIStateProvider';
|
import { useUIState } from '@/portainer/hooks/useUIState';
|
||||||
|
|
||||||
import { InformationPanel } from '@@/InformationPanel';
|
import { InformationPanel } from '@@/InformationPanel';
|
||||||
|
|
||||||
|
@ -10,9 +10,13 @@ import { getMotd } from './home.service';
|
||||||
export function MotdPanel() {
|
export function MotdPanel() {
|
||||||
const motd = useMotd();
|
const motd = useMotd();
|
||||||
|
|
||||||
const [uiState, setUIState] = useUIState();
|
const uiStateStore = useUIState();
|
||||||
|
|
||||||
if (!motd || motd.Message === '' || motd.Hash === uiState.dismissedInfoHash) {
|
if (
|
||||||
|
!motd ||
|
||||||
|
motd.Message === '' ||
|
||||||
|
motd.Hash === uiStateStore.dismissedInfoHash
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,10 +38,7 @@ export function MotdPanel() {
|
||||||
);
|
);
|
||||||
|
|
||||||
function onDismiss(hash: string) {
|
function onDismiss(hash: string) {
|
||||||
setUIState({
|
uiStateStore.dismissMotd(hash);
|
||||||
...uiState,
|
|
||||||
dismissedInfoHash: hash,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1 @@
|
||||||
import angular from 'angular';
|
export { HomeView } from './HomeView';
|
||||||
|
|
||||||
import { EnvironmentListAngular } from './EnvironmentList';
|
|
||||||
import { HomeViewAngular } from './HomeView';
|
|
||||||
|
|
||||||
export default angular
|
|
||||||
.module('portainer.app.home', [])
|
|
||||||
.component('homeView', HomeViewAngular)
|
|
||||||
.component('environmentList', EnvironmentListAngular).name;
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { createContext, PropsWithChildren, useContext } from 'react';
|
|
||||||
|
|
||||||
import { useLocalStorage } from '@/portainer/hooks/useLocalStorage';
|
|
||||||
|
|
||||||
interface UIState {
|
|
||||||
dismissedInfoPanels: Record<string, string>;
|
|
||||||
dismissedInfoHash: string;
|
|
||||||
dismissedUpdateVersion: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type UIStateService = [UIState, (state: UIState) => void];
|
|
||||||
|
|
||||||
const Context = createContext<null | UIStateService>(null);
|
|
||||||
|
|
||||||
export function useUIState() {
|
|
||||||
const context = useContext(Context);
|
|
||||||
|
|
||||||
if (context == null) {
|
|
||||||
throw new Error('Should be nested under a UIStateProvider component');
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function UIStateProvider({ children }: PropsWithChildren<unknown>) {
|
|
||||||
const service = useLocalStorage<UIState>('UI_STATE', {} as UIState);
|
|
||||||
|
|
||||||
return <Context.Provider value={service}>{children}</Context.Provider>;
|
|
||||||
}
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import create from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
|
import { keyBuilder } from '@/portainer/hooks/useLocalStorage';
|
||||||
|
|
||||||
|
interface UIState {
|
||||||
|
dismissedInfoPanels: Record<string, boolean>;
|
||||||
|
dismissInfoPanel(id: string): void;
|
||||||
|
|
||||||
|
dismissedInfoHash: string;
|
||||||
|
dismissMotd(hash: string): void;
|
||||||
|
|
||||||
|
dismissedUpdateVersion: string;
|
||||||
|
dismissUpdateVersion(version: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUIState = create<UIState>()(
|
||||||
|
persist(
|
||||||
|
(set) => ({
|
||||||
|
dismissedInfoPanels: {},
|
||||||
|
dismissInfoPanel(id: string) {
|
||||||
|
set((state) => ({
|
||||||
|
dismissedInfoPanels: { ...state.dismissedInfoPanels, [id]: true },
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
dismissedInfoHash: '',
|
||||||
|
dismissMotd(hash: string) {
|
||||||
|
set({ dismissedInfoHash: hash });
|
||||||
|
},
|
||||||
|
dismissedUpdateVersion: '',
|
||||||
|
dismissUpdateVersion(version: string) {
|
||||||
|
set({ dismissedUpdateVersion: version });
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{ name: keyBuilder('NEW_UI_STATE') }
|
||||||
|
)
|
||||||
|
);
|
|
@ -23,6 +23,7 @@ interface State {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UserContext = createContext<State | null>(null);
|
export const UserContext = createContext<State | null>(null);
|
||||||
|
UserContext.displayName = 'UserContext';
|
||||||
|
|
||||||
export function useUser() {
|
export function useUser() {
|
||||||
const context = useContext(UserContext);
|
const context = useContext(UserContext);
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import { react2angular } from 'react2angular';
|
|
||||||
|
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
|
import { CreateAccessToken } from '@/react/portainer/account/CreateAccessTokenView';
|
||||||
|
import {
|
||||||
|
DefaultRegistryAction,
|
||||||
|
DefaultRegistryDomain,
|
||||||
|
DefaultRegistryName,
|
||||||
|
} from '@/react/portainer/registries/ListView/DefaultRegistry';
|
||||||
import { Icon } from '@/react/components/Icon';
|
import { Icon } from '@/react/components/Icon';
|
||||||
import { ReactQueryDevtoolsWrapper } from '@/react/components/ReactQueryDevtoolsWrapper';
|
import { ReactQueryDevtoolsWrapper } from '@/react/components/ReactQueryDevtoolsWrapper';
|
||||||
import { AccessControlPanel } from '@/react/portainer/access-control';
|
import { AccessControlPanel } from '@/react/portainer/access-control';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
import { withI18nSuspense } from '@/react-tools/withI18nSuspense';
|
||||||
|
|
||||||
import { PageHeader } from '@@/PageHeader';
|
import { PageHeader } from '@@/PageHeader';
|
||||||
import { TagSelector } from '@@/TagSelector';
|
import { TagSelector } from '@@/TagSelector';
|
||||||
|
@ -26,22 +35,25 @@ export const componentsModule = angular
|
||||||
.module('portainer.app.react.components', [customTemplatesModule])
|
.module('portainer.app.react.components', [customTemplatesModule])
|
||||||
.component(
|
.component(
|
||||||
'tagSelector',
|
'tagSelector',
|
||||||
r2a(TagSelector, ['allowCreate', 'onChange', 'value'])
|
r2a(withReactQuery(TagSelector), ['allowCreate', 'onChange', 'value'])
|
||||||
)
|
)
|
||||||
.component(
|
.component(
|
||||||
'portainerTooltip',
|
'portainerTooltip',
|
||||||
react2angular(Tooltip, ['message', 'position', 'className'])
|
r2a(Tooltip, ['message', 'position', 'className'])
|
||||||
)
|
)
|
||||||
.component('fileUploadField', fileUploadField)
|
.component('fileUploadField', fileUploadField)
|
||||||
.component('porSwitchField', switchField)
|
.component('porSwitchField', switchField)
|
||||||
.component(
|
.component(
|
||||||
'passwordCheckHint',
|
'passwordCheckHint',
|
||||||
r2a(PasswordCheckHint, ['forceChangePassword', 'passwordValid'])
|
r2a(withReactQuery(PasswordCheckHint), [
|
||||||
|
'forceChangePassword',
|
||||||
|
'passwordValid',
|
||||||
|
])
|
||||||
)
|
)
|
||||||
.component('rdLoading', r2a(Loading, []))
|
.component('rdLoading', r2a(Loading, []))
|
||||||
.component(
|
.component(
|
||||||
'tableColumnHeader',
|
'tableColumnHeader',
|
||||||
react2angular(TableColumnHeaderAngular, [
|
r2a(TableColumnHeaderAngular, [
|
||||||
'colTitle',
|
'colTitle',
|
||||||
'canSort',
|
'canSort',
|
||||||
'isSorted',
|
'isSorted',
|
||||||
|
@ -51,13 +63,13 @@ export const componentsModule = angular
|
||||||
.component('viewLoading', r2a(ViewLoading, ['message']))
|
.component('viewLoading', r2a(ViewLoading, ['message']))
|
||||||
.component(
|
.component(
|
||||||
'pageHeader',
|
'pageHeader',
|
||||||
r2a(PageHeader, [
|
r2a(withUIRouter(withReactQuery(withCurrentUser(PageHeader))), [
|
||||||
'id',
|
|
||||||
'title',
|
'title',
|
||||||
'breadcrumbs',
|
'breadcrumbs',
|
||||||
'loading',
|
'loading',
|
||||||
'onReload',
|
'onReload',
|
||||||
'reload',
|
'reload',
|
||||||
|
'id',
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
.component(
|
.component(
|
||||||
|
@ -75,7 +87,7 @@ export const componentsModule = angular
|
||||||
)
|
)
|
||||||
.component(
|
.component(
|
||||||
'prIcon',
|
'prIcon',
|
||||||
react2angular(Icon, ['className', 'feather', 'icon', 'mode', 'size'])
|
r2a(Icon, ['className', 'feather', 'icon', 'mode', 'size'])
|
||||||
)
|
)
|
||||||
.component('reactQueryDevTools', r2a(ReactQueryDevtoolsWrapper, []))
|
.component('reactQueryDevTools', r2a(ReactQueryDevtoolsWrapper, []))
|
||||||
.component(
|
.component(
|
||||||
|
@ -86,13 +98,10 @@ export const componentsModule = angular
|
||||||
'datatableSearchbar',
|
'datatableSearchbar',
|
||||||
r2a(SearchBar, ['data-cy', 'onChange', 'value', 'placeholder'])
|
r2a(SearchBar, ['data-cy', 'onChange', 'value', 'placeholder'])
|
||||||
)
|
)
|
||||||
.component(
|
.component('boxSelectorBadgeIcon', r2a(BadgeIcon, ['featherIcon', 'icon']))
|
||||||
'boxSelectorBadgeIcon',
|
|
||||||
react2angular(BadgeIcon, ['featherIcon', 'icon'])
|
|
||||||
)
|
|
||||||
.component(
|
.component(
|
||||||
'accessControlPanel',
|
'accessControlPanel',
|
||||||
r2a(AccessControlPanel, [
|
r2a(withReactQuery(withCurrentUser(AccessControlPanel)), [
|
||||||
'disableOwnershipChange',
|
'disableOwnershipChange',
|
||||||
'onUpdateSuccess',
|
'onUpdateSuccess',
|
||||||
'resourceControl',
|
'resourceControl',
|
||||||
|
@ -100,4 +109,23 @@ export const componentsModule = angular
|
||||||
'resourceType',
|
'resourceType',
|
||||||
'environmentId',
|
'environmentId',
|
||||||
])
|
])
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'defaultRegistryName',
|
||||||
|
r2a(withReactQuery(DefaultRegistryName), [])
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'defaultRegistryAction',
|
||||||
|
r2a(withReactQuery(DefaultRegistryAction), [])
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'defaultRegistryDomain',
|
||||||
|
r2a(withReactQuery(DefaultRegistryDomain), [])
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'createAccessToken',
|
||||||
|
r2a(withI18nSuspense(withUIRouter(CreateAccessToken)), [
|
||||||
|
'onSubmit',
|
||||||
|
'onError',
|
||||||
|
])
|
||||||
).name;
|
).name;
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
|
|
||||||
|
import { HomeView } from '@/portainer/home';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
import { CreateAccessToken } from '@/react/portainer/account/CreateAccessTokenView';
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
import {
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
DefaultRegistryAction,
|
|
||||||
DefaultRegistryDomain,
|
|
||||||
DefaultRegistryName,
|
|
||||||
} from '@/react/portainer/registries/ListView/DefaultRegistry';
|
|
||||||
|
|
||||||
import { wizardModule } from './wizard';
|
import { wizardModule } from './wizard';
|
||||||
import { teamsModule } from './teams';
|
import { teamsModule } from './teams';
|
||||||
|
@ -18,10 +16,7 @@ export const viewsModule = angular
|
||||||
teamsModule,
|
teamsModule,
|
||||||
updateSchedulesModule,
|
updateSchedulesModule,
|
||||||
])
|
])
|
||||||
.component('defaultRegistryName', r2a(DefaultRegistryName, []))
|
|
||||||
.component('defaultRegistryAction', r2a(DefaultRegistryAction, []))
|
|
||||||
.component('defaultRegistryDomain', r2a(DefaultRegistryDomain, []))
|
|
||||||
.component(
|
.component(
|
||||||
'createAccessToken',
|
'homeView',
|
||||||
r2a(CreateAccessToken, ['onSubmit', 'onError'])
|
r2a(withUIRouter(withReactQuery(withCurrentUser(HomeView))), [])
|
||||||
).name;
|
).name;
|
||||||
|
|
|
@ -3,8 +3,14 @@ import angular from 'angular';
|
||||||
import { AngularSidebarService } from '@/react/sidebar/useSidebarState';
|
import { AngularSidebarService } from '@/react/sidebar/useSidebarState';
|
||||||
import { Sidebar } from '@/react/sidebar/Sidebar';
|
import { Sidebar } from '@/react/sidebar/Sidebar';
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
|
||||||
export const sidebarModule = angular
|
export const sidebarModule = angular
|
||||||
.module('portainer.app.sidebar', [])
|
.module('portainer.app.sidebar', [])
|
||||||
.component('sidebar', r2a(Sidebar, []))
|
.component(
|
||||||
|
'sidebar',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(Sidebar))), [])
|
||||||
|
)
|
||||||
.factory('SidebarService', AngularSidebarService).name;
|
.factory('SidebarService', AngularSidebarService).name;
|
||||||
|
|
|
@ -3,12 +3,21 @@ import { StateRegistry } from '@uirouter/angularjs';
|
||||||
|
|
||||||
import { ItemView, ListView } from '@/react/portainer/users/teams';
|
import { ItemView, ListView } from '@/react/portainer/users/teams';
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
|
||||||
export const teamsModule = angular
|
export const teamsModule = angular
|
||||||
.module('portainer.app.teams', [])
|
.module('portainer.app.teams', [])
|
||||||
.config(config)
|
.config(config)
|
||||||
.component('teamView', r2a(ItemView, []))
|
.component(
|
||||||
.component('teamsView', r2a(ListView, [])).name;
|
'teamView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(ItemView))), [])
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'teamsView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(ListView))), [])
|
||||||
|
).name;
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
function config($stateRegistryProvider: StateRegistry) {
|
function config($stateRegistryProvider: StateRegistry) {
|
||||||
|
|
|
@ -7,12 +7,24 @@ import {
|
||||||
CreateView,
|
CreateView,
|
||||||
ItemView,
|
ItemView,
|
||||||
} from '@/react/portainer/environments/update-schedules';
|
} from '@/react/portainer/environments/update-schedules';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
|
||||||
export const updateSchedulesModule = angular
|
export const updateSchedulesModule = angular
|
||||||
.module('portainer.edge.updateSchedules', [])
|
.module('portainer.edge.updateSchedules', [])
|
||||||
.component('updateSchedulesListView', r2a(ListView, []))
|
.component(
|
||||||
.component('updateSchedulesCreateView', r2a(CreateView, []))
|
'updateSchedulesListView',
|
||||||
.component('updateSchedulesItemView', r2a(ItemView, []))
|
r2a(withUIRouter(withReactQuery(withCurrentUser(ListView))), [])
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'updateSchedulesCreateView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(CreateView))), [])
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'updateSchedulesItemView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(ItemView))), [])
|
||||||
|
)
|
||||||
.config(config).name;
|
.config(config).name;
|
||||||
|
|
||||||
function config($stateRegistryProvider: StateRegistry) {
|
function config($stateRegistryProvider: StateRegistry) {
|
||||||
|
|
|
@ -7,15 +7,30 @@ import {
|
||||||
EnvironmentTypeSelectView,
|
EnvironmentTypeSelectView,
|
||||||
HomeView,
|
HomeView,
|
||||||
} from '@/react/portainer/environments/wizard';
|
} from '@/react/portainer/environments/wizard';
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
|
||||||
export const wizardModule = angular
|
export const wizardModule = angular
|
||||||
.module('portainer.app.react.views.wizard', [])
|
.module('portainer.app.react.views.wizard', [])
|
||||||
.component('wizardEnvironmentCreationView', r2a(EnvironmentCreationView, []))
|
.component(
|
||||||
|
'wizardEnvironmentCreationView',
|
||||||
|
r2a(
|
||||||
|
withUIRouter(withReactQuery(withCurrentUser(EnvironmentCreationView))),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
)
|
||||||
.component(
|
.component(
|
||||||
'wizardEnvironmentTypeSelectView',
|
'wizardEnvironmentTypeSelectView',
|
||||||
r2a(EnvironmentTypeSelectView, [])
|
r2a(
|
||||||
|
withUIRouter(withReactQuery(withCurrentUser(EnvironmentTypeSelectView))),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.component(
|
||||||
|
'wizardMainView',
|
||||||
|
r2a(withUIRouter(withReactQuery(withCurrentUser(HomeView))), [])
|
||||||
)
|
)
|
||||||
.component('wizardMainView', r2a(HomeView, []))
|
|
||||||
.config(config).name;
|
.config(config).name;
|
||||||
|
|
||||||
function config($stateRegistryProvider: StateRegistry) {
|
function config($stateRegistryProvider: StateRegistry) {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||||
import { r2a } from '@/react-tools/react2angular';
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
|
||||||
import { Settings } from '../types';
|
import { Settings } from '../types';
|
||||||
|
|
||||||
|
@ -20,7 +22,7 @@ export function EdgeComputeSettingsView({ settings, onSubmit }: Props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EdgeComputeSettingsViewAngular = r2a(EdgeComputeSettingsView, [
|
export const EdgeComputeSettingsViewAngular = r2a(
|
||||||
'settings',
|
withReactQuery(withCurrentUser(EdgeComputeSettingsView)),
|
||||||
'onSubmit',
|
['settings', 'onSubmit']
|
||||||
]);
|
);
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { react2angular } from '@/react-tools/react2angular';
|
import { react2angular } from '@/react-tools/react2angular';
|
||||||
|
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||||
|
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||||
|
|
||||||
import { SettingsFDO } from './SettingsFDO';
|
import { SettingsFDO } from './SettingsFDO';
|
||||||
|
|
||||||
const SettingsFDOAngular = react2angular(SettingsFDO, ['settings', 'onSubmit']);
|
const SettingsFDOAngular = react2angular(
|
||||||
|
withUIRouter(withReactQuery(SettingsFDO)),
|
||||||
|
['settings', 'onSubmit']
|
||||||
|
);
|
||||||
export { SettingsFDO, SettingsFDOAngular };
|
export { SettingsFDO, SettingsFDOAngular };
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { QueryClientProvider } from 'react-query';
|
|
||||||
import { UIRouterContextComponent } from '@uirouter/react-hybrid';
|
|
||||||
import { PropsWithChildren, StrictMode } from 'react';
|
|
||||||
|
|
||||||
import { UserProvider } from '@/portainer/hooks/useUser';
|
|
||||||
import { UIStateProvider } from '@/portainer/hooks/UIStateProvider';
|
|
||||||
|
|
||||||
import { createQueryClient } from './react-query';
|
|
||||||
|
|
||||||
const queryClient = createQueryClient();
|
|
||||||
|
|
||||||
export function RootProvider({ children }: PropsWithChildren<unknown>) {
|
|
||||||
return (
|
|
||||||
<StrictMode>
|
|
||||||
<QueryClientProvider client={queryClient}>
|
|
||||||
<UIStateProvider>
|
|
||||||
<UIRouterContextComponent>
|
|
||||||
<UserProvider>{children}</UserProvider>
|
|
||||||
</UIRouterContextComponent>
|
|
||||||
</UIStateProvider>
|
|
||||||
</QueryClientProvider>
|
|
||||||
</StrictMode>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -93,3 +93,5 @@ function handleError(error: unknown, errorMeta?: unknown) {
|
||||||
notifyError(title, error as Error, message);
|
notifyError(title, error as Error, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const queryClient = createQueryClient();
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { IComponentOptions, IController } from 'angular';
|
import { IComponentOptions, IController } from 'angular';
|
||||||
import { Suspense } from 'react';
|
import { StrictMode } from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { RootProvider } from './RootProvider';
|
|
||||||
|
|
||||||
function toProps(
|
function toProps(
|
||||||
propNames: string[],
|
propNames: string[],
|
||||||
controller: IController,
|
controller: IController,
|
||||||
|
@ -54,20 +52,29 @@ export function react2angular<T, U extends PropNames<T>[]>(
|
||||||
$element: HTMLElement[],
|
$element: HTMLElement[],
|
||||||
$q: ng.IQService
|
$q: ng.IQService
|
||||||
) {
|
) {
|
||||||
|
let isDestroyed = false;
|
||||||
const el = $element[0];
|
const el = $element[0];
|
||||||
|
|
||||||
this.$onChanges = () => {
|
this.$onChanges = () => {
|
||||||
const props = toProps(propNames, this, $q);
|
if (!isDestroyed) {
|
||||||
ReactDOM.render(
|
const props = toProps(propNames, this, $q);
|
||||||
<Suspense fallback="loading translations">
|
ReactDOM.render(
|
||||||
<RootProvider>
|
<StrictMode>
|
||||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||||
<Component {...(props as T)} />
|
<Component {...(props as T)} />
|
||||||
</RootProvider>
|
</StrictMode>,
|
||||||
</Suspense>,
|
|
||||||
el
|
el
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$onDestroy = () => {
|
||||||
|
if (!isDestroyed) {
|
||||||
|
isDestroyed = true;
|
||||||
|
ReactDOM.unmountComponentAtNode(el);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
this.$onDestroy = () => ReactDOM.unmountComponentAtNode(el);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { ComponentType } from 'react';
|
||||||
|
|
||||||
|
import { UserProvider } from '@/portainer/hooks/useUser';
|
||||||
|
|
||||||
|
export function withCurrentUser<T>(
|
||||||
|
WrappedComponent: ComponentType<T>
|
||||||
|
): ComponentType<T> {
|
||||||
|
// Try to create a nice displayName for React Dev Tools.
|
||||||
|
const displayName =
|
||||||
|
WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
||||||
|
|
||||||
|
function WrapperComponent(props: T) {
|
||||||
|
return (
|
||||||
|
<UserProvider>
|
||||||
|
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||||
|
<WrappedComponent {...props} />
|
||||||
|
</UserProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperComponent.displayName = displayName;
|
||||||
|
|
||||||
|
return WrapperComponent;
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { ComponentType, Suspense } from 'react';
|
||||||
|
|
||||||
|
export function withI18nSuspense<T>(
|
||||||
|
WrappedComponent: ComponentType<T>
|
||||||
|
): ComponentType<T> {
|
||||||
|
// Try to create a nice displayName for React Dev Tools.
|
||||||
|
const displayName =
|
||||||
|
WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
||||||
|
|
||||||
|
function WrapperComponent(props: T) {
|
||||||
|
return (
|
||||||
|
<Suspense fallback="Loading translations...">
|
||||||
|
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||||
|
<WrappedComponent {...props} />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperComponent.displayName = displayName;
|
||||||
|
|
||||||
|
return WrapperComponent;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { ComponentType } from 'react';
|
||||||
|
import { QueryClientProvider } from 'react-query';
|
||||||
|
|
||||||
|
import { queryClient as defaultQueryClient } from './react-query';
|
||||||
|
|
||||||
|
export function withReactQuery<T>(
|
||||||
|
WrappedComponent: ComponentType<T>,
|
||||||
|
queryClient = defaultQueryClient
|
||||||
|
): ComponentType<T> {
|
||||||
|
// Try to create a nice displayName for React Dev Tools.
|
||||||
|
const displayName =
|
||||||
|
WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
||||||
|
|
||||||
|
function WrapperComponent(props: T) {
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||||
|
<WrappedComponent {...props} />
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperComponent.displayName = displayName;
|
||||||
|
|
||||||
|
return WrapperComponent;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentType } from 'react';
|
||||||
|
import { UIRouterContextComponent } from '@uirouter/react-hybrid';
|
||||||
|
|
||||||
|
export function withUIRouter<T>(
|
||||||
|
WrappedComponent: ComponentType<T>
|
||||||
|
): ComponentType<T> {
|
||||||
|
// Try to create a nice displayName for React Dev Tools.
|
||||||
|
const displayName =
|
||||||
|
WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
||||||
|
|
||||||
|
function WrapperComponent(props: T) {
|
||||||
|
return (
|
||||||
|
<UIRouterContextComponent>
|
||||||
|
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||||
|
<WrappedComponent {...props} />
|
||||||
|
</UIRouterContextComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperComponent.displayName = displayName;
|
||||||
|
|
||||||
|
return WrapperComponent;
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import clsx from 'clsx';
|
||||||
import styles from './HeaderContainer.module.css';
|
import styles from './HeaderContainer.module.css';
|
||||||
|
|
||||||
const Context = createContext<null | boolean>(null);
|
const Context = createContext<null | boolean>(null);
|
||||||
|
Context.displayName = 'PageHeaderContext';
|
||||||
|
|
||||||
export function useHeaderContext() {
|
export function useHeaderContext() {
|
||||||
const context = useContext(Context);
|
const context = useContext(Context);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { createContext, PropsWithChildren, useContext } from 'react';
|
import { createContext, PropsWithChildren, useContext } from 'react';
|
||||||
|
|
||||||
const Context = createContext<null | boolean>(null);
|
const Context = createContext<null | boolean>(null);
|
||||||
|
Context.displayName = 'WidgetContext';
|
||||||
|
|
||||||
export function useWidgetContext() {
|
export function useWidgetContext() {
|
||||||
const context = useContext(Context);
|
const context = useContext(Context);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { createContext, PropsWithChildren, useContext } from 'react';
|
||||||
|
|
||||||
export function createRowContext<TContext>() {
|
export function createRowContext<TContext>() {
|
||||||
const Context = createContext<TContext | null>(null);
|
const Context = createContext<TContext | null>(null);
|
||||||
|
Context.displayName = 'RowContext';
|
||||||
|
|
||||||
return { RowProvider, useRowContext };
|
return { RowProvider, useRowContext };
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ interface TableSettingsContextInterface<T> {
|
||||||
const TableSettingsContext = createContext<TableSettingsContextInterface<
|
const TableSettingsContext = createContext<TableSettingsContextInterface<
|
||||||
Record<string, unknown>
|
Record<string, unknown>
|
||||||
> | null>(null);
|
> | null>(null);
|
||||||
|
TableSettingsContext.displayName = 'TableSettingsContext';
|
||||||
|
|
||||||
export function useTableSettings<T>() {
|
export function useTableSettings<T>() {
|
||||||
const Context = getContextType<T>();
|
const Context = getContextType<T>();
|
||||||
|
|
|
@ -7,6 +7,7 @@ interface TableSettingsContextInterface<T> {
|
||||||
const TableSettingsContext = createContext<TableSettingsContextInterface<
|
const TableSettingsContext = createContext<TableSettingsContextInterface<
|
||||||
Record<string, unknown>
|
Record<string, unknown>
|
||||||
> | null>(null);
|
> | null>(null);
|
||||||
|
TableSettingsContext.displayName = 'TableSettingsContext';
|
||||||
|
|
||||||
export function useTableSettings<T>() {
|
export function useTableSettings<T>() {
|
||||||
const Context = getContextType<T>();
|
const Context = getContextType<T>();
|
||||||
|
|
|
@ -2,6 +2,7 @@ import clsx from 'clsx';
|
||||||
import { createContext, PropsWithChildren, useContext } from 'react';
|
import { createContext, PropsWithChildren, useContext } from 'react';
|
||||||
|
|
||||||
const Context = createContext<null | boolean>(null);
|
const Context = createContext<null | boolean>(null);
|
||||||
|
Context.displayName = 'InputGroupContext';
|
||||||
|
|
||||||
type Size = 'small' | 'large';
|
type Size = 'small' | 'large';
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,14 @@ import { useQuery } from 'react-query';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { getVersionStatus } from '@/portainer/services/api/status.service';
|
import { getVersionStatus } from '@/portainer/services/api/status.service';
|
||||||
import { useUIState } from '@/portainer/hooks/UIStateProvider';
|
import { useUIState } from '@/portainer/hooks/useUIState';
|
||||||
|
|
||||||
import { Icon } from '@@/Icon';
|
import { Icon } from '@@/Icon';
|
||||||
|
|
||||||
import styles from './UpdateNotifications.module.css';
|
import styles from './UpdateNotifications.module.css';
|
||||||
|
|
||||||
export function UpdateNotification() {
|
export function UpdateNotification() {
|
||||||
const [uiState, setUIState] = useUIState();
|
const uiStateStore = useUIState();
|
||||||
const query = useUpdateNotification();
|
const query = useUpdateNotification();
|
||||||
|
|
||||||
if (!query.data || !query.data.UpdateAvailable) {
|
if (!query.data || !query.data.UpdateAvailable) {
|
||||||
|
@ -19,9 +19,9 @@ export function UpdateNotification() {
|
||||||
const { LatestVersion } = query.data;
|
const { LatestVersion } = query.data;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
uiState?.dismissedUpdateVersion?.length > 0 &&
|
!!uiStateStore.dismissedUpdateVersion &&
|
||||||
LatestVersion?.length > 0 &&
|
LatestVersion?.length > 0 &&
|
||||||
uiState?.dismissedUpdateVersion === LatestVersion
|
uiStateStore.dismissedUpdateVersion === LatestVersion
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -63,10 +63,7 @@ export function UpdateNotification() {
|
||||||
);
|
);
|
||||||
|
|
||||||
function onDismiss(version: string) {
|
function onDismiss(version: string) {
|
||||||
setUIState({
|
uiStateStore.dismissUpdateVersion(version);
|
||||||
...uiState,
|
|
||||||
dismissedUpdateVersion: version,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ interface State {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Context = createContext<State | null>(null);
|
export const Context = createContext<State | null>(null);
|
||||||
|
Context.displayName = 'SidebarContext';
|
||||||
|
|
||||||
export function useSidebarState() {
|
export function useSidebarState() {
|
||||||
const context = useContext(Context);
|
const context = useContext(Context);
|
||||||
|
|
|
@ -133,7 +133,6 @@
|
||||||
"react-select": "^5.2.1",
|
"react-select": "^5.2.1",
|
||||||
"react-table": "^7.7.0",
|
"react-table": "^7.7.0",
|
||||||
"react-tooltip": "^4.2.21",
|
"react-tooltip": "^4.2.21",
|
||||||
"react2angular": "^4.0.6",
|
|
||||||
"sanitize-html": "^2.5.3",
|
"sanitize-html": "^2.5.3",
|
||||||
"semver-compare": "^1.0.0",
|
"semver-compare": "^1.0.0",
|
||||||
"spinkit": "^2.0.1",
|
"spinkit": "^2.0.1",
|
||||||
|
|
49
yarn.lock
49
yarn.lock
|
@ -4397,10 +4397,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
|
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
|
||||||
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
||||||
|
|
||||||
"@types/angular@^1.6.39", "@types/angular@^1.8.3":
|
"@types/angular@^1.8.3":
|
||||||
version "1.8.4"
|
version "1.8.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.8.4.tgz#a2cc163e508389c51d4c4119ebff6b9395cec472"
|
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.8.3.tgz#97602244b685113ce9dc27823471b67d28f5ba3e"
|
||||||
integrity sha512-wPS/ncJWhyxJsndsW1B6Ta8D4mi97x1yItSu+rkLDytU3oRIh2CFAjMuJceYwFAh9+DIohndWM0QBA9OU2Hv0g==
|
integrity sha512-vgc5Z+TD07DT7NEUjFm6XMp0kEbGXIa95XmOL5IiHXR9LdrJpcdDh3jl1nCuZbWyzFn5/1OqtMfomcnA1sUFXQ==
|
||||||
|
|
||||||
"@types/aria-query@^4.2.0":
|
"@types/aria-query@^4.2.0":
|
||||||
version "4.2.2"
|
version "4.2.2"
|
||||||
|
@ -4678,14 +4678,7 @@
|
||||||
"@types/interpret" "*"
|
"@types/interpret" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/lodash.frompairs@^4.0.5":
|
"@types/lodash@^4.14.167":
|
||||||
version "4.0.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash.frompairs/-/lodash.frompairs-4.0.7.tgz#ee9b800b4dab37cd063dcc652a2bd86f7ff984b7"
|
|
||||||
integrity sha512-7UeH2+GF9yop4AqnPwae5/2TE+eY0WRDy0RRQtNGHjzIgdUhilRskMXvXqUcCSazvbkxasjqydXrIE1OB6bPKA==
|
|
||||||
dependencies:
|
|
||||||
"@types/lodash" "*"
|
|
||||||
|
|
||||||
"@types/lodash@*", "@types/lodash@^4.14.167", "@types/lodash@^4.14.85":
|
|
||||||
version "4.14.182"
|
version "4.14.182"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2"
|
||||||
integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==
|
integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==
|
||||||
|
@ -5694,11 +5687,6 @@ angular@1.8.2, angular@^1.3:
|
||||||
resolved "https://registry.yarnpkg.com/angular/-/angular-1.8.2.tgz#5983bbb5a9fa63e213cb7749199e0d352de3a2f1"
|
resolved "https://registry.yarnpkg.com/angular/-/angular-1.8.2.tgz#5983bbb5a9fa63e213cb7749199e0d352de3a2f1"
|
||||||
integrity sha512-IauMOej2xEe7/7Ennahkbb5qd/HFADiNuLSESz9Q27inmi32zB0lnAsFeLEWcox3Gd1F6YhNd1CP7/9IukJ0Gw==
|
integrity sha512-IauMOej2xEe7/7Ennahkbb5qd/HFADiNuLSESz9Q27inmi32zB0lnAsFeLEWcox3Gd1F6YhNd1CP7/9IukJ0Gw==
|
||||||
|
|
||||||
angular@>=1.5, angular@>=1.5.0:
|
|
||||||
version "1.8.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/angular/-/angular-1.8.3.tgz#851ad75d5163c105a7e329555ef70c90aa706894"
|
|
||||||
integrity sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw==
|
|
||||||
|
|
||||||
angularjs-scroll-glue@^2.2.0:
|
angularjs-scroll-glue@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/angularjs-scroll-glue/-/angularjs-scroll-glue-2.2.0.tgz#07d3399ac16ca874c63b6b5ee2ee30558b37e5d1"
|
resolved "https://registry.yarnpkg.com/angularjs-scroll-glue/-/angularjs-scroll-glue-2.2.0.tgz#07d3399ac16ca874c63b6b5ee2ee30558b37e5d1"
|
||||||
|
@ -12858,11 +12846,6 @@ lodash.debounce@^4.0.8:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||||
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
||||||
|
|
||||||
lodash.frompairs@^4.0.1:
|
|
||||||
version "4.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/lodash.frompairs/-/lodash.frompairs-4.0.1.tgz#bc4e5207fa2757c136e573614e9664506b2b1bd2"
|
|
||||||
integrity sha512-dvqe2I+cO5MzXCMhUnfYFa9MD+/760yx2aTAN1lqEcEkf896TxgrX373igVdqSJj6tQd0jnSLE1UMuKufqqxFw==
|
|
||||||
|
|
||||||
lodash.get@^4.4.2:
|
lodash.get@^4.4.2:
|
||||||
version "4.4.2"
|
version "4.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||||
|
@ -12893,7 +12876,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||||
|
|
||||||
lodash@^3.10.0, lodash@^3.6.0, lodash@^4.0.0, lodash@^4.11.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.7.0, lodash@~2.4.1, lodash@~4.17.15, lodash@~4.17.19, lodash@~4.17.21:
|
lodash@^3.10.0, lodash@^3.6.0, lodash@^4.0.0, lodash@^4.11.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.3.0, lodash@^4.7.0, lodash@~2.4.1, lodash@~4.17.15, lodash@~4.17.19, lodash@~4.17.21:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
@ -13613,16 +13596,6 @@ ng-file-upload@~12.2.13:
|
||||||
resolved "https://registry.yarnpkg.com/ng-file-upload/-/ng-file-upload-12.2.13.tgz#01800f3872e526f95310f8477e99e4f12d0d8d14"
|
resolved "https://registry.yarnpkg.com/ng-file-upload/-/ng-file-upload-12.2.13.tgz#01800f3872e526f95310f8477e99e4f12d0d8d14"
|
||||||
integrity sha1-AYAPOHLlJvlTEPhHfpnk8S0NjRQ=
|
integrity sha1-AYAPOHLlJvlTEPhHfpnk8S0NjRQ=
|
||||||
|
|
||||||
ngcomponent@^4.1.0:
|
|
||||||
version "4.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ngcomponent/-/ngcomponent-4.1.0.tgz#793e379138f552ea0cd2c767ad0aa7057678e228"
|
|
||||||
integrity sha512-cGL3iVoqMWTpCfaIwgRKhdaGqiy2Z+CCG0cVfjlBvdqE8saj8xap9B4OTf+qwObxLVZmDTJPDgx3bN6Q/lZ7BQ==
|
|
||||||
dependencies:
|
|
||||||
"@types/angular" "^1.6.39"
|
|
||||||
"@types/lodash" "^4.14.85"
|
|
||||||
angular ">=1.5.0"
|
|
||||||
lodash "^4.17.4"
|
|
||||||
|
|
||||||
ngtemplate-loader@^2.1.0:
|
ngtemplate-loader@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/ngtemplate-loader/-/ngtemplate-loader-2.1.0.tgz#fb71c614c509b114b3bb28a828cb608757531e96"
|
resolved "https://registry.yarnpkg.com/ngtemplate-loader/-/ngtemplate-loader-2.1.0.tgz#fb71c614c509b114b3bb28a828cb608757531e96"
|
||||||
|
@ -15735,16 +15708,6 @@ react-transition-group@^4.3.0:
|
||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
react2angular@^4.0.6:
|
|
||||||
version "4.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/react2angular/-/react2angular-4.0.6.tgz#ec49ef834d101c9a320e25229fc5afa5b29edc4f"
|
|
||||||
integrity sha512-MDl2WRoTyu7Gyh4+FAIlmsM2mxIa/DjSz6G/d90L1tK8ZRubqVEayKF6IPyAruC5DMhGDVJ7tlAIcu/gMNDjXg==
|
|
||||||
dependencies:
|
|
||||||
"@types/lodash.frompairs" "^4.0.5"
|
|
||||||
angular ">=1.5"
|
|
||||||
lodash.frompairs "^4.0.1"
|
|
||||||
ngcomponent "^4.1.0"
|
|
||||||
|
|
||||||
react@^17.0.2:
|
react@^17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
|
|
Loading…
Reference in New Issue