Skip to content

Commit

Permalink
Merge branch 'feature/contracts' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
radulescuandrew committed Oct 2, 2024
2 parents 7ecc5f3 + f10c124 commit 8e2c4c7
Show file tree
Hide file tree
Showing 18 changed files with 142 additions and 173 deletions.
11 changes: 9 additions & 2 deletions backend/src/api/documents/dto/create-document-contract.dto.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { UTCDate } from '@date-fns/utc';
import { Transform } from 'class-transformer';
import { IsDate, IsString, MaxLength, MinDate } from 'class-validator';
import {
IsDate,
IsString,
MaxLength,
MinDate,
MinLength,
} from 'class-validator';
import { IsDateGreaterThanOrEqualTo } from 'src/common/validators/is-date-gte.validator';

export class CreateDocumentContractDto {
@IsString()
@MaxLength(9)
@MinLength(1)
@MaxLength(10)
documentNumber: string;

@IsDate()
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/assets/locales/ro/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,8 @@
"contract_card_form": {
"document_number": {
"required": "Numărul contractului este obligatoriu",
"max_length": "Numărul contractului trebuie să aibă maxim 10 caractere",
"min_length": "Numărul contractului trebuie să aibă minim 1 caracter",
"unique": "Numărul contractului trebuie să fie unic",
"invalid": "Numărul contractului trebuie să fie un număr întreg pozitiv"
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/AutoFillContractCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const AutoFillContractCard = ({ onSubmit }: AutoFillContractCardProps) =>
<div className="bg-white rounded shadow flex flex-col p-4 gap-4">
<p className="font-robotoBold">{t('title')}</p>
<p className="text-cool-gray-700">{t('description')}</p>
<div className="flex flex-col md:flex-row gap-6">
<div className="flex flex-col md:flex-row items-start gap-6 sm:h-24">
<Controller
name="startingNumber"
control={control}
Expand Down
28 changes: 14 additions & 14 deletions frontend/src/components/ContractCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const dotsString = '.........................';
interface ContractCardProps {
volunteer: IVolunteer;
template: IDocumentTemplate;
initialNumber?: number;
initialNumber?: string;
initialDate?: Date | undefined;
initialPeriod?: [Date | undefined, Date | undefined];
isOpen?: boolean;
Expand All @@ -38,8 +38,9 @@ interface ContractCardProps {

export const fillCardValidationSchema = yup.object({
documentNumber: yup
.number()
.positive(`${i18n.t('doc_templates:contract_card_form.document_number.invalid')}`)
.string()
.min(1, `${i18n.t('doc_templates:contract_card_form.document_number.min_length')}`)
.max(10, `${i18n.t('doc_templates:contract_card_form.document_number.max_length')}`)
.typeError(`${i18n.t('doc_templates:contract_card_form.document_number.invalid')}`)
.required(`${i18n.t('doc_templates:contract_card_form.document_number:required')}`),
documentDate: yup
Expand Down Expand Up @@ -99,7 +100,7 @@ export const ContractCard = ({
// update values for the contract data, as well as for the contract preview, whenever the initial values coming from the parent change (the fast contract completion feature)
useEffect(() => {
// update contract number
setValue('documentNumber', initialNumber as number);
setValue('documentNumber', initialNumber as string);

// update contract date
setValue('documentDate', initialDate as Date);
Expand Down Expand Up @@ -161,7 +162,7 @@ export const ContractCard = ({
setError('documentPeriod', {});

// Reintilize with initial values Document Number
setValue('documentNumber', initialNumber as number);
setValue('documentNumber', initialNumber as string);

// Reintilize with initial values Document Date
setValue('documentDate', initialDate as Date);
Expand Down Expand Up @@ -230,12 +231,12 @@ export const ContractCard = ({
</ul>
{(currentError.response?.data as { statusCode?: number })?.statusCode ===
500 && (
<Button
label={t('organization.organization_data_form.retry_button')}
className="self-baseline"
onClick={handleRetrySendContract}
/>
)}
<Button
label={t('organization.organization_data_form.retry_button')}
className="self-baseline"
onClick={handleRetrySendContract}
/>
)}
</div>
</>
)}
Expand All @@ -254,7 +255,6 @@ export const ContractCard = ({
onChange={onChange}
placeholder={t('placeholders.contract_no')}
errorMessage={errors.documentNumber?.message}
type="number"
/>
)}
/>
Expand Down Expand Up @@ -288,8 +288,8 @@ export const ContractCard = ({
errorMessage={
errors?.documentPeriod
? errors.documentPeriod[0]?.message ||
errors.documentPeriod[1]?.message ||
errors.documentPeriod?.message
errors.documentPeriod[1]?.message ||
errors.documentPeriod?.message
: ''
}
/>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/ContractsStatistics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const ContractsStatistics = ({ statistics, isLoading, setQuery }: Contrac
return (
<div className="flex flex-col sm:flex-row gap-2">
<StatisticsCard
className="w-full"
label={t('statistics.saved_contracts')}
value={statistics?.pendingNgoRepresentativeSignature.toString()}
action={{
Expand All @@ -27,6 +28,7 @@ export const ContractsStatistics = ({ statistics, isLoading, setQuery }: Contrac
isLoading={isLoading}
/>
<StatisticsCard
className="w-full"
label={t('statistics.in_signing_contracts')}
value={statistics?.pendingVolunteerSignature.toString()}
action={{
Expand All @@ -37,6 +39,7 @@ export const ContractsStatistics = ({ statistics, isLoading, setQuery }: Contrac
/>

<StatisticsCard
className="w-full"
label={t('statistics.active_contracts')}
value={statistics?.activeContracts.toString()}
action={{
Expand All @@ -46,6 +49,7 @@ export const ContractsStatistics = ({ statistics, isLoading, setQuery }: Contrac
isLoading={isLoading}
/>
<StatisticsCard
className="w-full"
label={t('statistics.to_expire_soon')}
value={statistics?.soonToExpire.toString()}
action={{
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/DocumentContractFillCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const DocumentContractFillCards = ({
}: FieldValues) => {
volunteers.forEach((volunteer, index) => {
const volunteerData: IDocumentVolunteerData = {
documentNumber: startingNumber ? +startingNumber + index : 0,
documentNumber: startingNumber ? startingNumber + index : 0,
documentDate: documentContractDate ? documentContractDate : undefined,
documentPeriod: documentContractPeriod ? documentContractPeriod : undefined,
};
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/DocumentContractsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ const DocumentContractsTable = ({ query, setQuery }: DocumentContractsTableBasic

// todo: do we need shouldRefetch?
const onCloseSidePanel = () => {
queryClient.invalidateQueries({ queryKey: ['documents-contracts'] });
queryClient.invalidateQueries({ queryKey: ['contracts-statistics'] });
setIsViewContractSidePanelOpen(false);
setSelectedContract(undefined);
};
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/StatisticsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const StatisticsCard = ({ label, value, action, icon, info, className, isLoading
<div className="flex flex-col">
<div className="flex sm:gap-5 gap-2 sm:p-6 p-3 items-center">
{icon}
<div className="flex flex-col gap-2 shrink-[80]">
<div className="flex flex-col gap-2 shrink-[80] 3xl:h-full md:h-[6.25rem] h-[8.25rem] justify-between">
<p className="text-cool-gray-500 leading-5">{label}</p>
<div className="flex flex-wrap gap-2 items-end">
{isLoading ? <LoadingContent /> : <p className="text-4xl leading-8 font-semibold ">{value}</p>}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/GenerateContract.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { AxiosError } from 'axios';
import { format } from 'date-fns';

export interface IDocumentVolunteerData {
documentNumber: number;
documentNumber: string;
documentDate: Date;
documentPeriod: [Date, Date];
}
Expand Down
1 change: 1 addition & 0 deletions frontend/tailwind.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ module.exports = {
},
screens: {
xs: '400px',
'3xl': '1651px',
},
},
},
Expand Down
7 changes: 6 additions & 1 deletion mobile/src/assets/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
"required": "Phone is mandatory",
"pattern": "The telephone number must contain only numbers"
},
"birthday": {
"required": "The date of birth is required"
},
"password": {
"label": "Password",
"placeholder": "Enter your password",
Expand Down Expand Up @@ -809,12 +812,14 @@
"action_expired": "Action expired",
"rejected": "Rejected",
"current": "Current",
"not_started": "Not started",
"contract": {
"title": "Contract {{value}}",
"pending_volunteer_signature": "The contract has been generated and requires your approval. Download the template below and carefully read the document. Check if the identity data is correct, otherwise, modify the data and sign the document.",
"pending_ngo_signature": "The signed contract has been sent to the organization to obtain their signature as well. You will be notified immediately once the contract is fully signed. After that, you will be able to download the document with both signatures from the app.",
"signed_contract_footer": "Once signed, the contract cannot be modified. If you encounter problems, contact the organization to cancel the contract and generate a new one.",
"approved": "The contract has been approved by both parties and cannot be modified. Download and view the contract.",
"active": "The contract has been approved by both parties and cannot be modified. Download and view the contract.",
"not_started": "The contract has been signed by both parties and is about to begin.",
"expired": "The contract period has ended. Download and view the contract.",
"rejected": {
"ngo": "The contract has been rejected by the organization.",
Expand Down
9 changes: 7 additions & 2 deletions mobile/src/assets/locales/ro/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
"required": "Telefonul este obligatoriu",
"pattern": "Numărul de telefon trebuie să conțină doar numere"
},
"birthday": {
"required": "Data nașterii este obligatorie"
},
"password": {
"label": "Parola",
"placeholder": "Introdu parola",
Expand Down Expand Up @@ -809,13 +812,15 @@
"expired": "încheiat",
"rejected": "Respins",
"action_expired": "Acțiune expirată",
"current": "curent",
"current": "Curent",
"not_started": "Neînceput",
"contract": {
"title": "Contract {{value}}",
"pending_volunteer_signature": "Contractul a fost generat și necesită aprobarea ta. Descarcă modelul de mai jos și citește documentul cu atenție. Verifică dacă datele de identitate sunt corecte, în caz contrar, modifică datele și semnează documentul.",
"pending_ngo_signature": "Contractul semnat a fost trimis către organizație pentru a obține și semnătura lor. Vei fi notificat imediat ce contractul este complet semnat. După aceea, vei putea descărca documentul cu ambele semnături, din aplicație.",
"signed_contract_footer": "Odată semnat, contractul nu poate fi modificat. Dacă întâmpini probleme, ia legătura cu organizația, pentru a anula contractul și a genera un contract nou.",
"approved": "Contractul a fost aprobat de ambele părți și nu mai poate fi modificat. Descarcă și vizualizează contractul.",
"active": "Contractul a fost aprobat de ambele părți și nu mai poate fi modificat. Descarcă și vizualizează contractul.",
"not_started": "Contractul a fost semnat de ambele părți și urmează să înceapă.",
"expired": "Perioada contractului a luat sfârșit. Descarcă și vizualizează contractul.",
"rejected": {
"ngo": "Contractul a fost respins de către organizație.",
Expand Down
5 changes: 3 additions & 2 deletions mobile/src/common/enums/document-contract-status.enum.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
export enum DocumentContractStatus {
CREATED = 'CREATED', // Contract created by NGO, not signed by anyone yet
SCHEDULED = 'SCHEDULED', // Contract created by NGO, but scheduled to be sent to the volunteer
PENDING_VOLUNTEER_SIGNATURE = 'PENDING_VOLUNTEER_SIGNATURE', // Waiting for volunteer signature
PENDING_APPROVAL_NGO = 'PENDING_APPROVAL_NGO',
PENDING_NGO_REPRESENTATIVE_SIGNATURE = 'PENDING_NGO_REPRESENTATIVE_SIGNATURE',
APPROVED = 'APPROVED', // The volunteer and NGO have both signed the contract
REJECTED_VOLUNTEER = 'REJECTED_VOLUNTEER', // The volunteer rejected the contract
REJECTED_NGO = 'REJECTED_NGO', // The NGO rejected the contract
ACTION_EXPIRED = 'ACTION_EXPIRED', // The volunteer or NGO has not taken action for too long
ACTIVE = 'ACTIVE', // The contract is approved and active
EXPIRED = 'EXPIRED', // The contract is approved but expired
NOT_STARTED = 'NOT_STARTED', // The contract is approved but did not start yet
}
111 changes: 53 additions & 58 deletions mobile/src/common/utils/document-contracts.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,56 +88,47 @@ export const isIdentityDataIncomplete = (userProfile: IUserProfile) => {
return isMissingRequiredFields || isMissingGuardianFields;
};

export const mapContractToColor = (contract: IDocumentContract, t: any) => {
// rejected contract
if (
contract.status === DocumentContractStatus.REJECTED_NGO ||
contract.status === DocumentContractStatus.REJECTED_VOLUNTEER
) {
return {
color: 'red-500',
backgroundColor: 'red-50',
info: `${t('rejected')}`,
};
}
//action expired contract
if (contract.status === DocumentContractStatus.ACTION_EXPIRED) {
return {
color: 'color-danger-800',
backgroundColor: 'red-50',
info: `${t('action_expired')}`,
};
}
// expired contract
if (isAfter(new Date(), new Date(contract.documentEndDate))) {
return {
color: 'cool-gray-500',
backgroundColor: 'cool-gray-50',
info: `${t('expired')}`,
};
}
// pending
if (
contract.status === DocumentContractStatus.PENDING_APPROVAL_NGO ||
contract.status === DocumentContractStatus.PENDING_NGO_REPRESENTATIVE_SIGNATURE ||
contract.status === DocumentContractStatus.PENDING_VOLUNTEER_SIGNATURE
) {
return { color: 'yellow-500', backgroundColor: 'yellow-50', info: '' };
}
if (
isAfter(new Date(), new Date(contract.documentStartDate)) &&
isAfter(new Date(contract.documentEndDate), new Date())
) {
// active contract
return {
color: 'turquoise-500',
backgroundColor: 'turquoise-50',
info: `${t('current')}`,
};
}
export const mapContractToColor = (contract: IDocumentContract) => {
switch (contract.status) {
case DocumentContractStatus.REJECTED_NGO:
case DocumentContractStatus.REJECTED_VOLUNTEER:
return {
color: 'red-500',
backgroundColor: 'red-50',
info: `${i18n.t('documents-contract:rejected')}`,
};
case DocumentContractStatus.ACTION_EXPIRED:
return {
color: 'color-danger-800',
backgroundColor: 'red-50',
info: `${i18n.t('documents-contract:action_expired')}`,
};
case DocumentContractStatus.EXPIRED:
return {
color: 'cool-gray-500',
backgroundColor: 'cool-gray-50',
info: `${i18n.t('documents-contract:expired')}`,
};
case DocumentContractStatus.PENDING_APPROVAL_NGO:
case DocumentContractStatus.PENDING_NGO_REPRESENTATIVE_SIGNATURE:
case DocumentContractStatus.PENDING_VOLUNTEER_SIGNATURE:
return { color: 'yellow-500', backgroundColor: 'yellow-50', info: '' };
case DocumentContractStatus.ACTIVE:
return {
color: 'turquoise-500',
backgroundColor: 'turquoise-50',
info: `${i18n.t('documents-contract:current')}`,
};
case DocumentContractStatus.NOT_STARTED:
return {
color: 'green-500',
backgroundColor: 'green-50',
info: `${i18n.t('documents-contract:not_started')}`,
};

// default yellow
return { color: 'yellow-500', backgroundColor: 'yellow-50', info: '' };
default:
return { color: 'yellow-500', backgroundColor: 'yellow-50', info: '' };
}
};

export const mapContractRejectionReasonToText = (rejectionReason: RejectionReason) => {
Expand All @@ -153,24 +144,28 @@ export const mapContractRejectionReasonToText = (rejectionReason: RejectionReaso
}
};

export const renderContractInfoText = (contract: IDocumentContract, t: any) => {
export const renderContractInfoText = (contract: IDocumentContract) => {
switch (contract.status) {
case DocumentContractStatus.REJECTED_NGO:
return t('contract.rejected.ngo');
return i18n.t('documents-contract:contract.rejected.ngo');
case DocumentContractStatus.REJECTED_VOLUNTEER:
return t('contract.rejected.volunteer');
return i18n.t('documents-contract:contract.rejected.volunteer');
case DocumentContractStatus.ACTION_EXPIRED:
return t('contract.action_expired');
return i18n.t('documents-contract:contract.action_expired');
case DocumentContractStatus.PENDING_VOLUNTEER_SIGNATURE:
return t('contract.pending_volunteer_signature');
return i18n.t('documents-contract:contract.pending_volunteer_signature');
case DocumentContractStatus.PENDING_APPROVAL_NGO:
case DocumentContractStatus.PENDING_NGO_REPRESENTATIVE_SIGNATURE:
return t('contract.pending_ngo_signature');
case DocumentContractStatus.APPROVED:
return t('contract.approved');
return i18n.t('documents-contract:contract.pending_ngo_signature');
case DocumentContractStatus.ACTIVE:
return i18n.t('documents-contract:contract.active');
case DocumentContractStatus.EXPIRED:
return i18n.t('documents-contract:contract.expired');
case DocumentContractStatus.NOT_STARTED:
return i18n.t('documents-contract:contract.not_started');
default:
if (isAfter(new Date(), new Date(contract.documentEndDate))) {
return t('contract.expired');
return i18n.t('documents-contract:contract.expired');
}
return '';
}
Expand Down
Loading

0 comments on commit 8e2c4c7

Please sign in to comment.