mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 05:45:22 +02:00
feat(docker/images): show used tag correctly [EE-5396] (#10305)
This commit is contained in:
parent
b895e88075
commit
9bf2957ea7
29 changed files with 383 additions and 287 deletions
|
@ -10,8 +10,8 @@ import { Menu, MenuButton, MenuItem, MenuPopover } from '@reach/menu-button';
|
|||
import { positionRight } from '@reach/popover';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { Environment } from '@/react/portainer/environments/types';
|
||||
import { Authorized } from '@/react/hooks/useUser';
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
|
||||
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
||||
import {
|
||||
|
@ -24,14 +24,12 @@ import { useTableState } from '@@/datatables/useTableState';
|
|||
import { Button, ButtonGroup, LoadingButton } from '@@/buttons';
|
||||
import { Link } from '@@/Link';
|
||||
import { ButtonWithRef } from '@@/buttons/Button';
|
||||
import { useRepeater } from '@@/datatables/useRepeater';
|
||||
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
|
||||
|
||||
import { DockerImage } from '../../types';
|
||||
import { ImagesListResponse, useImages } from '../../queries/useImages';
|
||||
|
||||
import { columns as defColumns } from './columns';
|
||||
import { host as hostColumn } from './columns/host';
|
||||
import { RowProvider } from './RowContext';
|
||||
|
||||
const tableKey = 'images';
|
||||
|
||||
|
@ -48,76 +46,67 @@ const settingsStore = createPersistedStore<TableSettings>(
|
|||
);
|
||||
|
||||
export function ImagesDatatable({
|
||||
dataset,
|
||||
|
||||
environment,
|
||||
isHostColumnVisible,
|
||||
isExportInProgress,
|
||||
onDownload,
|
||||
onRefresh,
|
||||
onRemove,
|
||||
}: {
|
||||
dataset: Array<DockerImage>;
|
||||
environment: Environment;
|
||||
isHostColumnVisible: boolean;
|
||||
|
||||
onDownload: (images: Array<DockerImage>) => void;
|
||||
onRemove: (images: Array<DockerImage>, force: true) => void;
|
||||
onRefresh: () => Promise<void>;
|
||||
onDownload: (images: Array<ImagesListResponse>) => void;
|
||||
onRemove: (images: Array<ImagesListResponse>, force: true) => void;
|
||||
isExportInProgress: boolean;
|
||||
}) {
|
||||
const environmentId = useEnvironmentId();
|
||||
const tableState = useTableState(settingsStore, tableKey);
|
||||
const columns = useMemo(
|
||||
() => (isHostColumnVisible ? [...defColumns, hostColumn] : defColumns),
|
||||
[isHostColumnVisible]
|
||||
);
|
||||
|
||||
useRepeater(tableState.autoRefreshRate, onRefresh);
|
||||
const imagesQuery = useImages(environmentId, true, {
|
||||
refetchInterval: tableState.autoRefreshRate * 1000,
|
||||
});
|
||||
|
||||
return (
|
||||
<RowProvider context={{ environment }}>
|
||||
<Datatable
|
||||
title="Images"
|
||||
titleIcon={List}
|
||||
renderTableActions={(selectedItems) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<RemoveButtonMenu
|
||||
selectedItems={selectedItems}
|
||||
onRemove={onRemove}
|
||||
/>
|
||||
<Datatable
|
||||
title="Images"
|
||||
titleIcon={List}
|
||||
renderTableActions={(selectedItems) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<RemoveButtonMenu selectedItems={selectedItems} onRemove={onRemove} />
|
||||
|
||||
<ImportExportButtons
|
||||
isExportInProgress={isExportInProgress}
|
||||
onExportClick={onDownload}
|
||||
selectedItems={selectedItems}
|
||||
/>
|
||||
<ImportExportButtons
|
||||
isExportInProgress={isExportInProgress}
|
||||
onExportClick={onDownload}
|
||||
selectedItems={selectedItems}
|
||||
/>
|
||||
|
||||
<Authorized authorizations="DockerImageBuild">
|
||||
<Button
|
||||
as={Link}
|
||||
props={{ to: 'docker.images.build' }}
|
||||
data-cy="image-buildImageButton"
|
||||
icon={Plus}
|
||||
>
|
||||
Build a new image
|
||||
</Button>
|
||||
</Authorized>
|
||||
</div>
|
||||
)}
|
||||
dataset={dataset}
|
||||
settingsManager={tableState}
|
||||
columns={columns}
|
||||
emptyContentLabel="No images found"
|
||||
renderTableSettings={() => (
|
||||
<TableSettingsMenu>
|
||||
<TableSettingsMenuAutoRefresh
|
||||
value={tableState.autoRefreshRate}
|
||||
onChange={(value) => tableState.setAutoRefreshRate(value)}
|
||||
/>
|
||||
</TableSettingsMenu>
|
||||
)}
|
||||
/>
|
||||
</RowProvider>
|
||||
<Authorized authorizations="DockerImageBuild">
|
||||
<Button
|
||||
as={Link}
|
||||
props={{ to: 'docker.images.build' }}
|
||||
data-cy="image-buildImageButton"
|
||||
icon={Plus}
|
||||
>
|
||||
Build a new image
|
||||
</Button>
|
||||
</Authorized>
|
||||
</div>
|
||||
)}
|
||||
dataset={imagesQuery.data || []}
|
||||
isLoading={imagesQuery.isLoading}
|
||||
settingsManager={tableState}
|
||||
columns={columns}
|
||||
emptyContentLabel="No images found"
|
||||
renderTableSettings={() => (
|
||||
<TableSettingsMenu>
|
||||
<TableSettingsMenuAutoRefresh
|
||||
value={tableState.autoRefreshRate}
|
||||
onChange={(value) => tableState.setAutoRefreshRate(value)}
|
||||
/>
|
||||
</TableSettingsMenu>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -125,8 +114,8 @@ function RemoveButtonMenu({
|
|||
onRemove,
|
||||
selectedItems,
|
||||
}: {
|
||||
selectedItems: Array<DockerImage>;
|
||||
onRemove(selectedItems: Array<DockerImage>, force: boolean): void;
|
||||
selectedItems: Array<ImagesListResponse>;
|
||||
onRemove(selectedItems: Array<ImagesListResponse>, force: boolean): void;
|
||||
}) {
|
||||
return (
|
||||
<Authorized authorizations="DockerImageDelete">
|
||||
|
@ -176,8 +165,8 @@ function ImportExportButtons({
|
|||
onExportClick,
|
||||
}: {
|
||||
isExportInProgress: boolean;
|
||||
selectedItems: Array<DockerImage>;
|
||||
onExportClick(selectedItems: Array<DockerImage>): void;
|
||||
selectedItems: Array<ImagesListResponse>;
|
||||
onExportClick(selectedItems: Array<ImagesListResponse>): void;
|
||||
}) {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import { Environment } from '@/react/portainer/environments/types';
|
||||
|
||||
import { createRowContext } from '@@/datatables/RowContext';
|
||||
|
||||
interface RowContextState {
|
||||
environment: Environment;
|
||||
}
|
||||
|
||||
const { RowProvider, useRowContext } = createRowContext<RowContextState>();
|
||||
|
||||
export { RowProvider, useRowContext };
|
|
@ -2,7 +2,7 @@ import { isoDateFromTimestamp } from '@/portainer/filters/filters';
|
|||
|
||||
import { columnHelper } from './helper';
|
||||
|
||||
export const created = columnHelper.accessor('Created', {
|
||||
export const created = columnHelper.accessor('created', {
|
||||
id: 'created',
|
||||
header: 'Created',
|
||||
cell: ({ getValue }) => {
|
||||
|
|
|
@ -1,16 +1,5 @@
|
|||
import { createColumnHelper } from '@tanstack/react-table';
|
||||
|
||||
import { DockerImage } from '@/react/docker/images/types';
|
||||
import { ImagesListResponse } from '@/react/docker/images/queries/useImages';
|
||||
|
||||
export const columnHelper = createColumnHelper<
|
||||
DockerImage & { NodeName?: string }
|
||||
>();
|
||||
|
||||
/**
|
||||
* Docker response from proxy (with added portainer metadata)
|
||||
* images view model
|
||||
* images snapshot
|
||||
* snapshots view model
|
||||
*
|
||||
*
|
||||
*/
|
||||
export const columnHelper = createColumnHelper<ImagesListResponse>();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { columnHelper } from './helper';
|
||||
|
||||
export const host = columnHelper.accessor('NodeName', {
|
||||
export const host = columnHelper.accessor('nodeName', {
|
||||
header: 'Host',
|
||||
cell: ({ getValue }) => {
|
||||
const value = getValue();
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import { CellContext, Column } from '@tanstack/react-table';
|
||||
import { useSref } from '@uirouter/react';
|
||||
|
||||
import { DockerImage } from '@/react/docker/images/types';
|
||||
import { truncate } from '@/portainer/filters/filters';
|
||||
import { getValueAsArrayOfStrings } from '@/portainer/helpers/array';
|
||||
import { ImagesListResponse } from '@/react/docker/images/queries/useImages';
|
||||
|
||||
import { MultipleSelectionFilter } from '@@/datatables/Filter';
|
||||
|
||||
import { columnHelper } from './helper';
|
||||
|
||||
export const id = columnHelper.accessor('Id', {
|
||||
export const id = columnHelper.accessor('id', {
|
||||
id: 'id',
|
||||
header: 'Id',
|
||||
cell: Cell,
|
||||
enableColumnFilter: true,
|
||||
filterFn: (
|
||||
{ original: { Used } },
|
||||
{ original: { used } },
|
||||
columnId,
|
||||
filterValue: Array<'Used' | 'Unused'>
|
||||
) => {
|
||||
|
@ -23,11 +23,11 @@ export const id = columnHelper.accessor('Id', {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (filterValue.includes('Used') && Used) {
|
||||
if (filterValue.includes('Used') && used) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (filterValue.includes('Unused') && !Used) {
|
||||
if (filterValue.includes('Unused') && !used) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -63,12 +63,12 @@ function FilterByUsage<TData extends { Used: boolean }>({
|
|||
function Cell({
|
||||
getValue,
|
||||
row: { original: image },
|
||||
}: CellContext<DockerImage, string>) {
|
||||
}: CellContext<ImagesListResponse, string>) {
|
||||
const name = getValue();
|
||||
|
||||
const linkProps = useSref('.image', {
|
||||
id: image.Id,
|
||||
imageId: image.Id,
|
||||
id: image.id,
|
||||
imageId: image.id,
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -76,7 +76,7 @@ function Cell({
|
|||
<a href={linkProps.href} onClick={linkProps.onClick} title={name}>
|
||||
{truncate(name, 40)}
|
||||
</a>
|
||||
{!image.Used && (
|
||||
{!image.used && (
|
||||
<span className="label label-warning image-tag ml-2">Unused</span>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { humanize } from '@/portainer/filters/filters';
|
|||
|
||||
import { columnHelper } from './helper';
|
||||
|
||||
export const size = columnHelper.accessor('VirtualSize', {
|
||||
export const size = columnHelper.accessor('size', {
|
||||
id: 'size',
|
||||
header: 'Size',
|
||||
cell: ({ getValue }) => {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { CellContext } from '@tanstack/react-table';
|
||||
|
||||
import { DockerImage } from '@/react/docker/images/types';
|
||||
import { ImagesListResponse } from '@/react/docker/images/queries/useImages';
|
||||
|
||||
import { columnHelper } from './helper';
|
||||
|
||||
export const tags = columnHelper.accessor('RepoTags', {
|
||||
export const tags = columnHelper.accessor('tags', {
|
||||
id: 'tags',
|
||||
header: 'Tags',
|
||||
cell: Cell,
|
||||
});
|
||||
|
||||
function Cell({ getValue }: CellContext<DockerImage, string[]>) {
|
||||
function Cell({ getValue }: CellContext<ImagesListResponse, string[]>) {
|
||||
const repoTags = getValue();
|
||||
|
||||
return (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue