Skip to content

Commit 80035ae

Browse files
committed
fix
1 parent 6082362 commit 80035ae

File tree

2 files changed

+139
-53
lines changed

2 files changed

+139
-53
lines changed

src/components/ControlPlane/Kustomizations.tsx

Lines changed: 90 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,76 +7,105 @@ import { useTranslation } from 'react-i18next';
77
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo.ts';
88

99
import { YamlViewButton } from '../Yaml/YamlViewButton.tsx';
10-
import { useMemo } from 'react';
10+
import { Fragment, useCallback, useMemo, useRef } from 'react';
1111
import StatusFilter from '../Shared/StatusFilter/StatusFilter.tsx';
1212
import { ResourceStatusCell } from '../Shared/ResourceStatusCell.tsx';
1313
import { Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';
14+
import { useSplitter } from '../Splitter/SplitterContext.tsx';
15+
import { YamlSidePanel } from '../Yaml/YamlSidePanel.tsx';
16+
import { useHandleResourcePatch } from '../../lib/api/types/crossplane/useHandleResourcePatch.ts';
17+
import { ErrorDialog, ErrorDialogHandle } from '../Shared/ErrorMessageBox.tsx';
18+
import { KustomizationsRowActionsMenu, KustomizationItem } from './KustomizationsActionMenu.tsx';
1419

1520
export function Kustomizations() {
1621
const { data, error, isLoading } = useApiResource(FluxKustomization); //404 if component not enabled
1722
const { t } = useTranslation();
23+
const { openInAside } = useSplitter();
24+
const errorDialogRef = useRef<ErrorDialogHandle>(null);
25+
const handlePatch = useHandleResourcePatch(errorDialogRef);
1826

19-
interface CellData<T> {
20-
cell: {
21-
value: T | null; // null for grouping rows
22-
row: {
23-
original?: FluxRow; // missing for grouping rows
24-
};
25-
};
27+
interface CellRow<T> {
28+
original: T;
2629
}
2730

2831
type FluxRow = {
2932
name: string;
3033
created: string;
3134
isReady: boolean;
3235
statusUpdateTime?: string;
33-
item: unknown;
36+
item: KustomizationItem;
3437
readyMessage: string;
3538
};
3639

37-
const columns: AnalyticalTableColumnDefinition[] = useMemo(
38-
() => [
39-
{
40-
Header: t('FluxList.tableNameHeader'),
41-
accessor: 'name',
42-
minWidth: 250,
43-
},
44-
{
45-
Header: t('FluxList.tableCreatedHeader'),
46-
accessor: 'created',
47-
},
48-
{
49-
Header: t('FluxList.tableStatusHeader'),
50-
accessor: 'status',
51-
width: 125,
52-
hAlign: 'Center',
53-
Filter: ({ column }) => <StatusFilter column={column} />,
54-
Cell: (cellData: CellData<FluxRow['isReady']>) =>
55-
cellData.cell.row.original?.isReady != null ? (
56-
<ResourceStatusCell
57-
positiveText={t('common.ready')}
58-
negativeText={t('common.error')}
59-
isOk={cellData.cell.row.original?.isReady}
60-
transitionTime={
61-
cellData.cell.row.original?.statusUpdateTime ? cellData.cell.row.original?.statusUpdateTime : ''
62-
}
63-
message={cellData.cell.row.original?.readyMessage}
64-
/>
65-
) : null,
66-
},
40+
const openEditPanel = useCallback(
41+
(item: KustomizationItem) => {
42+
const identityKey = `${item.kind}:${item.metadata.namespace ?? ''}:${item.metadata.name}`;
43+
openInAside(
44+
<Fragment key={identityKey}>
45+
<YamlSidePanel
46+
isEdit={true}
47+
resource={item as unknown as Resource}
48+
filename={`${item.kind}_${item.metadata.name}`}
49+
onApply={async (parsed) => await handlePatch(item, parsed)}
50+
/>
51+
</Fragment>,
52+
);
53+
},
54+
[openInAside, handlePatch],
55+
);
6756

68-
{
69-
Header: t('yaml.YAML'),
70-
hAlign: 'Center',
71-
width: 75,
72-
accessor: 'yaml',
73-
disableFilters: true,
74-
Cell: (cellData: CellData<FluxRow>) => (
75-
<YamlViewButton variant="resource" resource={cellData.cell.row.original?.item as Resource} />
76-
),
77-
},
78-
],
79-
[t],
57+
const columns = useMemo<AnalyticalTableColumnDefinition[]>(
58+
() =>
59+
[
60+
{
61+
Header: t('FluxList.tableNameHeader'),
62+
accessor: 'name',
63+
minWidth: 250,
64+
},
65+
{
66+
Header: t('FluxList.tableCreatedHeader'),
67+
accessor: 'created',
68+
},
69+
{
70+
Header: t('FluxList.tableStatusHeader'),
71+
accessor: 'status',
72+
width: 125,
73+
hAlign: 'Center',
74+
Filter: ({ column }) => <StatusFilter column={column} />,
75+
Cell: ({ row }: { row: CellRow<FluxRow> }) =>
76+
row.original?.isReady != null ? (
77+
<ResourceStatusCell
78+
positiveText={t('common.ready')}
79+
negativeText={t('common.error')}
80+
isOk={row.original?.isReady}
81+
transitionTime={row.original?.statusUpdateTime ? row.original?.statusUpdateTime : ''}
82+
message={row.original?.readyMessage}
83+
/>
84+
) : null,
85+
},
86+
{
87+
Header: t('yaml.YAML'),
88+
hAlign: 'Center',
89+
width: 75,
90+
accessor: 'yaml',
91+
disableFilters: true,
92+
Cell: ({ row }: { row: CellRow<FluxRow> }) => (
93+
<YamlViewButton variant="resource" resource={row.original.item as unknown as Resource} />
94+
),
95+
},
96+
{
97+
Header: t('ManagedResources.actionColumnHeader'),
98+
hAlign: 'Center',
99+
width: 60,
100+
disableFilters: true,
101+
accessor: 'actions',
102+
Cell: ({ row }: { row: CellRow<FluxRow> }) =>
103+
row.original?.item ? (
104+
<KustomizationsRowActionsMenu item={row.original.item} onEdit={openEditPanel} />
105+
) : undefined,
106+
},
107+
] as AnalyticalTableColumnDefinition[],
108+
[t, openEditPanel],
80109
);
81110

82111
if (error) {
@@ -97,7 +126,12 @@ export function Kustomizations() {
97126
isReady: readyObject?.status === 'True',
98127
statusUpdateTime: readyObject?.lastTransitionTime,
99128
created: formatDateAsTimeAgo(item.metadata.creationTimestamp),
100-
item: item,
129+
item: {
130+
...item,
131+
kind: 'Kustomization',
132+
apiVersion: 'kustomize.toolkit.fluxcd.io/v1',
133+
metadata: { ...item.metadata },
134+
} as KustomizationItem,
101135
readyMessage: readyObject?.message ?? readyObject?.reason ?? '',
102136
};
103137
}) ?? [];
@@ -113,7 +147,10 @@ export function Kustomizations() {
113147
</Toolbar>
114148
}
115149
>
116-
<ConfiguredAnalyticstable columns={columns} isLoading={isLoading} data={rows} />
150+
<>
151+
<ConfiguredAnalyticstable columns={columns} isLoading={isLoading} data={rows} />
152+
<ErrorDialog ref={errorDialogRef} />
153+
</>
117154
</Panel>
118155
);
119156
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { FC, useRef, useState } from 'react';
2+
import { Button, Menu, MenuItem, MenuDomRef } from '@ui5/webcomponents-react';
3+
import { useTranslation } from 'react-i18next';
4+
import type { ButtonClickEventDetail } from '@ui5/webcomponents/dist/Button.js';
5+
import type { Ui5CustomEvent, ButtonDomRef } from '@ui5/webcomponents-react';
6+
import type { KustomizationsResponse } from '../../lib/api/types/flux/listKustomization';
7+
8+
export type KustomizationItem = KustomizationsResponse['items'][0] & {
9+
apiVersion?: string;
10+
metadata: KustomizationsResponse['items'][0]['metadata'] & { namespace?: string };
11+
};
12+
13+
interface KustomizationsRowActionsMenuProps {
14+
item: KustomizationItem;
15+
onEdit: (item: KustomizationItem) => void;
16+
}
17+
18+
export const KustomizationsRowActionsMenu: FC<KustomizationsRowActionsMenuProps> = ({ item, onEdit }) => {
19+
const { t } = useTranslation();
20+
const popoverRef = useRef<MenuDomRef>(null);
21+
const [open, setOpen] = useState(false);
22+
23+
const handleOpenerClick = (e: Ui5CustomEvent<ButtonDomRef, ButtonClickEventDetail>) => {
24+
if (popoverRef.current && e.currentTarget) {
25+
popoverRef.current.opener = e.currentTarget as unknown as HTMLElement;
26+
setOpen((prev) => !prev);
27+
}
28+
};
29+
30+
return (
31+
<>
32+
<Button icon="overflow" design="Transparent" onClick={handleOpenerClick} />
33+
<Menu
34+
ref={popoverRef}
35+
open={open}
36+
onItemClick={(event) => {
37+
const element = event.detail.item as HTMLElement;
38+
const action = element.dataset.action;
39+
if (action === 'edit') {
40+
onEdit(item);
41+
}
42+
setOpen(false);
43+
}}
44+
>
45+
<MenuItem text={t('ManagedResources.editAction', 'Edit')} icon="edit" data-action="edit" />
46+
</Menu>
47+
</>
48+
);
49+
};

0 commit comments

Comments
 (0)