From e6c13a3e80895a433d68fb3c9af6210da3eb157f Mon Sep 17 00:00:00 2001 From: Dragos-Paul Strat Date: Thu, 26 Sep 2024 14:54:01 +0300 Subject: [PATCH] feat: [contracts] add statistics --- .../document-contract-list-view.repository.ts | 51 ++---------------- .../document-contract.repository.ts | 2 +- .../src/assets/locales/ro/translation.json | 9 ++-- .../enums/document-contract-status.enum.ts | 1 - .../src/components/ContractsStatistics.tsx | 53 +++++++++++-------- .../src/components/DocumentContractsTable.tsx | 10 ++-- frontend/src/components/StatisticsCard.tsx | 6 ++- .../document-contracts.api.ts | 28 ++-------- 8 files changed, 52 insertions(+), 108 deletions(-) diff --git a/backend/src/modules/documents/repositories/document-contract-list-view.repository.ts b/backend/src/modules/documents/repositories/document-contract-list-view.repository.ts index e398b419..a5556d2e 100644 --- a/backend/src/modules/documents/repositories/document-contract-list-view.repository.ts +++ b/backend/src/modules/documents/repositories/document-contract-list-view.repository.ts @@ -62,54 +62,9 @@ export class DocumentContractListViewRepository extends RepositoryWithPagination } if (status) { - switch (status) { - case DocumentContractComputedStatuses.ACTIVE: - query - .andWhere('documentContractListView.status = :status', { - status: 'APPROVED', - }) - .andWhere( - 'documentContractListView.documentStartDate <= :currentDate', - { - currentDate: new Date(), - }, - ) - .andWhere( - 'documentContractListView.documentEndDate >= :currentDate', - { - currentDate: new Date(), - }, - ); - break; - case DocumentContractComputedStatuses.NOT_STARTED: - query - .andWhere('documentContractListView.status = :status', { - status: 'APPROVED', - }) - .andWhere( - 'documentContractListView.documentStartDate > :currentDate', - { - currentDate: new Date(), - }, - ); - break; - case DocumentContractComputedStatuses.EXPIRED: - query - .andWhere('documentContractListView.status = :status', { - status: 'APPROVED', - }) - .andWhere( - 'documentContractListView.documentEndDate < :currentDate', - { - currentDate: new Date(), - }, - ); - break; - default: - query.andWhere('documentContractListView.status = :status', { - status, - }); - } + query.andWhere('documentContractListView.status = :status', { + status, + }); } if (documentStartDate && documentEndDate) { diff --git a/backend/src/modules/documents/repositories/document-contract.repository.ts b/backend/src/modules/documents/repositories/document-contract.repository.ts index 2bf62d11..b9f62963 100644 --- a/backend/src/modules/documents/repositories/document-contract.repository.ts +++ b/backend/src/modules/documents/repositories/document-contract.repository.ts @@ -142,7 +142,7 @@ export class DocumentContractRepositoryService extends RepositoryWithPagination< COUNT(*) FILTER (WHERE status = $1) AS pending_ngo_representative_signature, COUNT(*) FILTER (WHERE status = $2) AS pending_volunteer_signature, COUNT(*) FILTER (WHERE CURRENT_DATE BETWEEN document_start_date AND document_end_date) AS active_contracts, - COUNT(*) FILTER (WHERE document_end_date - CURRENT_DATE <= 14) AS soon_to_expire + COUNT(*) FILTER (WHERE document_end_date - CURRENT_DATE <= 30 AND document_end_date - CURRENT_DATE > 0) AS soon_to_expire FROM document_contract WHERE organization_id = $3 `, [ diff --git a/frontend/src/assets/locales/ro/translation.json b/frontend/src/assets/locales/ro/translation.json index 5f15887f..92cc60a7 100644 --- a/frontend/src/assets/locales/ro/translation.json +++ b/frontend/src/assets/locales/ro/translation.json @@ -1019,9 +1019,10 @@ }, "statistics": { "active_contracts": "Contracte active", - "in_signing_contracts": "Contracte în curs de semnare", - "saved_contracts": "Contracte salvate (netrimise)", - "to_expire_soon": "Contracte care expiră curând" + "in_signing_contracts": "Contracte în curs de semnare la voluntari", + "saved_contracts": "Contracte verificate care necesită semnatură", + "to_expire_soon": "Contracte care expiră curând", + "view_list": "Vezi lista" }, "table_header": { "title": "Template-uri de contracte", @@ -1320,4 +1321,4 @@ "clear": "Șterge", "apply_all": "Aplică pentru toate" } -} \ No newline at end of file +} diff --git a/frontend/src/common/enums/document-contract-status.enum.ts b/frontend/src/common/enums/document-contract-status.enum.ts index 7c9cff7c..824d6d87 100644 --- a/frontend/src/common/enums/document-contract-status.enum.ts +++ b/frontend/src/common/enums/document-contract-status.enum.ts @@ -16,7 +16,6 @@ export enum DocumentContractStatusForFilter { PENDING_VOLUNTEER_SIGNATURE = 'PENDING_VOLUNTEER_SIGNATURE', PENDING_APPROVAL_NGO = 'PENDING_APPROVAL_NGO', PENDING_NGO_REPRESENTATIVE_SIGNATURE = 'PENDING_NGO_REPRESENTATIVE_SIGNATURE', - APPROVED = 'APPROVED', REJECTED_VOLUNTEER = 'REJECTED_VOLUNTEER', REJECTED_NGO = 'REJECTED_NGO', ACTION_EXPIRED = 'ACTION_EXPIRED', diff --git a/frontend/src/components/ContractsStatistics.tsx b/frontend/src/components/ContractsStatistics.tsx index 7e930e2a..96daa1d8 100644 --- a/frontend/src/components/ContractsStatistics.tsx +++ b/frontend/src/components/ContractsStatistics.tsx @@ -1,51 +1,58 @@ import React from 'react'; import StatisticsCard from './StatisticsCard'; import { useTranslation } from 'react-i18next'; +import { IDocumentContractsStatistics } from '../common/interfaces/document-contract.interface'; +import { UpdateType } from '../common/interfaces/hoc-query-props.interface'; +import { DocumentContractsTableQueryProps } from './DocumentContractsTable'; +import { DocumentContractStatusForFilter } from '../common/enums/document-contract-status.enum'; -export const ContractsStatistics = () => { +interface ContractsStatisticsProps { + statistics: IDocumentContractsStatistics; + isLoading: boolean; + setQuery: (changes: DocumentContractsTableQueryProps, updateType?: UpdateType) => void; +} + +export const ContractsStatistics = ({ statistics, isLoading, setQuery }: ContractsStatisticsProps) => { const { t } = useTranslation('volunteering_contracts'); return (
{}, + label: t('statistics.view_list'), + onClick: () => { setQuery({ status: DocumentContractStatusForFilter.PENDING_NGO_REPRESENTATIVE_SIGNATURE }, 'push') }, }} + isLoading={isLoading} /> {}, + label: t('statistics.view_list'), + onClick: () => { setQuery({ status: DocumentContractStatusForFilter.PENDING_VOLUNTEER_SIGNATURE }, 'push') }, }} + isLoading={isLoading} /> + {}, + label: t('statistics.view_list'), + onClick: () => { setQuery({ status: DocumentContractStatusForFilter.ACTIVE }, 'push') }, }} + isLoading={isLoading} /> {}, + label: t('statistics.view_list'), + onClick: () => { setQuery({ endDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), status: DocumentContractStatusForFilter.ACTIVE }, 'push') }, }} + isLoading={isLoading} />
); diff --git a/frontend/src/components/DocumentContractsTable.tsx b/frontend/src/components/DocumentContractsTable.tsx index c467b08c..3f8f740c 100644 --- a/frontend/src/components/DocumentContractsTable.tsx +++ b/frontend/src/components/DocumentContractsTable.tsx @@ -28,6 +28,7 @@ import { useTranslation } from 'react-i18next'; import { getContractsForDownload } from '../services/contracts/contracts.api'; import { useDeleteDocumentContractMutation, + useGetContractsStatisticsQuery, useGetDocumentsContractsQuery, } from '../services/document-contracts/document-contracts.service'; import { @@ -35,7 +36,7 @@ import { } from '../common/enums/document-contract-status.enum'; import { IPaginationQueryParams } from '../common/constants/pagination'; -import { IDocumentContract } from '../common/interfaces/document-contract.interface'; +import { IDocumentContract, IDocumentContractsStatistics } from '../common/interfaces/document-contract.interface'; import DocumentsContractSidePanel from './DocumentsContractSidePanel'; import VolunteerSelect from '../containers/VolunteerSelect'; import { ListItem } from '../common/interfaces/list-item.interface'; @@ -107,7 +108,7 @@ const ContractsTableHeader = [ }, ]; -interface DocumentContractsTableQueryProps extends IPaginationQueryParams { +export interface DocumentContractsTableQueryProps extends IPaginationQueryParams { volunteerId?: string; volunteerName?: string; search?: string; @@ -149,6 +150,8 @@ const DocumentContractsTable = ({ query, setQuery }: DocumentContractsTableBasic ...(query.endDate ? { documentEndDate: formatDate(query?.endDate as Date, 'yyyy-MM-dd') } : {}), }); + const { data: statistics, isLoading: isLoadingStatistics } = useGetContractsStatisticsQuery(); + const { mutate: deleteContract } = useDeleteDocumentContractMutation(); const onView = (row: IDocumentContract) => { @@ -223,7 +226,6 @@ const DocumentContractsTable = ({ query, setQuery }: DocumentContractsTableBasic const mapContractStatusToPopoverItems = (status: DocumentContractStatusForFilter) => { switch (status) { - case DocumentContractStatusForFilter.APPROVED: case DocumentContractStatusForFilter.SCHEDULED: case DocumentContractStatusForFilter.CREATED: case DocumentContractStatusForFilter.PENDING_VOLUNTEER_SIGNATURE: @@ -321,7 +323,7 @@ const DocumentContractsTable = ({ query, setQuery }: DocumentContractsTableBasic return ( <> - + { +const StatisticsCard = ({ label, value, action, icon, info, className, isLoading }: StatisticsCardProps) => { return (
@@ -20,7 +22,7 @@ const StatisticsCard = ({ label, value, action, icon, info, className }: Statist

{label}

-

{value}

+ {isLoading ? :

{value}

} {info && ( {info} diff --git a/frontend/src/services/document-contracts/document-contracts.api.ts b/frontend/src/services/document-contracts/document-contracts.api.ts index 68f6479f..ea5aa03b 100644 --- a/frontend/src/services/document-contracts/document-contracts.api.ts +++ b/frontend/src/services/document-contracts/document-contracts.api.ts @@ -1,4 +1,3 @@ -import { DocumentContractStatusForFilter } from '../../common/enums/document-contract-status.enum'; import { IAddDocumentContractDTO, IDocumentContract, @@ -14,30 +13,9 @@ import API from '../api'; export const getDocumentsContracts = async ( params: IGetDocumentsContractsParams, ): Promise> => { - const { data: contracts } = await API.get>( - '/documents/contracts', - { - params, - }, - ); - - // These enum values (ACTIVE, EXPIRED, NOT_STARTED) are not stored in the database - // They are derived statuses based on the contract's start and end dates - // We need to manually update the status here to reflect these derived states - const statusesToUpdate = [ - DocumentContractStatusForFilter.ACTIVE, - DocumentContractStatusForFilter.EXPIRED, - DocumentContractStatusForFilter.NOT_STARTED, - ]; - - if (params.status && statusesToUpdate.includes(params.status)) { - contracts.items = contracts.items.map((item: IDocumentContract) => ({ - ...item, - status: params.status as DocumentContractStatusForFilter, - })); - } - - return contracts; + return API.get>('/documents/contracts', { + params, + }).then((res) => res.data); }; export const getDocumentContract = (id: string): Promise => {