Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LANDGRIF-1531] Implement UI for tracking data upload #1170

Merged
merged 1 commit into from
May 9, 2024
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
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"recharts": "2.9.0",
"rooks": "7.14.1",
"sharp": "0.32.6",
"socket.io-client": "4.7.5",
"tailwind-merge": "2.2.1",
"tailwindcss": "3.4.1",
"tailwindcss-animate": "1.0.7",
Expand Down
1 change: 0 additions & 1 deletion client/src/containers/admin/data-table/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,33 @@ import { format } from 'date-fns';
import { useRouter } from 'next/router';
import { useSession } from 'next-auth/react';

import useModal from 'hooks/modals';
import useModal from '@/hooks/modals';
import {
useSourcingLocations,
useSourcingLocationsMaterials,
useSourcingLocationsMaterialsTabularData,
} from 'hooks/sourcing-locations';
import DownloadMaterialsDataButton from 'containers/admin/download-materials-data-button';
import DataUploadError from 'containers/admin/data-upload-error';
import DataUploader from 'containers/uploader';
import Button, { Anchor } from 'components/button';
import Modal from 'components/modal';
import Table from 'components/table';
import { DEFAULT_PAGE_SIZES } from 'components/table/pagination/constants';
import { usePermissions } from 'hooks/permissions';
import { RoleName } from 'hooks/permissions/enums';
} from '@/hooks/sourcing-locations';
import DownloadMaterialsDataButton from '@/containers/admin/download-materials-data-button';
import DataUploadError from '@/containers/admin/data-upload-error';
import DataUploader from '@/containers/uploader';
import Button, { Anchor } from '@/components/button';
import Modal from '@/components/modal';
import Table from '@/components/table';
import { DEFAULT_PAGE_SIZES } from '@/components/table/pagination/constants';
import { usePermissions } from '@/hooks/permissions';
import { RoleName } from '@/hooks/permissions/enums';
import { useLasTask } from '@/hooks/tasks';

import type { PaginationState, SortingState, VisibilityState } from '@tanstack/react-table';
import type { TableProps } from 'components/table/component';
import type { Task } from 'types';
import type { TableProps } from '@/components/table/component';

const YEARS_COLUMNS_UNIT = 't/yr';

