Skip to content

Commit

Permalink
PM: Supporting documents (#4260)
Browse files Browse the repository at this point in the history
* add new model

* add new urls

* add test & migrations

* imports

* tests & fixes

* add modal for supporting documents

* add delete, fix doc layout

* add supporting docs to follow up pages

* add title validation

* textfield size small

* regenerate schema

* add error for > 10 files, accept other filetypes

* add message when deleting doc

* display both title and file name

* upd e2e test_smoke_details_payment_plan

* review

* fix urls in tests

* migrations

* fix download resp

* fix

* add get_id

* supporting docs fixes

* mypy

* fixes

---------

Co-authored-by: Maciej Szewczyk <[email protected]>
  • Loading branch information
pavlo-mk and Maciej Szewczyk authored Oct 3, 2024
1 parent bbf6217 commit 50a2d06
Show file tree
Hide file tree
Showing 34 changed files with 1,067 additions and 32 deletions.
21 changes: 21 additions & 0 deletions src/frontend/data/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3370,6 +3370,7 @@ type PaymentPlanNode implements Node {
deliveryMechanisms: [DeliveryMechanismPerPaymentPlanNode]
paymentItems(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection!
approvalProcess(offset: Int, before: String, after: String, first: Int, last: Int): ApprovalProcessNodeConnection!
documents(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanSupportingDocumentNodeConnection!
adminUrl: String
currencyName: String
hasPaymentListExportFile: Boolean
Expand All @@ -3392,6 +3393,7 @@ type PaymentPlanNode implements Node {
unsuccessfulPaymentsCount: Int
canSendToPaymentGateway: Boolean
canSplit: Boolean
supportingDocuments: [PaymentPlanSupportingDocumentNode]
}

type PaymentPlanNodeConnection {
Expand All @@ -3418,6 +3420,25 @@ enum PaymentPlanStatus {
FINISHED
}

type PaymentPlanSupportingDocumentNode implements Node {
id: ID!
paymentPlan: PaymentPlanNode!
title: String!
file: String!
uploadedAt: DateTime!
createdBy: UserNode
}

type PaymentPlanSupportingDocumentNodeConnection {
pageInfo: PageInfo!
edges: [PaymentPlanSupportingDocumentNodeEdge]!
}

type PaymentPlanSupportingDocumentNodeEdge {
node: PaymentPlanSupportingDocumentNode
cursor: String!
}

type PaymentRecordAndPaymentNode {
objType: String
id: String
Expand Down
77 changes: 74 additions & 3 deletions src/frontend/src/__generated__/graphql.tsx

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/frontend/src/__generated__/introspection-result.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 51 additions & 0 deletions src/frontend/src/api/paymentModuleApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,54 @@ export const bulkActionPaymentPlansManagerial = async (
);
return response.data;
};

export const deleteSupportingDocument = async (
businessArea: string,
programId: string,
paymentPlanId: string,
fileId: string,
) => {
try {
await api.delete(
`${businessArea}/programs/${programId}/payment-plans/${paymentPlanId}/supporting-documents/${fileId}/`,
);
return { success: true };
} catch (error: any) {
const errorMessage = error?.message || 'An unknown error occurred';
throw new Error(`Failed to delete supporting document: ${errorMessage}`);
}
};

export const uploadSupportingDocument = async (
businessArea: string,
programId: string,
paymentPlanId: string,
file: File,
title: string,
) => {
const formData = new FormData();
formData.append('file', file);
formData.append('title', title);

try {
const response = await api.post(
`${businessArea}/programs/${programId}/payment-plans/${paymentPlanId}/supporting-documents/`,
formData,
);
return response.data; // Return the response data
} catch (error) {
throw new Error(`Failed to upload supporting document: ${error.message}`);
}
};

export const fetchSupportingDocument = async (
businessAreaSlug: string,
programId: string,
paymentPlanId: string,
fileId: string,
): Promise<any> => {
const response = await api.get(
`${businessAreaSlug}/programs/${programId}/payment-plans/${paymentPlanId}/supporting-documents/${fileId}/download/`,
);
return response;
};
5 changes: 5 additions & 0 deletions src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ export const PAYMENT_PLAN_QUERY = gql`
unicefId
}
unsuccessfulPaymentsCount
supportingDocuments {
id
title
file
}
}
}
`;
30 changes: 18 additions & 12 deletions src/frontend/src/components/core/DropzoneField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ interface DropzoneContainerProps {
disabled: boolean;
}

interface DropzoneFieldProps {
onChange: (acceptedFiles: File[]) => void;
loading: boolean;
dontShowFilename: boolean;
accepts?: { [key: string]: string[] };
}

const DropzoneContainer = styled.div<DropzoneContainerProps>`
width: 500px;
height: 100px;
Expand All @@ -32,36 +39,35 @@ export const DropzoneField = ({
onChange,
loading,
dontShowFilename,
}: {
onChange: (acceptedFiles: File[]) => void;
loading: boolean;
dontShowFilename: boolean;
}): React.ReactElement => {
accepts = {
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': [
'.xlsx',
],
},
}: DropzoneFieldProps): React.ReactElement => {
const { t } = useTranslation();
const onDrop = useCallback((acceptedFiles: File[]) => {
onChange(acceptedFiles);
}, []);

const { getRootProps, getInputProps, acceptedFiles } = useDropzone({
disabled: loading,
accept: {
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': [
'.xlsx',
],
},
accept: accepts,
onDrop,
});

const acceptedFilename =
acceptedFiles.length > 0 ? acceptedFiles[0].name : null;

return (
<Box display="flex" justifyContent="center" p={5}>
<DropzoneContainer {...getRootProps()} disabled={loading}>
<LoadingComponent isLoading={loading} absolute />
<input
{...getInputProps()}
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
accept={Object.values(accepts).flat().join(',')}
data-cy="file-input"
/>{' '}
/>
{dontShowFilename || !acceptedFilename
? t('UPLOAD FILE')
: acceptedFilename}
Expand Down
Loading

0 comments on commit 50a2d06

Please sign in to comment.