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
Expand Up @@ -12,11 +12,12 @@ const COLORS: Record<string, string> = {
};

type DatasetStatisticsProps = {
label: string;
totalMediaItems: number;
totalAnnotatedItems: number;
};

export const DatasetStatistics = ({ totalMediaItems, totalAnnotatedItems }: DatasetStatisticsProps) => {
export const DatasetStatistics = ({ label, totalMediaItems, totalAnnotatedItems }: DatasetStatisticsProps) => {
const totalUnannotatedItems = totalMediaItems - totalAnnotatedItems;
const percentageAnnotated = totalMediaItems > 0 ? Math.round((totalAnnotatedItems / totalMediaItems) * 100) : 0;
const percentageUnannotated = totalMediaItems > 0 ? Math.round((totalUnannotatedItems / totalMediaItems) * 100) : 0;
Expand All @@ -32,7 +33,9 @@ export const DatasetStatistics = ({ totalMediaItems, totalAnnotatedItems }: Data
>
<Text>Unannotated</Text>
<Text>{percentageUnannotated}%</Text>
<Text>{totalUnannotatedItems} images</Text>
<Text>
{totalUnannotatedItems} {label}
</Text>
</Flex>

<Flex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { DatasetStatistics } from './dataset-statistics.component';

describe('DatasetStatistics', () => {
it('displays correct statistics when all items are annotated', async () => {
render(<DatasetStatistics totalMediaItems={100} totalAnnotatedItems={33} />);
render(<DatasetStatistics label='images' totalMediaItems={100} totalAnnotatedItems={33} />);

expect(await screen.findByText('100')).toBeVisible();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// Copyright (C) 2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

import { dimensionValue, Divider, Flex, Loading, Text, View } from '@geti/ui';
import { Loading } from '@geti/ui';
import { useDeleteStagedDataset } from 'hooks/api/staged-dataset.hook';
import { getJobProgress, isJobRunning } from 'hooks/api/util';

import { Job } from '../../../constants/shared-types';
import { CancelJobConfirmation } from '../../../features/dataset/import-export/cancel-job-confirmation/cancel-job-confirmation.component';
import { BottomProgressBar } from '../../../features/models/model-listing/current-model-training/bottom-progress-bar.component';
import { formatBytes } from '../../../shared/util';
import { JobStatusCard } from '../../job-status-card/job-status-card.component';

type ImportActiveJobProps = {
job: Job;
Expand All @@ -30,28 +31,14 @@ export const ImportActiveJob = ({ job, size, fileName, stagedDatasetId, deleteEn

return (
<BottomProgressBar progress={progress}>
<View padding='size-150'>
<Flex justifyContent='space-between' alignItems='center' gap='size-250'>
<Text UNSAFE_style={{ fontWeight: 500, fontSize: dimensionValue('size-200') }}>
Import dataset - {fileName} - {formatBytes(size)}
</Text>

<CancelJobConfirmation jobId={job.job_id} onRemove={handleRemove} />
</Flex>

<Text>{fileName} file is being processed for import</Text>

<Divider size='S' marginY='size-150' />

<Flex justifyContent='space-between'>
<Flex alignItems='center' gap='size-100'>
<Loading mode='inline' size='S' />
<Text>{job?.message ?? job.status.toLocaleLowerCase()}</Text>
</Flex>

{isRunning && <Text>{progress}%</Text>}
</Flex>
</View>
<JobStatusCard
title={`Import dataset - ${fileName} - ${formatBytes(size)}`}
actionButtons={<CancelJobConfirmation jobId={job.job_id} onRemove={handleRemove} />}
message={`${fileName} file is being processed for import`}
bottomIcon={<Loading mode='inline' size='S' />}
bottomLeftMessage={job?.message ?? job.status.toLocaleLowerCase()}
bottomRightMessage={isRunning ? `${progress}%` : undefined}
/>
</BottomProgressBar>
);
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
// Copyright (C) 2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

import { Button, dimensionValue, Divider, Flex, Text, View } from '@geti/ui';
import { Button } from '@geti/ui';
import { useDeleteStagedDataset } from 'hooks/api/staged-dataset.hook';

import { formatBytes } from '../../../shared/util';
import { JobStatusCard } from '../../job-status-card/job-status-card.component';

type ImportFailedJobProps = {
size: number;
error?: string;
message?: string;
fileName: string;
stagedDatasetId: string;
message?: string;
error?: string;
deleteEntry: () => void;
};

Expand All @@ -30,27 +31,22 @@ export const ImportFailedJob = ({
};

return (
<View padding='size-150'>
<Flex justifyContent='space-between' alignItems='center' gap='size-250'>
<Text UNSAFE_style={{ fontWeight: 500, fontSize: dimensionValue('size-200') }}>
Import dataset - {fileName} - {formatBytes(size)}
</Text>

<Flex justifyContent='space-between' alignItems='center' gap='size-250'>
<Button
variant='secondary'
style='fill'
aria-label='close import dataset status'
onPress={handleClose}
>
Close
</Button>
</Flex>
</Flex>

<Text>{message}</Text>
<Divider size='S' marginY='size-150' />
<Text>{error}</Text>
</View>
<JobStatusCard
title={`Import dataset - ${fileName} - ${formatBytes(size)}`}
actionButtons={
<Button
variant='secondary'
style='fill'
aria-label='close import dataset status'
onPress={handleClose}
isPending={deleteFileMutation.isPending}
isDisabled={deleteFileMutation.isPending}
>
Close
</Button>
}
message={message}
bottomLeftMessage={error ?? 'An unknown error '}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (C) 2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

import { Button, dimensionValue, Divider, Flex, Text, View } from '@geti/ui';
import { Button } from '@geti/ui';
import { CheckCircleOutlined } from '@geti/ui/icons';
import { useDeleteStagedDataset } from 'hooks/api/staged-dataset.hook';

import { formatBytes } from '../../../shared/util';
import { JobStatusCard } from '../../job-status-card/job-status-card.component';

import classes from './import-job-done.module.scss';

Expand All @@ -24,32 +25,23 @@ export const ImportJobDone = ({ fileName, size, stagedDatasetId, deleteEntry }:
};

return (
<View padding='size-150'>
<Flex justifyContent='space-between' alignItems='center' gap='size-250'>
<Text UNSAFE_style={{ fontWeight: 500, fontSize: dimensionValue('size-200') }}>
Import dataset - {fileName} - {formatBytes(size)}
</Text>

<Flex justifyContent='space-between' alignItems='center' gap='size-250'>
<Button
variant='secondary'
style='fill'
aria-label='close import dataset status'
onPress={handleClose}
>
Close
</Button>
</Flex>
</Flex>

<Text>{fileName} file has been imported successfully</Text>
<Divider size='S' marginY='size-150' />

<Flex alignItems='center' gap='size-100'>
<CheckCircleOutlined className={classes.checkIcon} width={16} height={16} />

<Text>Ready</Text>
</Flex>
</View>
<JobStatusCard
title={`Import dataset - ${fileName} - ${formatBytes(size)}`}
actionButtons={
<Button
variant='secondary'
style='fill'
aria-label='close import dataset status'
onPress={handleClose}
isPending={deleteFileMutation.isPending}
isDisabled={deleteFileMutation.isPending}
>
Close
</Button>
}
message={`${fileName} file has been imported successfully`}
bottomLeftMessage={'Ready'}
bottomIcon={<CheckCircleOutlined className={classes.checkIcon} width={16} height={16} />}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// Copyright (C) 2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

import { Button, dimensionValue, Divider, Flex, Text, View } from '@geti/ui';
import { Button } from '@geti/ui';
import { InfoOutline } from '@geti/ui/icons';
import { useStagedDataset } from 'hooks/api/staged-dataset.hook';

import { getErrorMessage } from '../../../query-client/query-client';
import { formatBytes } from '../../../shared/util';
import { DeleteStagedFileConfirmation } from '../../delete-staged-file-confirmation/delete-staged-file-confirmation.component';
import { JobStatusCard } from '../../job-status-card/job-status-card.component';
import { ImportFailedJob } from '../import-failed-job/import-failed-job.component';

type StagedImportDatasetProps = {
message: string;
Expand All @@ -25,39 +28,34 @@ export const StagedImportDataset = ({
onOpen,
deleteEntry,
}: StagedImportDatasetProps) => {
const { error, isError, data: stagedDataset } = useStagedDataset(stagedDatasetId);
const { error, isError, isFetching, data: stagedDataset } = useStagedDataset(stagedDatasetId);

if (isError) {
return (
<ImportFailedJob
size={0}
fileName={fileName}
error={getErrorMessage(error)}
message={'An error occurred during staged file reading'}
stagedDatasetId={stagedDatasetId}
deleteEntry={deleteEntry}
/>
);
}

return (
<View
position='relative'
borderColor='gray-200'
borderRadius='regular'
backgroundColor='gray-75'
borderWidth='thin'
>
<View padding='size-150'>
<Flex justifyContent='space-between' alignItems='center' gap='size-250'>
<Text UNSAFE_style={{ fontWeight: 500, fontSize: dimensionValue('size-200') }}>
Import dataset - {fileName} - {formatBytes(stagedDataset?.size ?? 0)}
</Text>

<Flex justifyContent='space-between' alignItems='center' gap='size-250'>
<DeleteStagedFileConfirmation stagedDatasetId={stagedDatasetId} deleteEntry={deleteEntry} />

<Button onPress={onOpen} isDisabled={isError}>
{primaryButtonLabel}
</Button>
</Flex>
</Flex>

<Divider size='S' marginY='size-150' />

<Flex alignItems='center' gap='size-100'>
<InfoOutline width={16} height={16} />

<Text>{isError ? `Error: ${error?.detail}` : message}</Text>
</Flex>
</View>
</View>
<JobStatusCard
title={`Import dataset - ${fileName} - ${formatBytes(stagedDataset?.size ?? 0)}`}
actionButtons={
<>
<DeleteStagedFileConfirmation stagedDatasetId={stagedDatasetId} deleteEntry={deleteEntry} />
<Button onPress={onOpen} isPending={isFetching} isDisabled={isFetching}>
{primaryButtonLabel}
</Button>
</>
}
bottomIcon={<InfoOutline width={16} height={16} />}
bottomLeftMessage={message}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright (C) 2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

import { screen } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { fireEvent, screen } from '@testing-library/react';
import { HttpResponse } from 'msw';
import { render } from 'test-utils/render';

Expand Down Expand Up @@ -50,8 +49,38 @@ describe('StagedImportDataset', () => {

expect(await screen.findByText(message)).toBeVisible();

await userEvent.click(await screen.findByRole('button', { name: /continue/i }));
fireEvent.click(await screen.findByRole('button', { name: /continue/i }));

expect(openDialogSpy).toHaveBeenCalledTimes(1);
});

it('renders failed import state when staged dataset request returns not found', async () => {
const apiError = 'staged dataset not found';
const openDialogSpy = vi.fn();

server.use(
http.get('/api/staged_datasets/{staged_dataset_id}', () =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
HttpResponse.json({ detail: apiError }, { status: 404 })
)
);

render(
<StagedImportDataset
message={'Map labels for the uploaded dataset'}
fileName={fileName}
stagedDatasetId={stagedDatasetId}
primaryButtonLabel={'continue'}
onOpen={openDialogSpy}
deleteEntry={vi.fn()}
/>
);

expect(await screen.findByText('An error occurred during staged file reading')).toBeVisible();
expect(await screen.findByText(apiError)).toBeVisible();
expect(await screen.findByRole('button', { name: /close import dataset status/i })).toBeVisible();
expect(screen.queryByRole('button', { name: /continue/i })).not.toBeInTheDocument();
expect(openDialogSpy).not.toHaveBeenCalled();
});
});
Loading
Loading