const AdminDataPage: React.FC<{ task: Task }> = ({ task }) => {
const AdminDataPage: React.FC = () => {
const { push, query } = useRouter();
const [sorting, setSorting] = useState<SortingState>([]);
const { data: session } = useSession();
const { data: task } = useLasTask();

const { hasRole } = usePermissions();
const isAdmin = hasRole(RoleName.ADMIN);
Expand Down Expand Up @@ -220,7 +221,12 @@ const AdminDataPage: React.FC<{ task: Task }> = ({ task }) => {
Uploading a new file will replace all the current data.
</p>
<div className="mt-10">
<DataUploader variant="inline" />
<DataUploader
variant="inline"
onUploadInProgress={(isUploadInProgress) => {
if (!isUploadInProgress) closeUploadDataSourceModal();
}}
/>
</div>
</div>

Expand Down
1 change: 0 additions & 1 deletion client/src/containers/admin/data-upload-error/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { useCallback, useState } from 'react';
import { format } from 'date-fns';

import { useUpdateTask, useTaskErrors } from 'hooks/tasks';
import { useProfile } from 'hooks/profile';
import UploadIcon from 'components/icons/upload-icon';
import Disclaimer from 'components/disclaimer';
import Button from 'components/button';
import { triggerCsvDownload } from 'utils/csv-download';
import { useUpdateTask, useTaskErrors } from '@/hooks/tasks';
import { useProfile } from '@/hooks/profile';
import UploadIcon from '@/components/icons/upload-icon';
import Disclaimer from '@/components/disclaimer';
import Button from '@/components/button';
import { triggerCsvDownload } from '@/utils/csv-download';

import type { Task } from 'types';
import type { DisclaimerProps } from 'components/disclaimer/component';
import type { Task } from '@/types';
import type { DisclaimerProps } from '@/components/disclaimer/component';

const VARIANT_STATUS: Record<Task['status'], DisclaimerProps['variant']> = {
completed: 'success',
Expand Down Expand Up @@ -50,16 +50,6 @@ const DataUploadError: React.FC<DataUploadErrorProps> = ({ task }) => {
>
<div className="flex w-full items-center space-x-6">
<div className="flex-1 space-y-1.5">
{task?.status === 'processing' && (
<>
<h3>Upload in progress</h3>
<p className="text-gray-500">
There is a uploading task in progress created at{' '}
{format(new Date(task.createdAt), 'MMM d, yyyy HH:mm z')}.
</p>
</>
)}

{task?.status === 'completed' && task?.errors.length === 0 && (
<>
<h3>Upload completed</h3>
Expand Down
40 changes: 0 additions & 40 deletions client/src/containers/admin/data-uploader/component.tsx

This file was deleted.

1 change: 0 additions & 1 deletion client/src/containers/admin/data-uploader/index.ts

This file was deleted.

48 changes: 48 additions & 0 deletions client/src/containers/admin/data-uploader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { DownloadIcon } from '@heroicons/react/solid';

import DataUploadError from '@/containers/admin/data-upload-error';
import { Anchor } from '@/components/button';
import UploadTracker from '@/containers/uploader/tracker';
import { useLasTask } from '@/hooks/tasks';
import DataUploader from '@/containers/uploader';

const AdminDataUploader: React.FC = () => {
const { data: lastTask } = useLasTask();

return (
<div className="flex h-full w-full items-center justify-center">
<div className="space-y-16">
<div className="space-y-4 text-center text-lg">
<p className="font-semibold">
1. Download the Excel template and fill it with your data.
</p>
<Anchor
href="/files/data-template.xlsx"
download
target="_blank"
rel="noopener noreferrer"
icon={<DownloadIcon aria-hidden="true" />}
>
Download template
</Anchor>
</div>
<div className="flex flex-col items-center space-y-4 text-center text-lg">
<p className="font-semibold">2. Upload the filled Excel file.</p>
{lastTask?.status !== 'processing' && (
<div className="w-[640px]">
<DataUploader />
</div>
)}
{lastTask?.status === 'processing' && (
<div className="w-[880px]">
<UploadTracker />
</div>
)}
{lastTask?.status === 'failed' && <DataUploadError task={lastTask} />}
</div>
</div>
</div>
);
};

export default AdminDataUploader;
1 change: 0 additions & 1 deletion client/src/containers/uploader/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { useCallback, useEffect, useState } from 'react';
import toast from 'react-hot-toast';
import classNames from 'classnames';
import { useRouter } from 'next/router';
import { useQueryClient } from '@tanstack/react-query';

import { useUploadDataSource } from 'hooks/sourcing-data';
import { useLasTask } from 'hooks/tasks';
import FileDropzone from 'components/file-dropzone';
import { useUploadDataSource } from '@/hooks/sourcing-data';
import { LAST_TASK_PARAMS, useLasTask } from '@/hooks/tasks';
import { env } from '@/env.mjs';
import FileDropzone from '@/components/file-dropzone';

import type { FileDropZoneProps } from 'components/file-dropzone/types';
import type { FileDropZoneProps } from '@/components/file-dropzone/types';
import type { Task } from 'types';

type DataUploaderProps = {
Expand All @@ -29,7 +30,7 @@ const DataUploader: React.FC<DataUploaderProps> = ({ variant = 'default', onUplo
const router = useRouter();
const uploadDataSource = useUploadDataSource();
const lastTask = useLasTask();
const { refetch: refetchLastTask } = lastTask;
const queryClient = useQueryClient();

const handleOnDrop: FileDropZoneProps['onDrop'] = useCallback(
(acceptedFiles) => {
Expand All @@ -40,8 +41,16 @@ const DataUploader: React.FC<DataUploaderProps> = ({ variant = 'default', onUplo
});

uploadDataSource.mutate(formData, {
onSuccess: () => {
refetchLastTask();
onSuccess: ({ data }) => {
const { attributes, ...rest } = data;
queryClient.setQueryData(['tasks', LAST_TASK_PARAMS], {
data: [
{
...rest,
...attributes,
},
],
});
},
onError: ({ response }) => {
const errors = response?.data?.errors;
Expand All @@ -53,7 +62,7 @@ const DataUploader: React.FC<DataUploaderProps> = ({ variant = 'default', onUplo
},
});
},
[refetchLastTask, uploadDataSource],
[uploadDataSource, queryClient],
);

const handleFileRejected: FileDropZoneProps['onDropRejected'] = useCallback((rejectedFiles) => {
Expand All @@ -69,12 +78,9 @@ const DataUploader: React.FC<DataUploaderProps> = ({ variant = 'default', onUplo
// Status of the uploading process
const isUploading = uploadDataSource.isLoading;
const isWaiting = uploadDataSource.isSuccess && currentTaskId === lastTask.data?.id;
const isProcessing = lastTask.data?.status === 'processing';
const isWorking = isUploading || isWaiting || isProcessing;
const isCompleted =
!isWorking &&
uploadDataSource.isSuccess &&
(lastTask.data?.status === 'completed' || lastTask.data?.status === 'failed');
const isTaskFailed = lastTask.data?.status === 'failed';
const isWorking = (isUploading || isWaiting) && !isTaskFailed;
const isCompleted = !isWorking && uploadDataSource.isSuccess;

useEffect(() => {
if (!currentTaskId && lastTask.data?.id) {
Expand All @@ -83,20 +89,15 @@ const DataUploader: React.FC<DataUploaderProps> = ({ variant = 'default', onUplo
}, [currentTaskId, lastTask.data?.id]);

useEffect(() => {
if (isCompleted && router.isReady) {
router.reload();
onUploadInProgress(false);
}
if (isCompleted) onUploadInProgress?.(false);
}, [isCompleted, onUploadInProgress, router]);

useEffect(() => {
if (isWorking && onUploadInProgress) {
onUploadInProgress(true);
}
if (isWorking && onUploadInProgress) onUploadInProgress?.(true);
}, [isWorking, onUploadInProgress]);

return (
<div className="relative w-full min-w-[640px]">
<div className="relative w-full">
<div
className={classNames('relative z-10 rounded-xl bg-white', {
'p-4 shadow-lg': variant === 'default',
Expand All @@ -110,19 +111,6 @@ const DataUploader: React.FC<DataUploaderProps> = ({ variant = 'default', onUplo
isUploading={isWorking}
/>
</div>

{isWorking && (
<div className="w-full px-20">
<div className="rounded-b-xl bg-white px-10 py-4">
<div className="h-[4px] w-full rounded bg-gradient-to-r from-[#5FCFF9] via-[#42A56A] to-[#F5CA7D]" />
<p className="mt-1 text-left text-xs text-gray-500">
{isUploading && 'Uploading file...'}
{isWaiting && 'File uploaded successfully! Starting to process the data...'}
{isProcessing && 'Processing file...'}
</p>
</div>
</div>
)}
</div>
);
};
Expand Down
Loading
Loading