From ea87c60a80e909881c066cf5f5208cbdf2f33628 Mon Sep 17 00:00:00 2001 From: William Wills Date: Wed, 15 May 2024 12:23:02 -0400 Subject: [PATCH 1/4] feat: refactored upload and download buttons to be projects only, add added the ability to have only the download button only fix: downloadProjectXlsImmediate mutation --- .../api/cadt/v1/projects/projects.api.ts | 54 +++++---- .../ProjectXlsUploadDownloadButtons.tsx | 114 ++++++++++++++++++ .../buttons/XlsUploadDownloadButtons.tsx | 86 ------------- .../components/blocks/buttons/index.ts | 2 +- .../widgets/DownloadProjectXlsButton.tsx | 6 +- src/renderer/pages/MyProjectsPage.tsx | 4 +- src/renderer/pages/MyUnitsPage.tsx | 8 +- src/renderer/translations/tokens/en-US.json | 3 +- 8 files changed, 157 insertions(+), 120 deletions(-) create mode 100644 src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx delete mode 100644 src/renderer/components/blocks/buttons/XlsUploadDownloadButtons.tsx diff --git a/src/renderer/api/cadt/v1/projects/projects.api.ts b/src/renderer/api/cadt/v1/projects/projects.api.ts index d0126229..17afefda 100644 --- a/src/renderer/api/cadt/v1/projects/projects.api.ts +++ b/src/renderer/api/cadt/v1/projects/projects.api.ts @@ -3,13 +3,19 @@ import { cadtApi, projectsTag, stagedProjectsTag } from '../'; import { Project } from '@/schemas/Project.schema'; interface GetProjectsParams { - page: number; + page?: number; orgUid?: string | null; search?: string | null; order?: string | null; xls?: boolean | null; } +interface DownloadProjectParams { + orgUid?: string; + search?: string; + order?: string; +} + interface GetProjectParams { warehouseProjectId: string; } @@ -56,14 +62,12 @@ const projectsApi = cadtApi.injectEndpoints({ method: 'GET', }; }, - // @ts-ignore - providesTags: (_response, _error, { orgUid }) => [{ type: projectsTag, id: orgUid }], }), getProjectsImmediate: builder.mutation({ - query: ({ page, orgUid, search, order, xls }: GetProjectsParams) => { + query: ({ orgUid, search, order, xls }: GetProjectsParams) => { // Initialize the params object with page and limit - const params: GetProjectsParams & { limit: number } = { page, limit: 10 }; + const params: GetProjectsParams = {}; if (orgUid) { params.orgUid = orgUid; @@ -90,8 +94,6 @@ const projectsApi = cadtApi.injectEndpoints({ method: 'GET', }; }, - // @ts-ignore - providesTags: (_response, _error, { orgUid }) => [{ type: projectsTag, id: orgUid }], }), getProject: builder.query({ @@ -207,20 +209,31 @@ const projectsApi = cadtApi.injectEndpoints({ invalidatesTags: [stagedProjectsTag], }), - downloadProjectsXls: builder.query({ - query: ({ orgUid }) => ({ - url: `/v1/projects/xlsx`, - method: 'GET', - params: { orgUid, xls: true }, - }), - }), + downloadProjectsXlsImmediate: builder.mutation({ + query: ({ orgUid, search, order }: DownloadProjectParams) => { + const params: DownloadProjectParams & { xls: boolean } = { xls: true }; - downloadProjectsXlsImmediate: builder.mutation({ - query: ({ orgUid }) => ({ - url: `/v1/projects/xlsx`, - method: 'GET', - params: { orgUid, xls: true }, - }), + if (orgUid) { + params.orgUid = orgUid; + } else { + params.orgUid = 'all'; + } + + if (search) { + params.search = search.replace(/[^a-zA-Z0-9 _.-]+/, ''); + } + + if (order) { + params.order = order; + } + + return { + url: `/v1/projects`, + params, + method: 'GET', + responseHandler: (response: any) => response.blob(), + }; + }, }), }), }); @@ -234,7 +247,6 @@ export const { useDeleteProjectMutation, useStageCreateProjectMutation, useStageUpdateProjectMutation, - useDownloadProjectsXlsQuery, useUploadProjectsXlsMutation, useDownloadProjectsXlsImmediateMutation, } = projectsApi; diff --git a/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx b/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx new file mode 100644 index 00000000..9a3f64ca --- /dev/null +++ b/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx @@ -0,0 +1,114 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { AiOutlineDownload, AiOutlineUpload } from 'react-icons/ai'; +import { Button } from '@/components'; +import { useDownloadProjectsXlsImmediateMutation, useUploadProjectsXlsMutation } from '@/api'; +import { Alert } from 'flowbite-react'; +import { FormattedMessage } from 'react-intl'; +import { useQueryParamState } from '@/hooks'; + +interface XlsUploadDownloadButtonsProps { + downloadOnly?: boolean; +} + +const ProjectXlsUploadDownloadButtons: React.FC = ({ + downloadOnly, +}: XlsUploadDownloadButtonsProps) => { + // upload hooks and state + const [triggerUploadProjectXls] = useUploadProjectsXlsMutation(); + const fileInputRef = useRef(null); + const [showUploadFailedAlert, setShowUploadFailedAlert] = useState(false); + + // download hooks and state + const [triggerDownloadProjectsXls, { isLoading: downloadProjectsLoading, error: downloadProjectsError }] = + useDownloadProjectsXlsImmediateMutation(); + const [showDownLoadFailedAlert, setShowDownloadFailedAlert] = useState(false); + const [orgUid] = useQueryParamState('orgUid', undefined); + const [search] = useQueryParamState('search', undefined); + const [order] = useQueryParamState('order', undefined); + + useEffect(() => { + if (downloadProjectsError) { + setShowDownloadFailedAlert(true); + } + }, [downloadProjectsError]); + + const handleFileChange = async (event: React.ChangeEvent) => { + const xlsx: File | undefined = event.target.files?.[0]; + + if (xlsx) { + const uploadResult: any = await triggerUploadProjectXls({ xlsx }); + if (uploadResult?.data?.error || uploadResult?.error) { + setShowUploadFailedAlert(true); + } + } + }; + + const handleUpload = () => { + fileInputRef.current?.click(); + }; + + const handleDownload = async (data: any) => { + const fileData = JSON.stringify(data); + const blob = new Blob([fileData], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = 'projects-data.xlsx'; + document.body.appendChild(link); + link.click(); + link.remove(); + URL.revokeObjectURL(url); + }; + + const handleClickDownload = async () => { + try { + const result = await triggerDownloadProjectsXls({ + orgUid, + search, + order, + }).unwrap(); + handleDownload(result); + } catch (error) { + setShowDownloadFailedAlert(true); + } + }; + + return ( + <> + {downloadOnly ? ( + + ) : ( + <> + + + + + + + )} + + {showUploadFailedAlert && ( +
+ setShowUploadFailedAlert(false)}> + ! + +
+ )} + {showDownLoadFailedAlert && ( +
+ setShowDownloadFailedAlert(false)}> + ! + +
+ )} + + ); +}; + +export { ProjectXlsUploadDownloadButtons }; diff --git a/src/renderer/components/blocks/buttons/XlsUploadDownloadButtons.tsx b/src/renderer/components/blocks/buttons/XlsUploadDownloadButtons.tsx deleted file mode 100644 index 33bb2d6d..00000000 --- a/src/renderer/components/blocks/buttons/XlsUploadDownloadButtons.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useRef, useState } from 'react'; -import { AiOutlineDownload, AiOutlineUpload } from 'react-icons/ai'; -import { Button } from '@/components'; -import { - useDownloadProjectsXlsImmediateMutation, - useDownloadUnitsXlsImmediateMutation, - useUploadProjectsXlsMutation, - useUploadUnitsXlsMutation, -} from '@/api'; -import { Alert } from 'flowbite-react'; -import { FormattedMessage } from 'react-intl'; - -interface XlsUploadDownloadButtonsProps { - type: 'project' | 'unit'; - orgUid: string; -} - -const XlsUploadDownloadButtons: React.FC = ({ - type, - orgUid, -}: XlsUploadDownloadButtonsProps) => { - const fileInputRef = useRef(null); - const [triggerUploadProjectXls] = useUploadProjectsXlsMutation(); - const [triggerDownloadProjectXls] = useDownloadProjectsXlsImmediateMutation(); - const [triggerUploadUnitXls] = useUploadUnitsXlsMutation(); - const [triggerDownloadUnitsXls] = useDownloadUnitsXlsImmediateMutation(); - const [showFailedAlert, setShowFailedAlert] = useState(false); - - const handleFileChange = async (event: React.ChangeEvent) => { - const xlsx: File | undefined = event.target.files?.[0]; - - if (xlsx && type === 'project') { - const uploadResult: any = await triggerUploadProjectXls({ xlsx }); - if (uploadResult?.data?.error || uploadResult?.error) { - setShowFailedAlert(true); - } - } else if (xlsx) { - const uploadResult: any = await triggerUploadUnitXls({ xlsx }); - if (uploadResult?.data?.error || uploadResult?.error) { - setShowFailedAlert(true); - } - } - }; - - const handleUpload = () => { - fileInputRef.current?.click(); - }; - - const handleDownload = async () => { - const xlsxData: any = - type === 'project' ? await triggerDownloadProjectXls({ orgUid }) : await triggerDownloadUnitsXls({ orgUid }); - console.log(xlsxData); - - if (!xlsxData?.data?.error && !xlsxData?.error) { - const url = xlsxData; - const link = document.createElement('a'); - link.href = url; - link.download = type === 'project' ? 'projects.xlsx' : 'units.xlsx'; - link.click(); - link.remove(); - } - }; - - return ( - <> - - - - - - {showFailedAlert && ( -
- setShowFailedAlert(false)}> - ! - -
- )} - - ); -}; - -export { XlsUploadDownloadButtons }; diff --git a/src/renderer/components/blocks/buttons/index.ts b/src/renderer/components/blocks/buttons/index.ts index 4f82d666..d6904af9 100644 --- a/src/renderer/components/blocks/buttons/index.ts +++ b/src/renderer/components/blocks/buttons/index.ts @@ -1,4 +1,4 @@ export * from './QueryRefetchButton'; export * from './ConnectButton'; export * from './FormButton'; -export * from './XlsUploadDownloadButtons'; +export * from './ProjectXlsUploadDownloadButtons'; diff --git a/src/renderer/components/blocks/widgets/DownloadProjectXlsButton.tsx b/src/renderer/components/blocks/widgets/DownloadProjectXlsButton.tsx index 0f6b6eeb..cca217a3 100644 --- a/src/renderer/components/blocks/widgets/DownloadProjectXlsButton.tsx +++ b/src/renderer/components/blocks/widgets/DownloadProjectXlsButton.tsx @@ -2,11 +2,7 @@ import React from 'react'; import { useQueryParamState } from '@/hooks'; import { useGetProjectsImmediateMutation } from '@/api'; -interface DownloadProjectsProps { - orgUid: string; -} - -const DownloadProjectXlsButton: React.FC = () => { +const DownloadProjectXlsButton: React.FC = () => { const [getProjects, { isLoading: projectsLoading }] = useGetProjectsImmediateMutation(); const [currentPage] = useQueryParamState('page', '1'); const [orgUid] = useQueryParamState('orgUid', undefined); diff --git a/src/renderer/pages/MyProjectsPage.tsx b/src/renderer/pages/MyProjectsPage.tsx index a5f32af6..d07e3ebf 100644 --- a/src/renderer/pages/MyProjectsPage.tsx +++ b/src/renderer/pages/MyProjectsPage.tsx @@ -9,12 +9,12 @@ import { ComponentCenteredSpinner, IndeterminateProgressOverlay, OrgUidBadge, + ProjectXlsUploadDownloadButtons, SearchBox, StagedProjectSuccessModal, StagingTableTab, SyncIndicator, Tabs, - XlsUploadDownloadButtons, } from '@/components'; import { FormattedMessage } from 'react-intl'; import { useGetOrganizationsMapQuery } from '@/api/cadt/v1/organizations'; @@ -124,7 +124,7 @@ const MyProjectsPage: React.FC = () => { )} - {myOrganization && } + {myOrganization && } {orgUid && } diff --git a/src/renderer/pages/MyUnitsPage.tsx b/src/renderer/pages/MyUnitsPage.tsx index 7f392fb3..7b67b74b 100644 --- a/src/renderer/pages/MyUnitsPage.tsx +++ b/src/renderer/pages/MyUnitsPage.tsx @@ -9,13 +9,13 @@ import { ComponentCenteredSpinner, IndeterminateProgressOverlay, OrgUidBadge, + ProjectXlsUploadDownloadButtons, SearchBox, + StagedUnitSuccessModal, + StagingDiffModal, StagingTableTab, SyncIndicator, Tabs, - StagedUnitSuccessModal, - StagingDiffModal, - XlsUploadDownloadButtons, } from '@/components'; import { FormattedMessage } from 'react-intl'; import { useGetOrganizationsMapQuery } from '@/api/cadt/v1/organizations'; @@ -126,7 +126,7 @@ const MyUnitsPage: React.FC = () => { )} - {myOrganization && } + {myOrganization && } {orgUid && } diff --git a/src/renderer/translations/tokens/en-US.json b/src/renderer/translations/tokens/en-US.json index 42eddd80..6dc28be7 100644 --- a/src/renderer/translations/tokens/en-US.json +++ b/src/renderer/translations/tokens/en-US.json @@ -129,5 +129,6 @@ "upload-failed": "Upload failed", "split": "Split", "split-unit": "Split Unit", - "detailed-audit-view": "Detailed Audit Record" + "detailed-audit-view": "Detailed Audit Record", + "download-failed": "Download failed" } From af793d48b4831d09b7f263fcfc694b11867b2a5f Mon Sep 17 00:00:00 2001 From: William Wills Date: Wed, 15 May 2024 16:39:56 -0400 Subject: [PATCH 2/4] feat: projects xls downloading successfully --- .../ProjectXlsUploadDownloadButtons.tsx | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx b/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx index 9a3f64ca..310f7c80 100644 --- a/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx +++ b/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx @@ -1,7 +1,7 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { AiOutlineDownload, AiOutlineUpload } from 'react-icons/ai'; import { Button } from '@/components'; -import { useDownloadProjectsXlsImmediateMutation, useUploadProjectsXlsMutation } from '@/api'; +import { useUploadProjectsXlsMutation } from '@/api'; import { Alert } from 'flowbite-react'; import { FormattedMessage } from 'react-intl'; import { useQueryParamState } from '@/hooks'; @@ -17,20 +17,29 @@ const ProjectXlsUploadDownloadButtons: React.FC = const [triggerUploadProjectXls] = useUploadProjectsXlsMutation(); const fileInputRef = useRef(null); const [showUploadFailedAlert, setShowUploadFailedAlert] = useState(false); + const [downloadLoading, setDownloadLoading] = useState(false); // download hooks and state - const [triggerDownloadProjectsXls, { isLoading: downloadProjectsLoading, error: downloadProjectsError }] = - useDownloadProjectsXlsImmediateMutation(); const [showDownLoadFailedAlert, setShowDownloadFailedAlert] = useState(false); const [orgUid] = useQueryParamState('orgUid', undefined); const [search] = useQueryParamState('search', undefined); const [order] = useQueryParamState('order', undefined); useEffect(() => { - if (downloadProjectsError) { - setShowDownloadFailedAlert(true); + if (showDownLoadFailedAlert) { + setDownloadLoading(false); } - }, [downloadProjectsError]); + }, [showDownLoadFailedAlert]); + + const downloadXlsUrl: URL = useMemo(() => { + const url = new URL('http://localhost:31310/v1/projects'); + url.searchParams.append('xls', 'true'); + orgUid && url.searchParams.append('orgUid', orgUid); + search && url.searchParams.append('search', search); + order && url.searchParams.append('order', order); + + return url; + }, [order, orgUid, search]); const handleFileChange = async (event: React.ChangeEvent) => { const xlsx: File | undefined = event.target.files?.[0]; @@ -47,9 +56,8 @@ const ProjectXlsUploadDownloadButtons: React.FC = fileInputRef.current?.click(); }; - const handleDownload = async (data: any) => { - const fileData = JSON.stringify(data); - const blob = new Blob([fileData], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); + const handleDownloadedData = async (data: Blob) => { + const blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; @@ -61,13 +69,22 @@ const ProjectXlsUploadDownloadButtons: React.FC = }; const handleClickDownload = async () => { + setDownloadLoading(true); try { - const result = await triggerDownloadProjectsXls({ - orgUid, - search, - order, - }).unwrap(); - handleDownload(result); + const downloadResponse: Response = await fetch(downloadXlsUrl); + if (!downloadResponse?.ok) { + setShowDownloadFailedAlert(true); + return; + } + + const blob: Blob = await downloadResponse.blob(); + if (!blob) { + setShowDownloadFailedAlert(true); + return; + } + + await handleDownloadedData(blob); + setDownloadLoading(false); } catch (error) { setShowDownloadFailedAlert(true); } @@ -76,14 +93,14 @@ const ProjectXlsUploadDownloadButtons: React.FC = return ( <> {downloadOnly ? ( - ) : ( <> - - - ); -}; - -export { DownloadProjectXlsButton }; diff --git a/src/renderer/components/blocks/widgets/index.ts b/src/renderer/components/blocks/widgets/index.ts index d3ae9ceb..d1a64eb1 100644 --- a/src/renderer/components/blocks/widgets/index.ts +++ b/src/renderer/components/blocks/widgets/index.ts @@ -7,4 +7,3 @@ export * from './UnitSummary'; export * from './DiffViewer'; export * from './ProjectAndUnitActions'; export * from './StagedItemActions'; -export * from './DownloadProjectXlsButton'; From fb28d5660c98941ce0f6828dca32569d276f0134 Mon Sep 17 00:00:00 2001 From: William Wills Date: Thu, 16 May 2024 12:17:33 -0400 Subject: [PATCH 4/4] feat: added download/upload buttons to my units feat: added download button to projects page feat: added download button to units page fix: orgUid selection applying to download only after page refresh --- src/renderer/api/cadt/v1/units/units.api.ts | 22 +-- .../ProjectXlsUploadDownloadButtons.tsx | 30 ++-- .../buttons/UnitXlsUploadDownloadButtons.tsx | 130 ++++++++++++++++++ .../components/blocks/buttons/index.ts | 1 + src/renderer/pages/MyUnitsPage.tsx | 4 +- src/renderer/pages/ProjectsListPage.tsx | 4 +- src/renderer/pages/UnitsListPage.tsx | 2 + 7 files changed, 154 insertions(+), 39 deletions(-) create mode 100644 src/renderer/components/blocks/buttons/UnitXlsUploadDownloadButtons.tsx diff --git a/src/renderer/api/cadt/v1/units/units.api.ts b/src/renderer/api/cadt/v1/units/units.api.ts index e988347f..27973b02 100644 --- a/src/renderer/api/cadt/v1/units/units.api.ts +++ b/src/renderer/api/cadt/v1/units/units.api.ts @@ -1,4 +1,4 @@ -import { isNil, isEmpty, omit } from 'lodash'; +import { isEmpty, isNil, omit } from 'lodash'; import { cadtApi, stagedUnitsTag, unitsTag } from '../'; import { Unit } from '@/schemas/Unit.schema'; @@ -110,7 +110,7 @@ const unitsApi = cadtApi.injectEndpoints({ }), stageSplitUnit: builder.mutation({ - query: ({warehouseUnitId, records}) => { + query: ({ warehouseUnitId, records }) => { return { url: `/v1/units/split`, method: 'POST', @@ -136,22 +136,6 @@ const unitsApi = cadtApi.injectEndpoints({ }, invalidatesTags: [stagedUnitsTag], }), - - downloadUnitsXls: builder.query({ - query: ({ orgUid }) => ({ - url: `/v1/units/xlsx`, - method: 'GET', - params: { orgUid, xls: true }, - }), - }), - - downloadUnitsXlsImmediate: builder.mutation({ - query: ({ orgUid }) => ({ - url: `/v1/units/xlsx`, - method: 'GET', - params: { orgUid, xls: true }, - }), - }), }), }); @@ -164,7 +148,5 @@ export const { useStageCreateUnitMutation, useStageUpdateUnitMutation, useStageSplitUnitMutation, - useDownloadUnitsXlsQuery, useUploadUnitsXlsMutation, - useDownloadUnitsXlsImmediateMutation, } = unitsApi; diff --git a/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx b/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx index 310f7c80..d2eaa426 100644 --- a/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx +++ b/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx @@ -1,17 +1,22 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { AiOutlineDownload, AiOutlineUpload } from 'react-icons/ai'; import { Button } from '@/components'; import { useUploadProjectsXlsMutation } from '@/api'; import { Alert } from 'flowbite-react'; import { FormattedMessage } from 'react-intl'; -import { useQueryParamState } from '@/hooks'; interface XlsUploadDownloadButtonsProps { + orgUid: string; + order: string; + search: string; downloadOnly?: boolean; } const ProjectXlsUploadDownloadButtons: React.FC = ({ downloadOnly, + orgUid, + order, + search, }: XlsUploadDownloadButtonsProps) => { // upload hooks and state const [triggerUploadProjectXls] = useUploadProjectsXlsMutation(); @@ -21,9 +26,6 @@ const ProjectXlsUploadDownloadButtons: React.FC = // download hooks and state const [showDownLoadFailedAlert, setShowDownloadFailedAlert] = useState(false); - const [orgUid] = useQueryParamState('orgUid', undefined); - const [search] = useQueryParamState('search', undefined); - const [order] = useQueryParamState('order', undefined); useEffect(() => { if (showDownLoadFailedAlert) { @@ -31,16 +33,6 @@ const ProjectXlsUploadDownloadButtons: React.FC = } }, [showDownLoadFailedAlert]); - const downloadXlsUrl: URL = useMemo(() => { - const url = new URL('http://localhost:31310/v1/projects'); - url.searchParams.append('xls', 'true'); - orgUid && url.searchParams.append('orgUid', orgUid); - search && url.searchParams.append('search', search); - order && url.searchParams.append('order', order); - - return url; - }, [order, orgUid, search]); - const handleFileChange = async (event: React.ChangeEvent) => { const xlsx: File | undefined = event.target.files?.[0]; @@ -71,7 +63,13 @@ const ProjectXlsUploadDownloadButtons: React.FC = const handleClickDownload = async () => { setDownloadLoading(true); try { - const downloadResponse: Response = await fetch(downloadXlsUrl); + const url = new URL('http://localhost:31310/v1/projects'); + url.searchParams.append('xls', 'true'); + orgUid && url.searchParams.append('orgUid', orgUid); + search && url.searchParams.append('search', search); + order && url.searchParams.append('order', order); + + const downloadResponse: Response = await fetch(url); if (!downloadResponse?.ok) { setShowDownloadFailedAlert(true); return; diff --git a/src/renderer/components/blocks/buttons/UnitXlsUploadDownloadButtons.tsx b/src/renderer/components/blocks/buttons/UnitXlsUploadDownloadButtons.tsx new file mode 100644 index 00000000..8b6812f9 --- /dev/null +++ b/src/renderer/components/blocks/buttons/UnitXlsUploadDownloadButtons.tsx @@ -0,0 +1,130 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { AiOutlineDownload, AiOutlineUpload } from 'react-icons/ai'; +import { Button } from '@/components'; +import { useUploadUnitsXlsMutation } from '@/api'; +import { Alert } from 'flowbite-react'; +import { FormattedMessage } from 'react-intl'; + +interface XlsUploadDownloadButtonsProps { + orgUid: string; + order: string; + search: string; + downloadOnly?: boolean; +} + +const UnitXlsUploadDownloadButtons: React.FC = ({ + downloadOnly, + search, + order, + orgUid, +}: XlsUploadDownloadButtonsProps) => { + // upload hooks and state + const [triggerUploadUnitXls] = useUploadUnitsXlsMutation(); + const fileInputRef = useRef(null); + const [showUploadFailedAlert, setShowUploadFailedAlert] = useState(false); + const [downloadLoading, setDownloadLoading] = useState(false); + + // download hooks and state + const [showDownLoadFailedAlert, setShowDownloadFailedAlert] = useState(false); + + useEffect(() => { + if (showDownLoadFailedAlert) { + setDownloadLoading(false); + } + }, [showDownLoadFailedAlert]); + + const handleFileChange = async (event: React.ChangeEvent) => { + const xlsx: File | undefined = event.target.files?.[0]; + + if (xlsx) { + const uploadResult: any = await triggerUploadUnitXls({ xlsx }); + if (uploadResult?.data?.error || uploadResult?.error) { + setShowUploadFailedAlert(true); + } + } + }; + + const handleUpload = () => { + fileInputRef.current?.click(); + }; + + const handleDownloadedData = async (data: Blob) => { + const blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = 'units-data.xlsx'; + document.body.appendChild(link); + link.click(); + link.remove(); + URL.revokeObjectURL(url); + }; + + const handleClickDownload = async () => { + setDownloadLoading(true); + try { + const url = new URL('http://localhost:31310/v1/units'); + url.searchParams.append('xls', 'true'); + console.log('**** units', orgUid); + orgUid && url.searchParams.append('orgUid', orgUid); + search && url.searchParams.append('search', search); + order && url.searchParams.append('order', order); + + const downloadResponse: Response = await fetch(url); + if (!downloadResponse?.ok) { + setShowDownloadFailedAlert(true); + return; + } + + const blob: Blob = await downloadResponse.blob(); + if (!blob) { + setShowDownloadFailedAlert(true); + return; + } + + await handleDownloadedData(blob); + setDownloadLoading(false); + } catch (error) { + setShowDownloadFailedAlert(true); + } + }; + + return ( + <> + {downloadOnly ? ( + + ) : ( + <> + + + + + + + )} + + {showUploadFailedAlert && ( +
+ setShowUploadFailedAlert(false)}> + ! + +
+ )} + {showDownLoadFailedAlert && ( +
+ setShowDownloadFailedAlert(false)}> + ! + +
+ )} + + ); +}; + +export { UnitXlsUploadDownloadButtons }; diff --git a/src/renderer/components/blocks/buttons/index.ts b/src/renderer/components/blocks/buttons/index.ts index d6904af9..ff471e53 100644 --- a/src/renderer/components/blocks/buttons/index.ts +++ b/src/renderer/components/blocks/buttons/index.ts @@ -2,3 +2,4 @@ export * from './QueryRefetchButton'; export * from './ConnectButton'; export * from './FormButton'; export * from './ProjectXlsUploadDownloadButtons'; +export * from './UnitXlsUploadDownloadButtons'; diff --git a/src/renderer/pages/MyUnitsPage.tsx b/src/renderer/pages/MyUnitsPage.tsx index 7b67b74b..8fe61e01 100644 --- a/src/renderer/pages/MyUnitsPage.tsx +++ b/src/renderer/pages/MyUnitsPage.tsx @@ -9,13 +9,13 @@ import { ComponentCenteredSpinner, IndeterminateProgressOverlay, OrgUidBadge, - ProjectXlsUploadDownloadButtons, SearchBox, StagedUnitSuccessModal, StagingDiffModal, StagingTableTab, SyncIndicator, Tabs, + UnitXlsUploadDownloadButtons, } from '@/components'; import { FormattedMessage } from 'react-intl'; import { useGetOrganizationsMapQuery } from '@/api/cadt/v1/organizations'; @@ -126,7 +126,7 @@ const MyUnitsPage: React.FC = () => { )} - {myOrganization && } + {myOrganization && } {orgUid && } diff --git a/src/renderer/pages/ProjectsListPage.tsx b/src/renderer/pages/ProjectsListPage.tsx index 7ef9f5f9..f879cbd6 100644 --- a/src/renderer/pages/ProjectsListPage.tsx +++ b/src/renderer/pages/ProjectsListPage.tsx @@ -8,6 +8,7 @@ import { OrgUidBadge, ProjectModal, ProjectsListTable, + ProjectXlsUploadDownloadButtons, SearchBox, SkeletonTable, SyncIndicator, @@ -68,8 +69,9 @@ const ProjectsListPage: React.FC = () => { return ( <> {projectsFetching && } -
+
+ { {unitsFetching && }
+