diff --git a/src/renderer/api/cadt/v1/projects/projects.api.ts b/src/renderer/api/cadt/v1/projects/projects.api.ts index d0126229..a2e02799 100644 --- a/src/renderer/api/cadt/v1/projects/projects.api.ts +++ b/src/renderer/api/cadt/v1/projects/projects.api.ts @@ -3,11 +3,10 @@ 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 GetProjectParams { @@ -27,7 +26,7 @@ interface GetProjectsResponse { const projectsApi = cadtApi.injectEndpoints({ endpoints: (builder) => ({ getProjects: builder.query({ - query: ({ page, orgUid, search, order, xls }: GetProjectsParams) => { + query: ({ page, orgUid, search, order }: GetProjectsParams) => { // Initialize the params object with page and limit const params: GetProjectsParams & { limit: number } = { page, limit: 10 }; @@ -43,27 +42,18 @@ const projectsApi = cadtApi.injectEndpoints({ params.order = order; } - if (xls) { - params.xls = xls; - if (!orgUid) { - params.orgUid = 'all'; - } - } - return { url: `/v1/projects`, params, 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 }: 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; @@ -77,21 +67,12 @@ const projectsApi = cadtApi.injectEndpoints({ params.order = order; } - if (xls) { - params.xls = xls; - if (!orgUid) { - params.orgUid = 'all'; - } - } - return { url: `/v1/projects`, params, method: 'GET', }; }, - // @ts-ignore - providesTags: (_response, _error, { orgUid }) => [{ type: projectsTag, id: orgUid }], }), getProject: builder.query({ @@ -206,22 +187,6 @@ 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 }) => ({ - url: `/v1/projects/xlsx`, - method: 'GET', - params: { orgUid, xls: true }, - }), - }), }), }); @@ -234,7 +199,5 @@ export const { useDeleteProjectMutation, useStageCreateProjectMutation, useStageUpdateProjectMutation, - useDownloadProjectsXlsQuery, useUploadProjectsXlsMutation, - useDownloadProjectsXlsImmediateMutation, } = projectsApi; 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 new file mode 100644 index 00000000..d2eaa426 --- /dev/null +++ b/src/renderer/components/blocks/buttons/ProjectXlsUploadDownloadButtons.tsx @@ -0,0 +1,129 @@ +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'; + +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(); + 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 triggerUploadProjectXls({ 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 = 'projects-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/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; + } + + 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 { ProjectXlsUploadDownloadButtons }; 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/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..ff471e53 100644 --- a/src/renderer/components/blocks/buttons/index.ts +++ b/src/renderer/components/blocks/buttons/index.ts @@ -1,4 +1,5 @@ export * from './QueryRefetchButton'; export * from './ConnectButton'; export * from './FormButton'; -export * from './XlsUploadDownloadButtons'; +export * from './ProjectXlsUploadDownloadButtons'; +export * from './UnitXlsUploadDownloadButtons'; diff --git a/src/renderer/components/blocks/tabs/CommittedProjectsTab.tsx b/src/renderer/components/blocks/tabs/CommittedProjectsTab.tsx index 765adda1..a73c2843 100644 --- a/src/renderer/components/blocks/tabs/CommittedProjectsTab.tsx +++ b/src/renderer/components/blocks/tabs/CommittedProjectsTab.tsx @@ -8,12 +8,19 @@ import { debounce } from 'lodash'; interface PageTabProps { orgUid: string; search: string; + order: string; + setOrder: (order: string) => void; setIsLoading: (isLoading: boolean) => void; } -const CommittedProjectsTab: React.FC = ({ orgUid, search, setIsLoading }: PageTabProps) => { +const CommittedProjectsTab: React.FC = ({ + orgUid, + search, + order, + setOrder, + setIsLoading, +}: PageTabProps) => { const [currentPage, setCurrentPage] = useQueryParamState('page', '1'); - const [order, setOrder] = useQueryParamState('order', undefined); const handleSetOrder = useColumnOrderHandler(order, setOrder); const [projectDetailsFragment, projectDetailsModalActive, setProjectModalActive] = useWildCardUrlHash('project'); const { diff --git a/src/renderer/components/blocks/tabs/CommittedUnitsTab.tsx b/src/renderer/components/blocks/tabs/CommittedUnitsTab.tsx index dde16d0c..6dd66d9c 100644 --- a/src/renderer/components/blocks/tabs/CommittedUnitsTab.tsx +++ b/src/renderer/components/blocks/tabs/CommittedUnitsTab.tsx @@ -8,12 +8,13 @@ import { debounce } from 'lodash'; interface PageTabProps { orgUid: string; search: string; + order: string; + setOrder: (order: string) => void; setIsLoading: (isLoading: boolean) => void; } -const CommittedUnitsTab: React.FC = ({ orgUid, search, setIsLoading }: PageTabProps) => { +const CommittedUnitsTab: React.FC = ({ orgUid, search, order, setOrder, setIsLoading }: PageTabProps) => { const [currentPage, setCurrentPage] = useQueryParamState('page', '1'); - const [order, setOrder] = useQueryParamState('order', undefined); const handleSetOrder = useColumnOrderHandler(order, setOrder); const [unitDetailsFragment, unitDetailsModalActive, setUnitsModalActive] = useWildCardUrlHash('unit'); const { diff --git a/src/renderer/components/blocks/widgets/DownloadProjectXlsButton.tsx b/src/renderer/components/blocks/widgets/DownloadProjectXlsButton.tsx deleted file mode 100644 index 0f6b6eeb..00000000 --- a/src/renderer/components/blocks/widgets/DownloadProjectXlsButton.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import { useQueryParamState } from '@/hooks'; -import { useGetProjectsImmediateMutation } from '@/api'; - -interface DownloadProjectsProps { - orgUid: string; -} - -const DownloadProjectXlsButton: React.FC = () => { - const [getProjects, { isLoading: projectsLoading }] = useGetProjectsImmediateMutation(); - const [currentPage] = useQueryParamState('page', '1'); - const [orgUid] = useQueryParamState('orgUid', undefined); - const [search] = useQueryParamState('search', undefined); - const [order] = useQueryParamState('order', undefined); - - const handleDownload = (data: any) => { - const fileData = JSON.stringify(data); - const blob = new Blob([fileData], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = 'projects-data.json'; - document.body.appendChild(link); - link.click(); - link.remove(); - URL.revokeObjectURL(url); - }; - - const handleClick = async () => { - try { - const result = await getProjects({ page: Number(currentPage), orgUid, search, order, xls: true }).unwrap(); - handleDownload(result); - } catch (error) { - console.error('Error fetching projects:', error); - } - }; - - return ( -
- -
- ); -}; - -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'; diff --git a/src/renderer/pages/MyProjectsPage.tsx b/src/renderer/pages/MyProjectsPage.tsx index a5f32af6..42165394 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'; @@ -40,6 +40,7 @@ const MyProjectsPage: React.FC = () => { const navigate = useNavigate(); const [orgUid, setOrgUid] = useQueryParamState('orgUid', undefined); const [search, setSearch] = useQueryParamState('search', undefined); + const [order, setOrder] = useQueryParamState('order', undefined); const [projectStagedSuccess, setProjectStagedSuccess] = useUrlHash('success-stage-project'); const [commitModalActive, setCommitModalActive] = useUrlHash('commit-staged-items'); const [, setCreateProjectModalActive] = useUrlHash('create-project'); @@ -124,7 +125,7 @@ const MyProjectsPage: React.FC = () => { )} - {myOrganization && } + {myOrganization && } {orgUid && } @@ -132,7 +133,13 @@ const MyProjectsPage: React.FC = () => { setActiveTab(tab)}> }> {activeTab === TabTypes.COMMITTED && ( - + )} { const [stagingDiffFragment, stagingDiffModalActive, setStagingDiffModalActive] = useWildCardUrlHash('staging'); const [orgUid, setOrgUid] = useQueryParamState('orgUid', undefined); const [search, setSearch] = useQueryParamState('search', undefined); + const [order, setOrder] = useQueryParamState('order', undefined); const [, setCreateUnitModalActive] = useUrlHash('create-unit'); const [commitModalActive, setCommitModalActive] = useUrlHash('commit-staged-items'); const [activeTab, setActiveTab] = useState(TabTypes.COMMITTED); @@ -126,7 +127,7 @@ const MyUnitsPage: React.FC = () => { )} - {myOrganization && } + {myOrganization && } {orgUid && } @@ -134,7 +135,13 @@ const MyUnitsPage: React.FC = () => { setActiveTab(tab)}> }> {activeTab === TabTypes.COMMITTED && ( - + )} { return ( <> {projectsFetching && } -
+
+ { {unitsFetching && }
+