mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
refactor(app): details widget migration [EE-5352] (#8886)
This commit is contained in:
parent
fdd79cece8
commit
af77e33993
57 changed files with 2046 additions and 1079 deletions
0
app/react/common/stacks/CreateView/.keep
Normal file
0
app/react/common/stacks/CreateView/.keep
Normal file
0
app/react/common/stacks/ItemView/.keep
Normal file
0
app/react/common/stacks/ItemView/.keep
Normal file
106
app/react/common/stacks/ItemView/StackContainersDatatable.tsx
Normal file
106
app/react/common/stacks/ItemView/StackContainersDatatable.tsx
Normal file
|
@ -0,0 +1,106 @@
|
|||
import { Box } from 'lucide-react';
|
||||
|
||||
import { DockerContainer } from '@/react/docker/containers/types';
|
||||
import { Environment } from '@/react/portainer/environments/types';
|
||||
import { createStore } from '@/react/docker/containers/ListView/ContainersDatatable/datatable-store';
|
||||
import { useColumns } from '@/react/docker/containers/ListView/ContainersDatatable/columns';
|
||||
import { ContainersDatatableActions } from '@/react/docker/containers/ListView/ContainersDatatable/ContainersDatatableActions';
|
||||
import { ContainersDatatableSettings } from '@/react/docker/containers/ListView/ContainersDatatable/ContainersDatatableSettings';
|
||||
import { useShowGPUsColumn } from '@/react/docker/containers/utils';
|
||||
|
||||
import { Datatable, Table } from '@@/datatables';
|
||||
import {
|
||||
buildAction,
|
||||
QuickActionsSettings,
|
||||
} from '@@/datatables/QuickActionsSettings';
|
||||
import { ColumnVisibilityMenu } from '@@/datatables/ColumnVisibilityMenu';
|
||||
import { TableSettingsProvider } from '@@/datatables/useTableSettings';
|
||||
import { useTableState } from '@@/datatables/useTableState';
|
||||
|
||||
import { useContainers } from '../../../docker/containers/queries/containers';
|
||||
import { RowProvider } from '../../../docker/containers/ListView/ContainersDatatable/RowContext';
|
||||
|
||||
const storageKey = 'stack-containers';
|
||||
const settingsStore = createStore(storageKey);
|
||||
|
||||
const actions = [
|
||||
buildAction('logs', 'Logs'),
|
||||
buildAction('inspect', 'Inspect'),
|
||||
buildAction('stats', 'Stats'),
|
||||
buildAction('exec', 'Console'),
|
||||
buildAction('attach', 'Attach'),
|
||||
];
|
||||
|
||||
export interface Props {
|
||||
environment: Environment;
|
||||
stackName: string;
|
||||
}
|
||||
|
||||
export function StackContainersDatatable({ environment, stackName }: Props) {
|
||||
const tableState = useTableState(settingsStore, storageKey);
|
||||
|
||||
const isGPUsColumnVisible = useShowGPUsColumn(environment.Id);
|
||||
const columns = useColumns(false, isGPUsColumnVisible);
|
||||
|
||||
const containersQuery = useContainers(environment.Id, {
|
||||
filters: {
|
||||
label: [`com.docker.compose.project=${stackName}`],
|
||||
},
|
||||
autoRefreshRate: tableState.autoRefreshRate * 1000,
|
||||
});
|
||||
|
||||
return (
|
||||
<RowProvider context={{ environment }}>
|
||||
<TableSettingsProvider settings={settingsStore}>
|
||||
<Datatable
|
||||
title="Containers"
|
||||
titleIcon={Box}
|
||||
settingsManager={tableState}
|
||||
columns={columns}
|
||||
renderTableActions={(selectedRows) => (
|
||||
<ContainersDatatableActions
|
||||
selectedItems={selectedRows}
|
||||
isAddActionVisible={false}
|
||||
endpointId={environment.Id}
|
||||
/>
|
||||
)}
|
||||
initialTableState={{
|
||||
columnVisibility: Object.fromEntries(
|
||||
tableState.hiddenColumns.map((col) => [col, false])
|
||||
),
|
||||
}}
|
||||
renderTableSettings={(tableInstance) => {
|
||||
const columnsToHide = tableInstance
|
||||
.getAllColumns()
|
||||
.filter((col) => col.getCanHide());
|
||||
|
||||
return (
|
||||
<>
|
||||
<ColumnVisibilityMenu<DockerContainer>
|
||||
columns={columnsToHide}
|
||||
onChange={(hiddenColumns) => {
|
||||
tableState.setHiddenColumns(hiddenColumns);
|
||||
tableInstance.setColumnVisibility(
|
||||
Object.fromEntries(
|
||||
hiddenColumns.map((col) => [col, false])
|
||||
)
|
||||
);
|
||||
}}
|
||||
value={tableState.hiddenColumns}
|
||||
/>
|
||||
<Table.SettingsMenu
|
||||
quickActions={<QuickActionsSettings actions={actions} />}
|
||||
>
|
||||
<ContainersDatatableSettings settings={tableState} />
|
||||
</Table.SettingsMenu>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
dataset={containersQuery.data || []}
|
||||
isLoading={containersQuery.isLoading}
|
||||
emptyContentLabel="No containers found"
|
||||
/>
|
||||
</TableSettingsProvider>
|
||||
</RowProvider>
|
||||
);
|
||||
}
|
0
app/react/common/stacks/ListView/.keep
Normal file
0
app/react/common/stacks/ListView/.keep
Normal file
21
app/react/common/stacks/common/confirm-stack-update.ts
Normal file
21
app/react/common/stacks/common/confirm-stack-update.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { openSwitchPrompt } from '@@/modals/SwitchPrompt';
|
||||
import { ModalType } from '@@/modals';
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
|
||||
export async function confirmStackUpdate(
|
||||
message: string,
|
||||
defaultValue: boolean
|
||||
) {
|
||||
const result = await openSwitchPrompt(
|
||||
'Are you sure?',
|
||||
'Re-pull image and redeploy',
|
||||
{
|
||||
message,
|
||||
confirmButton: buildConfirmButton('Update'),
|
||||
modalType: ModalType.Warn,
|
||||
defaultValue,
|
||||
}
|
||||
);
|
||||
|
||||
return result ? { pullImage: result.value } : undefined;
|
||||
}
|
1
app/react/common/stacks/readme.md
Normal file
1
app/react/common/stacks/readme.md
Normal file
|
@ -0,0 +1 @@
|
|||
Stacks are placed in the `/app/react/common` folder, because they are used by both Kubernetes and Docker environments and are saved locally to the Portainer database.
|
25
app/react/common/stacks/stack.service.ts
Normal file
25
app/react/common/stacks/stack.service.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { useQuery } from 'react-query';
|
||||
|
||||
import axios from '@/portainer/services/axios';
|
||||
import { withError } from '@/react-tools/react-query';
|
||||
|
||||
import { StackFile, StackId } from './types';
|
||||
|
||||
const queryKeys = {
|
||||
stackFile: (stackId?: StackId) => ['stacks', stackId, 'file'],
|
||||
};
|
||||
|
||||
export function useStackFile(stackId?: StackId) {
|
||||
return useQuery(queryKeys.stackFile(stackId), () => getStackFile(stackId), {
|
||||
...withError('Unable to retrieve stack'),
|
||||
enabled: !!stackId,
|
||||
});
|
||||
}
|
||||
|
||||
async function getStackFile(stackId?: StackId) {
|
||||
if (!stackId) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
const { data } = await axios.get<StackFile>(`/stacks/${stackId}/file`);
|
||||
return data;
|
||||
}
|
29
app/react/common/stacks/types.ts
Normal file
29
app/react/common/stacks/types.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
export type StackId = number;
|
||||
|
||||
export enum StackType {
|
||||
/**
|
||||
* Represents a stack managed via docker stack
|
||||
*/
|
||||
DockerSwarm = 1,
|
||||
/**
|
||||
* Represents a stack managed via docker-compose
|
||||
*/
|
||||
DockerCompose,
|
||||
/**
|
||||
* Represents a stack managed via kubectl
|
||||
*/
|
||||
Kubernetes,
|
||||
/**
|
||||
* Represents a stack managed via Nomad
|
||||
*/
|
||||
Nomad,
|
||||
}
|
||||
|
||||
export enum StackStatus {
|
||||
Active = 1,
|
||||
Inactive,
|
||||
}
|
||||
|
||||
export type StackFile = {
|
||||
StackFileContent: string;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue