Doing
This commit is contained in:
parent
62b51b4047
commit
5f7a4c2e63
759
package-lock.json
generated
759
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,7 @@
|
||||
"form-data": "^4.0.0",
|
||||
"heroicons": "^2.1.5",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.151",
|
||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.160",
|
||||
"next": "^14.2.3",
|
||||
"prettier": "^2.8.7",
|
||||
"react": "18.2.0",
|
||||
|
@ -87,4 +87,14 @@ export default class Customers extends BaseNotary {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async sendReminder(uid: string, documentsUid: string[]): Promise<void> {
|
||||
const url = new URL(this.baseURl.concat(`/${uid}/send_reminder`));
|
||||
try {
|
||||
await this.postRequest<void>(url, { documentsUid });
|
||||
} catch (err) {
|
||||
this.onError(err);
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
import { DocumentReminder } from "le-coffre-resources/dist/Notary";
|
||||
|
||||
import BaseNotary from "../BaseNotary";
|
||||
|
||||
// TODO Type get query params -> Where + inclue + orderby
|
||||
export interface IGetDocumentRemindersparams {
|
||||
where?: {};
|
||||
include?: {};
|
||||
orderBy?: {};
|
||||
}
|
||||
|
||||
// TODO Type getbyuid query params
|
||||
|
||||
export default class DocumentReminders extends BaseNotary {
|
||||
private static instance: DocumentReminders;
|
||||
private readonly baseURl = this.namespaceUrl.concat("/document_reminders");
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!this.instance) {
|
||||
return new this();
|
||||
} else {
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
public async get(q: IGetDocumentRemindersparams): Promise<DocumentReminder[]> {
|
||||
const url = new URL(this.baseURl);
|
||||
const query = { q };
|
||||
Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
|
||||
try {
|
||||
return await this.getRequest<DocumentReminder[]>(url);
|
||||
} catch (err) {
|
||||
this.onError(err);
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import { DocumentNotary } from "le-coffre-resources/dist/Notary";
|
||||
import BaseNotary from "../BaseNotary";
|
||||
|
||||
// TODO Type get query params -> Where + inclue + orderby
|
||||
export interface IGetDocumentNotaryparams {
|
||||
where?: {};
|
||||
include?: {};
|
||||
orderBy?: {};
|
||||
}
|
||||
|
||||
// TODO Type getbyuid query params
|
||||
|
||||
export default class DocumentsNotary extends BaseNotary {
|
||||
private static instance: DocumentsNotary;
|
||||
private readonly baseURl = this.namespaceUrl.concat("/documents_notary");
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!this.instance) {
|
||||
return new this();
|
||||
} else {
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
// public async get(q: IGetDocumentNotaryparams): Promise<DocumentNotary[]> {
|
||||
// const url = new URL(this.baseURl);
|
||||
// const query = { q };
|
||||
// Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
|
||||
// try {
|
||||
// return await this.getRequest<DocumentNotary[]>(url);
|
||||
// } catch (err) {
|
||||
// this.onError(err);
|
||||
// return Promise.reject(err);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* @description : Create a Document Notary
|
||||
*/
|
||||
public async post(body: any): Promise<DocumentNotary> {
|
||||
const url = new URL(this.baseURl);
|
||||
try {
|
||||
return await this.postRequestFormData<DocumentNotary>(url, body);
|
||||
} catch (err) {
|
||||
this.onError(err);
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,8 @@ type IProps = {
|
||||
defaultFiles?: IDocumentFileWithUid[];
|
||||
onDelete?: (fileUid: string) => Promise<any>;
|
||||
onAddFile?: (file: File) => Promise<any>;
|
||||
onAddToList?: (file: IDocumentFile) => void;
|
||||
onRemoveFromList?: (file: IDocumentFile) => void;
|
||||
} & (
|
||||
| { onDelete: (fileUid: string) => Promise<any>; onAddFile?: never; defaultFiles: IDocumentFileWithUid[] }
|
||||
| { onDelete?: never; onAddFile: (file: File) => Promise<any>; defaultFiles: IDocumentFileWithUid[] }
|
||||
@ -68,7 +70,7 @@ export type IDocumentFileWithUid = IDocumentFileBase & {
|
||||
type IDocumentFile = IDocumentFileBase | IDocumentFileWithUid;
|
||||
|
||||
export default function DragAndDrop(props: IProps) {
|
||||
const { title, description, defaultFiles, onDelete, onAddFile } = props;
|
||||
const { title, description, defaultFiles, onDelete, onAddFile, onAddToList, onRemoveFromList } = props;
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [documentFiles, setDocumentFiles] = useState<IDocumentFile[]>([]);
|
||||
@ -95,6 +97,9 @@ export default function DragAndDrop(props: IProps) {
|
||||
}
|
||||
|
||||
return setTimeout(async () => {
|
||||
if (onAddToList) {
|
||||
onAddToList(newDoc);
|
||||
}
|
||||
setDocumentFiles((prevDocs) => prevDocs.map((doc) => (doc.id === newDoc.id ? newDoc : doc)));
|
||||
}, 1000);
|
||||
} catch (error: any) {
|
||||
@ -117,6 +122,9 @@ export default function DragAndDrop(props: IProps) {
|
||||
|
||||
const handleRemove = useCallback(
|
||||
(documentFile: IDocumentFile) => {
|
||||
if (onRemoveFromList) {
|
||||
onRemoveFromList(documentFile);
|
||||
}
|
||||
const loadingDoc = { ...documentFile, isLoading: true };
|
||||
setDocumentFiles((prevDocs) => prevDocs.map((doc) => (doc.id === documentFile.id ? loadingDoc : doc)));
|
||||
|
||||
|
@ -7,6 +7,7 @@ import Customer from "le-coffre-resources/dist/Customer";
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import Customers from "@Front/Api/LeCoffreApi/Notary/Customers/Customers";
|
||||
|
||||
type IProps = {
|
||||
isOpen: boolean;
|
||||
@ -22,6 +23,7 @@ export default function ReminderModal(props: IProps) {
|
||||
|
||||
const onRemind = useCallback(() => {
|
||||
console.log("selectedOptions", selectedOptions);
|
||||
Customers.getInstance().sendReminder(customer.uid!, selectedOptions.map((option) => option.value) as string[]);
|
||||
onRemindSuccess();
|
||||
onClose?.();
|
||||
}, [onClose, onRemindSuccess, selectedOptions]);
|
||||
@ -31,7 +33,7 @@ export default function ReminderModal(props: IProps) {
|
||||
customer.documents?.map((document) => {
|
||||
return {
|
||||
label: document.document_type?.name ?? "",
|
||||
value: document.document_type?.uid ?? "",
|
||||
value: document.uid ?? "",
|
||||
};
|
||||
}) ?? [],
|
||||
[customer],
|
||||
|
@ -10,6 +10,9 @@ import ReminderModal from "./ReminderModal";
|
||||
import { useRouter } from "next/router";
|
||||
import Module from "@Front/Config/Module";
|
||||
import Link from "next/link";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import DocumentReminders from "@Front/Api/LeCoffreApi/Notary/DocumentReminders/DocumentReminders";
|
||||
import { DocumentReminder } from "le-coffre-resources/dist/Notary";
|
||||
|
||||
type IProps = {
|
||||
customer: Customer;
|
||||
@ -19,9 +22,25 @@ type IProps = {
|
||||
|
||||
export default function EmailReminder(props: IProps) {
|
||||
const { customer, doesCustomerHaveDocument, isAnchored } = props;
|
||||
const [reminders, setReminders] = useState<DocumentReminder[] | null>(null);
|
||||
const { isOpen, open, close } = useOpenable();
|
||||
const router = useRouter();
|
||||
|
||||
const fetchReminders = useCallback(async () => {
|
||||
DocumentReminders.getInstance()
|
||||
.get({
|
||||
where: { document: { depositor: { uid: customer.uid } } },
|
||||
include: { document: "true" },
|
||||
orderBy: { reminder_date: "desc" },
|
||||
})
|
||||
.then((reminders) => setReminders(reminders))
|
||||
.catch((e) => console.warn(e));
|
||||
}, [customer.uid]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchReminders();
|
||||
}, []);
|
||||
|
||||
let { folderUid } = router.query;
|
||||
|
||||
return (
|
||||
@ -47,16 +66,38 @@ export default function EmailReminder(props: IProps) {
|
||||
</Link>
|
||||
<div className={classes["info"]}>
|
||||
{/* TODO: mettre la date de la dernière relance */}
|
||||
<Typography typo={ETypo.TEXT_XS_REGULAR} color={ETypoColor.TEXT_SECONDARY}>
|
||||
Dernière relance: -
|
||||
</Typography>
|
||||
{!reminders && (
|
||||
<Typography typo={ETypo.TEXT_XS_REGULAR} color={ETypoColor.TEXT_SECONDARY}>
|
||||
Dernière relance: -
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{reminders && reminders.length > 0 && (
|
||||
<Typography typo={ETypo.TEXT_XS_REGULAR} color={ETypoColor.TEXT_SECONDARY}>
|
||||
Dernière relance: {new Date(reminders[0]!.reminder_date!).toLocaleDateString()}
|
||||
</Typography>
|
||||
)}
|
||||
{/* TODO: mettre le nombre de relance */}
|
||||
<Typography typo={ETypo.TEXT_XS_REGULAR} color={ETypoColor.TEXT_SECONDARY}>
|
||||
Nombre de relance: -
|
||||
</Typography>
|
||||
{!reminders && (
|
||||
<Typography typo={ETypo.TEXT_XS_REGULAR} color={ETypoColor.TEXT_SECONDARY}>
|
||||
Nombre de relance: -
|
||||
</Typography>
|
||||
)}
|
||||
{reminders && reminders.length > 0 && (
|
||||
<Typography typo={ETypo.TEXT_XS_REGULAR} color={ETypoColor.TEXT_SECONDARY}>
|
||||
Nombre de relance: {reminders.length}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<ReminderModal isOpen={isOpen} onRemindSuccess={() => {}} onClose={close} customer={customer} />
|
||||
<ReminderModal
|
||||
isOpen={isOpen}
|
||||
onRemindSuccess={() => {
|
||||
window.location.reload();
|
||||
}}
|
||||
onClose={close}
|
||||
customer={customer}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -15,17 +15,34 @@ import { useRouter } from "next/router";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import DocumentsNotary from "@Front/Api/LeCoffreApi/Notary/DocumentsNotary/DocumentsNotary";
|
||||
|
||||
enum EClientSelection {
|
||||
ALL_CLIENTS = "all_clients",
|
||||
SELECTED_CLIENTS = "selected_clients",
|
||||
}
|
||||
|
||||
type IDocumentFileBase = {
|
||||
id: string;
|
||||
file: File | null;
|
||||
uid?: string;
|
||||
isLoading?: boolean;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export type IDocumentFileWithUid = IDocumentFileBase & {
|
||||
uid: string;
|
||||
};
|
||||
|
||||
type IDocumentFile = IDocumentFileBase | IDocumentFileWithUid;
|
||||
|
||||
export default function SendDocuments() {
|
||||
const router = useRouter();
|
||||
let { folderUid } = router.query;
|
||||
const [folder, setFolder] = useState<OfficeFolder | null>(null);
|
||||
const [clientSelection, setClientSelection] = useState<EClientSelection | null>(null);
|
||||
const [clientSelection, setClientSelection] = useState<EClientSelection | null>(EClientSelection.SELECTED_CLIENTS);
|
||||
const [files, setFiles] = useState<IDocumentFile[]>([]);
|
||||
const [selectedClients, setSelectedClients] = useState<string[]>([]);
|
||||
|
||||
const onFormSubmit = useCallback(
|
||||
async (
|
||||
@ -34,10 +51,53 @@ export default function SendDocuments() {
|
||||
[key: string]: any;
|
||||
},
|
||||
//TODO: when back is done
|
||||
) => {},
|
||||
[],
|
||||
) => {
|
||||
if (folder?.customers && clientSelection === EClientSelection.ALL_CLIENTS) {
|
||||
const allClientIds = folder.customers.map((customer) => customer.uid ?? "");
|
||||
setSelectedClients(allClientIds);
|
||||
}
|
||||
const formData = new FormData();
|
||||
|
||||
selectedClients.forEach(async (customer) => {
|
||||
console.log(files[0]?.file);
|
||||
|
||||
if (!files[0]?.file) return;
|
||||
formData.append("customerUid", customer as string);
|
||||
formData.append("folderUid", folderUid as string);
|
||||
formData.append("file", files[0].file as File);
|
||||
const documentNotary = await DocumentsNotary.getInstance().post(formData);
|
||||
console.log(documentNotary);
|
||||
});
|
||||
// const formData = new FormData();
|
||||
// files.forEach((file) => {
|
||||
// if (file.file) {
|
||||
// formData.append("file", file.file);
|
||||
// }
|
||||
// });
|
||||
// selectedClients.forEach((client) => {
|
||||
// formData.append("customers", client);
|
||||
// });
|
||||
// formData.append("folder", folderUid as string);
|
||||
// const documentNotary = await DocumentsNotary.getInstance().post(formData);
|
||||
// console.log(documentNotary);
|
||||
},
|
||||
[files, clientSelection, selectedClients],
|
||||
);
|
||||
|
||||
const onAddToList = useCallback((documentFile: IDocumentFile) => {
|
||||
const test = files;
|
||||
test.push(documentFile);
|
||||
setFiles(test);
|
||||
}, []);
|
||||
|
||||
const onRemoveFromList = useCallback((documentFile: IDocumentFile) => {
|
||||
const test = files;
|
||||
const index = test.findIndex((doc) => doc.id === documentFile.id);
|
||||
if (index === -1) return;
|
||||
test.splice(index, 1);
|
||||
setFiles(test);
|
||||
}, []);
|
||||
|
||||
const fetchFolder = useCallback(async () => {
|
||||
Folders.getInstance()
|
||||
.getByUid(folderUid as string, {
|
||||
@ -53,14 +113,34 @@ export default function SendDocuments() {
|
||||
.catch((e) => console.warn(e));
|
||||
}, [folderUid]);
|
||||
|
||||
const onClientSelectionChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setClientSelection(e.target.value as EClientSelection);
|
||||
console.log(e.target.value);
|
||||
const onClientSelectionChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const selection = e.target.value as EClientSelection;
|
||||
setClientSelection(selection);
|
||||
|
||||
if (selection === EClientSelection.ALL_CLIENTS && folder?.customers) {
|
||||
// Automatically select all customers
|
||||
const allClientIds = folder.customers.map((customer) => customer.uid ?? "");
|
||||
setSelectedClients(allClientIds);
|
||||
} else {
|
||||
// Clear selected clients when selecting "Sélectionner certains clients"
|
||||
setSelectedClients([]);
|
||||
}
|
||||
},
|
||||
[folder],
|
||||
);
|
||||
|
||||
const handleClientSelectionChange = useCallback((selectedOptions: any) => {
|
||||
setSelectedClients(selectedOptions.map((option: any) => option.id));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchFolder();
|
||||
}, [fetchFolder]);
|
||||
if (folder?.customers && clientSelection === EClientSelection.ALL_CLIENTS) {
|
||||
const allClientIds = folder.customers.map((customer) => customer.uid ?? "");
|
||||
setSelectedClients(allClientIds);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const backUrl = useMemo(
|
||||
() =>
|
||||
@ -100,16 +180,26 @@ export default function SendDocuments() {
|
||||
value={EClientSelection.SELECTED_CLIENTS}
|
||||
label="Sélectionner certains clients"
|
||||
onChange={onClientSelectionChange}
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Form onSubmit={onFormSubmit} className={classes["form"]}>
|
||||
{clientSelection === EClientSelection.SELECTED_CLIENTS && (
|
||||
<AutocompleteMultiSelect label="Choisir le ou les clients: " options={clientsOptions} />
|
||||
<AutocompleteMultiSelect
|
||||
label="Choisir le ou les clients: "
|
||||
options={clientsOptions}
|
||||
onSelectionChange={handleClientSelectionChange}
|
||||
/>
|
||||
)}
|
||||
{clientSelection && (
|
||||
<>
|
||||
<DragAndDrop title="Glisser ou déposer ou" description="Formats acceptés : PDF, JPG Taille maximale : 5 Mo" />
|
||||
<DragAndDrop
|
||||
title="Glisser ou déposer ou"
|
||||
description="Formats acceptés : PDF, JPG Taille maximale : 5 Mo"
|
||||
onAddToList={onAddToList}
|
||||
onRemoveFromList={onRemoveFromList}
|
||||
/>
|
||||
|
||||
<div className={classes["buttons-container"]}>
|
||||
<a href={backUrl}>
|
||||
|
@ -80,7 +80,8 @@
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"isolatedModules": true
|
||||
"isolatedModules": true,
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
|
Loading…
x
Reference in New Issue
Block a user