Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
'use client';
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { CellClickedEvent, GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community';

import { CellClickedEvent, GridOptions } from 'ag-grid-community';
import { useRouter } from 'next/navigation';
import { ApplicationRoute } from '@/src/types/routes';
import { Container } from '@/src/models/deployments/containers';
import { useI18n } from '@/src/locales/client';
import { useNotification } from '@/src/context/NotificationContext';
import { useAppContext } from '@/src/context/AppContext';
import { CONTAINER_STATUS } from '@/src/types/deployments/containers';
import { FC, useCallback, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';

import {
deleteContainer,
duplicateContainer,
getContainer,
runContainer,
stopContainer,
} from '@/src/app/actions/deployments';
import HeaderButtons from '@/src/components/Containers/List/HeaderButtons';
import ContainerDuplicate from '@/src/components/Deployments/Modals/ContainerDuplicate';
import { ModalType } from '@/src/components/EntityListView/Components/Modals';
import { ServerActionResponse } from '@/src/models/server-action';
import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
import { getErrorNotification, getSuccessNotification } from '@/src/utils/notification';
import Delete from '@/src/components/EntityView/Modals/Delete/Delete';
import ListEntities from '@/src/components/ListView/List';
import { ACTION_COLUMN, ACTIONS_COLUMN_CEL_ID } from '@/src/constants/ag-grid';
import { Notification } from '@/src/models/notification';
import { ContainersI18nKey, EntitiesI18nKey } from '@/src/constants/i18n';
import { getTranslatedDeploymentType, getTranslatedType } from '@/src/utils/deployments/entity';
import { IMAGE_BUILD_POLL_INTERVAL } from '@/src/constants/deployments/images';
import ListView from '@/src/components/ListView/ListView';
import HeaderButtons from '@/src/components/Containers/List/HeaderButtons';
import ContainerDuplicate from '@/src/components/Deployments/Modals/ContainerDuplicate';

import {
getDeleteOperation,
getDuplicateOperation,
getOpenInNewTabOperation,
getRunOperation,
getStopOperation,
} from '@/src/constants/grid-columns/actions';
import { getUrnForEntity, onOpenInNewTab } from '@/src/utils/open-in-new-tab';
import { CONTAINERS_COLUMNS } from '@/src/constants/grid-columns/grid-columns';
import Delete from '@/src/components/EntityView/Modals/Delete/Delete';
import { ContainersI18nKey, EntitiesI18nKey } from '@/src/constants/i18n';
import { useNotification } from '@/src/context/NotificationContext';
import { useI18n } from '@/src/locales/client';
import { Container } from '@/src/models/deployments/containers';
import { Notification } from '@/src/models/notification';
import { ServerActionResponse } from '@/src/models/server-action';
import { CONTAINER_STATUS } from '@/src/types/deployments/containers';
import { ApplicationRoute } from '@/src/types/routes';
import { getTranslatedDeploymentType, getTranslatedType } from '@/src/utils/deployments/entity';
import { getErrorNotification, getSuccessNotification } from '@/src/utils/notification';
import { getUrnForEntity, onOpenInNewTab } from '@/src/utils/open-in-new-tab';
import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';

interface Props {
route: ApplicationRoute;
Expand All @@ -49,19 +49,11 @@ const ContainersList: FC<Props> = ({ route, containersList }) => {
const t = useI18n();
const router = useRouter();
const { showNotification } = useNotification();
const { visualizerConnector } = useAppContext();
const visualizerConnectorRef = useRef(visualizerConnector);

const [names, setNames] = useState(containersList.map((container) => container.name || ''));
const [currentContainer, setCurrentContainer] = useState<Container | null>(null);
const [modalType, setModalType] = useState<ModalType>();
const [showColumnsPanel, setShowColumnsPanel] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [gridApi, setGridApi] = useState<GridApi | null>(null);

const onGridReady = useCallback(({ api }: GridReadyEvent) => {
setGridApi(api);
}, []);

const gridOptions: GridOptions = {
onCellClicked: (e: CellClickedEvent) => {
Expand Down Expand Up @@ -177,14 +169,6 @@ const ContainersList: FC<Props> = ({ route, containersList }) => {
]),
];

const toggleColumnsPanel = () => setShowColumnsPanel(!showColumnsPanel);

const closeColumnsPanel = useCallback(() => setShowColumnsPanel(false), [setShowColumnsPanel]);

useEffect(() => {
visualizerConnectorRef.current = visualizerConnector;
}, [visualizerConnector]);

useEffect(() => {
let interval: NodeJS.Timeout | null = null;

Expand Down Expand Up @@ -248,38 +232,27 @@ const ContainersList: FC<Props> = ({ route, containersList }) => {
};
}, [showNotification, route, t, router, containersList]);

useEffect(() => {
window.addEventListener('click', closeColumnsPanel);
return () => window.removeEventListener('click', closeColumnsPanel);
}, [closeColumnsPanel]);

return (
<>
<ListView
data={containersList}
view={route}
<ListEntities
rowData={containersList}
columnDefs={columnDefs}
additionalGridOptions={gridOptions}
title={t(ContainersI18nKey.ContainersListTitle, {
type: getTranslatedType(route, t),
entityType: getTranslatedDeploymentType(route, t),
})}
emptyDataTitle={t(EntitiesI18nKey.NoContainersType, {
isMainListView
listLabel={t(ContainersI18nKey.ContainersListTitle, {
type: getTranslatedType(route, t),
entityType: getTranslatedDeploymentType(route, t),
})}
showColumnsPanel={showColumnsPanel}
toggleColumnsPanel={toggleColumnsPanel}
storageKey={`${route}`}
onGridReady={onGridReady}
emptyDataProps={{
title: t(EntitiesI18nKey.NoContainersType, {
type: getTranslatedType(route, t),
entityType: getTranslatedDeploymentType(route, t),
}),
}}
storageKey={route}
>
<HeaderButtons
toggleColumnsPanel={toggleColumnsPanel}
route={route}
names={containersList.map((container) => container.name as string) || []}
gridApi={gridApi}
/>
</ListView>
<HeaderButtons route={route} names={containersList.map((container) => container.name as string) || []} />
</ListEntities>
{isModalOpen &&
modalType === ModalType.duplicate &&
currentContainer &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
'use client';

import { FC, MouseEvent, useCallback, useState } from 'react';
import { DialButtonDropdown, DialPrimaryButton, DropdownItem } from '@epam/ai-dial-ui-kit';
import { IconPlus } from '@tabler/icons-react';
import { FC, useCallback, useState } from 'react';
import { createPortal } from 'react-dom';
import { DialButtonDropdown, DialGhostButton, DialPrimaryButton, DropdownItem } from '@epam/ai-dial-ui-kit';
import { IconColumns2, IconPlus } from '@tabler/icons-react';
import { GridApi } from 'ag-grid-community';

import { ApplicationRoute } from '@/src/types/routes';
import { Container } from '@/src/models/deployments/containers';
import { createContainer } from '@/src/app/actions/deployments';
import { ModalType } from '@/src/components/EntityListView/Components/Modals';
import { CONTAINER_TYPE } from '@/src/types/deployments/containers';
import { ButtonsI18nKey, ContainersI18nKey, CreateI18nKey, EntitiesI18nKey } from '@/src/constants/i18n';
import { useIsTabletScreen } from '@/src/hooks/use-is-tablet-screen';
import { BASE_BUTTON_ICON_PROPS } from '@/src/constants/main-layout';
import { useNotification } from '@/src/context/NotificationContext';
import { createContainer } from '@/src/app/actions/deployments';
import { getErrorNotification } from '@/src/utils/notification';
import { useIsTabletScreen } from '@/src/hooks/use-is-tablet-screen';
import { useI18n } from '@/src/locales/client';
import { Container } from '@/src/models/deployments/containers';
import { CONTAINER_TYPE } from '@/src/types/deployments/containers';
import { ApplicationRoute } from '@/src/types/routes';
import { getTranslatedDeploymentType, getTranslatedType } from '@/src/utils/deployments/entity';
import { getErrorNotification } from '@/src/utils/notification';
import { getUrnForEntity } from '@/src/utils/open-in-new-tab';
import { useRouter } from 'next/navigation';
import { BASE_BUTTON_ICON_PROPS } from '@/src/constants/main-layout';
import { useI18n } from '@/src/locales/client';

import ResetFiltersButton from '@/src/components/ListView/Header/ResetFiltersButton';
import ContainerCreate from '@/src/components/Deployments/Modals/ContainerCreate';
import ServingCreate from '@/src/components/Deployments/Modals/ServingCreate';

interface Props {
toggleColumnsPanel: () => void;
route: ApplicationRoute;
names: string[];
gridApi?: GridApi | null;
}

const HeaderButtons: FC<Props> = ({ toggleColumnsPanel, route, names, gridApi }) => {
const HeaderButtons: FC<Props> = ({ route, names }) => {
const t = useI18n();
const router = useRouter();
const isTabletScreen = useIsTabletScreen();
Expand Down Expand Up @@ -87,24 +83,9 @@ const HeaderButtons: FC<Props> = ({ toggleColumnsPanel, route, names, gridApi })
[route, router, showNotification],
);

const onToggleColumnsPanel = useCallback(
(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();

toggleColumnsPanel();
},
[toggleColumnsPanel],
);

return (
<>
<div className="flex gap-4">
<ResetFiltersButton gridApi={gridApi} />
<DialGhostButton
label={t(ButtonsI18nKey.Columns)}
iconBefore={<IconColumns2 {...BASE_BUTTON_ICON_PROPS} />}
onClick={onToggleColumnsPanel}
/>
{route === ApplicationRoute.ModelServings ? (
<DialButtonDropdown items={dropdownItems} label={isTabletScreen ? '' : t(ButtonsI18nKey.Create)} />
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,6 @@ describe('HeaderButtons', () => {
expect(screen.getByRole('button', { name: /create/i })).toBeInTheDocument();
});

test('renders columns button', () => {
render(<HeaderButtons {...defaultProps} />);

expect(screen.getByRole('button', { name: /columns/i })).toBeInTheDocument();
});

test('calls toggleColumnsPanel when columns button clicked', async () => {
const user = userEvent.setup();
render(<HeaderButtons {...defaultProps} />);

const columnsButton = screen.getByRole('button', { name: /columns/i });
await user.click(columnsButton);

expect(mockToggleColumnsPanel).toHaveBeenCalled();
});

test.skip('opens modal when create button clicked', async () => {
const user = userEvent.setup();
render(<HeaderButtons {...defaultProps} />);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { FC, MouseEvent, useCallback, useEffect, useState } from 'react';
import { GridApi, GridReadyEvent } from 'ag-grid-community';
import { IconColumns2 } from '@tabler/icons-react';
import { DialGhostButton } from '@epam/ai-dial-ui-kit';
import { ApplicationRoute } from '@/src/types/routes';
import { KubEvent } from '@/src/models/deployments/containers';
import { useI18n } from '@/src/locales/client';
import ListView from '@/src/components/ListView/ListView';
import { ButtonsI18nKey, EntitiesI18nKey, TabsI18nKey } from '@/src/constants/i18n';
import ResetFiltersButton from '@/src/components/ListView/Header/ResetFiltersButton';
import { BASE_BUTTON_ICON_PROPS } from '@/src/constants/main-layout';
import { FC } from 'react';

import ListEntities from '@/src/components/ListView/List';
import { CONTAINER_EVENTS } from '@/src/constants/grid-columns/grid-columns';
import { EntitiesI18nKey, TabsI18nKey } from '@/src/constants/i18n';
import { useI18n } from '@/src/locales/client';
import { KubEvent } from '@/src/models/deployments/containers';
import { ApplicationRoute } from '@/src/types/routes';

interface Props {
route: ApplicationRoute;
Expand All @@ -19,59 +15,15 @@ interface Props {
const Events: FC<Props> = ({ route, events }) => {
const t = useI18n();

const [showColumnsPanel, setShowColumnsPanel] = useState(false);

const [gridApi, setGridApi] = useState<GridApi | null>(null);

const onGridReady = useCallback(({ api }: GridReadyEvent) => {
setGridApi(api);
}, []);

const toggleColumnsPanel = useCallback(() => setShowColumnsPanel(!showColumnsPanel), [showColumnsPanel]);
const closeColumnsPanel = useCallback(() => setShowColumnsPanel(false), [setShowColumnsPanel]);

useEffect(() => {
window.addEventListener('click', closeColumnsPanel);
return () => window.removeEventListener('click', closeColumnsPanel);
}, [closeColumnsPanel]);

const onToggleColumnsPanel = useCallback(
(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();

toggleColumnsPanel();
},
[toggleColumnsPanel],
);

return (
<div className="h-full flex">
<ListView
view={route}
data={events}
allowPadding={false}
columnDefs={CONTAINER_EVENTS(t)}
title={t(TabsI18nKey.Events)}
emptyDataTitle={t(EntitiesI18nKey.NoEvents)}
showColumnsPanel={showColumnsPanel}
toggleColumnsPanel={toggleColumnsPanel}
onGridReady={onGridReady}
storageKey={`${route}/events`}
>
<div className="flex gap-4">
{!!events.length && (
<>
<ResetFiltersButton gridApi={gridApi} />
<DialGhostButton
label={t(ButtonsI18nKey.Columns)}
iconBefore={<IconColumns2 {...BASE_BUTTON_ICON_PROPS} />}
onClick={onToggleColumnsPanel}
/>
</>
)}
</div>
</ListView>
</div>
<ListEntities
rowData={events}
columnDefs={CONTAINER_EVENTS(t)}
listLabel={t(TabsI18nKey.Events)}
emptyDataProps={{ title: t(EntitiesI18nKey.NoEvents) }}
storageKey={`${route}/events`}
isEnableColumnPanel
/>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const ItemsList: FC<Props> = ({ items, setItems, addItemLabel, validate, isModal
<ul className="flex flex-col gap-4 w-full overflow-auto">
{items == null || items.length === 0 ? (
<Item
item={''}
item=""
index={0}
onChange={onChangeItem}
onRemove={onRemoveItem}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('Common Item component', () => {
const user = userEvent.setup();

test('component rendered correctly', () => {
render(<Item item={'item'} index={1} onChange={onChange} onRemove={onRemove} isModal={false} disabled={false} />);
render(<Item item="item" index={1} onChange={onChange} onRemove={onRemove} isModal={false} disabled={false} />);

expect(screen.getByRole('listitem')).toBeInTheDocument();
});
Expand All @@ -19,7 +19,7 @@ describe('Common Item component', () => {
const validate = vi.fn();
render(
<Item
item={''}
item=""
index={1}
onChange={onChange}
onRemove={onRemove}
Expand All @@ -42,7 +42,7 @@ describe('Common Item component', () => {
});

test('onRemove called', async () => {
render(<Item item={''} index={1} onChange={onChange} onRemove={onRemove} isModal={false} disabled={false} />);
render(<Item item="" index={1} onChange={onChange} onRemove={onRemove} isModal={false} disabled={false} />);

const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('Common ItemsList component', () => {

test('add correctly', async () => {
const setItems = vi.fn();
render(<ItemsList items={['item', 'item-2']} setItems={setItems} addItemLabel={'ADD'} />);
render(<ItemsList items={['item', 'item-2']} setItems={setItems} addItemLabel="ADD" />);

const button = screen.getByRole('button', { name: 'ADD' });

Expand Down
Loading
Loading