🎨 client dashboard
This commit is contained in:
parent
4936540456
commit
b85a0b9b02
@ -15,6 +15,7 @@
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-primary);
|
||||
|
||||
svg {
|
||||
width: 18px;
|
||||
|
@ -1,11 +1,16 @@
|
||||
.root {
|
||||
padding: 24px;
|
||||
background-color: white;
|
||||
background-color: var(--white);
|
||||
border: 1px dashed #e7e7e7;
|
||||
|
||||
height: fit-content;
|
||||
|
||||
&[data-drag-over="true"] {
|
||||
border: 1px dashed grey;
|
||||
border: 1px dashed var(--grey);
|
||||
}
|
||||
|
||||
&.validated {
|
||||
border: 1px dashed var(--green-flash);
|
||||
}
|
||||
|
||||
.top-container {
|
||||
@ -25,6 +30,12 @@
|
||||
.right {
|
||||
margin-left: 18px;
|
||||
|
||||
.refused-button {
|
||||
font-size: 14px;
|
||||
color: var(--re-hover);
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -50,7 +61,7 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cross{
|
||||
.cross {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@ -58,6 +69,7 @@
|
||||
|
||||
.bottom-container {
|
||||
margin-top: 16px;
|
||||
|
||||
.add-button {
|
||||
.add-document {
|
||||
display: flex;
|
||||
@ -66,4 +78,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
@ -9,8 +9,12 @@ import Button, { EButtonVariant } from "../Button";
|
||||
import Tooltip from "../ToolTip";
|
||||
import Typography, { ITypo, ITypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
import { Document, File as FileCustomer } from "le-coffre-resources/dist/Customer";
|
||||
import { Document, DocumentHistory, File as FileCustomer } from "le-coffre-resources/dist/Customer";
|
||||
import Files from "@Front/Api/LeCoffreApi/SuperAdmin/Files/Files";
|
||||
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
|
||||
import classNames from "classnames";
|
||||
import Confirm from "../Modal/Confirm";
|
||||
import InputField from "../Form/Elements/InputField";
|
||||
|
||||
type IProps = {
|
||||
title: string;
|
||||
@ -23,12 +27,15 @@ type IProps = {
|
||||
type IFile = {
|
||||
index: number;
|
||||
file: File;
|
||||
uid: string;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
files: IFile[];
|
||||
isDragOver: boolean;
|
||||
currentFiles?: FileCustomer[];
|
||||
refusedReason?: string;
|
||||
isShowRefusedReasonModalVisible: boolean;
|
||||
};
|
||||
|
||||
export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
@ -42,6 +49,8 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
files: [],
|
||||
isDragOver: false,
|
||||
currentFiles: this.props.defaultFiles,
|
||||
refusedReason: "",
|
||||
isShowRefusedReasonModalVisible: false,
|
||||
};
|
||||
|
||||
this.addDocument = this.addDocument.bind(this);
|
||||
@ -50,12 +59,18 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
this.onDragOver = this.onDragOver.bind(this);
|
||||
this.onDragDrop = this.onDragDrop.bind(this);
|
||||
this.onDragLeave = this.onDragLeave.bind(this);
|
||||
this.onCloseModalShowRefusedReason = this.onCloseModalShowRefusedReason.bind(this);
|
||||
this.onOpenModalShowRefusedReason = this.onOpenModalShowRefusedReason.bind(this);
|
||||
this.showRefusedReason = this.showRefusedReason.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
className={classes["root"]}
|
||||
className={classNames(
|
||||
classes["root"],
|
||||
this.props.document.document_status === EDocumentStatus.VALIDATED && classes["validated"],
|
||||
)}
|
||||
onDragOver={this.onDragOver}
|
||||
onDrop={this.onDragDrop}
|
||||
onDragLeave={this.onDragLeave}
|
||||
@ -68,14 +83,22 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
<div className={classes["separator"]} />
|
||||
<div className={classes["right"]}>
|
||||
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK} className={classes["title"]}>
|
||||
{this.props.title} <Tooltip text={"Blabla"} />
|
||||
{this.props.title}{" "}
|
||||
{this.props.document.document_type?.public_description !== "" && (
|
||||
<Tooltip text={this.props.document.document_type?.public_description} />
|
||||
)}
|
||||
</Typography>
|
||||
<Typography typo={ITypo.CAPTION_14} color={ITypoColor.GREY}>
|
||||
Demandé par le notaire le {this.formatDate(this.props.dateAsked)}
|
||||
{this.props.document.document_status !== EDocumentStatus.VALIDATED && (
|
||||
<Typography color={ITypoColor.GREY} typo={ITypo.CAPTION_14}>
|
||||
Sélectionnez des documents .jpg, .pdf ou .png
|
||||
</Typography>
|
||||
)}
|
||||
{this.props.document.document_history?.map((history) => (
|
||||
<div key={history.uid}>{this.renderDocumentHistory(history)}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{this.state.files.length > 0 && (
|
||||
{this.props.document.document_status !== EDocumentStatus.VALIDATED && this.state.files.length > 0 && (
|
||||
<div className={classes["documents-container"]}>
|
||||
{this.state.files.map((file) => {
|
||||
const fileObj = file.file;
|
||||
@ -99,7 +122,7 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{this.props.document.document_status !== EDocumentStatus.VALIDATED && (
|
||||
<div className={classes["bottom-container"]}>
|
||||
<Button variant={EButtonVariant.LINE} className={classes["add-button"]} onClick={this.addDocument}>
|
||||
<Typography typo={ITypo.P_SB_16} color={ITypoColor.PINK_FLASH} className={classes["add-document"]}>
|
||||
@ -107,6 +130,23 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
</Typography>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<Confirm
|
||||
isOpen={this.state.isShowRefusedReasonModalVisible}
|
||||
onClose={this.onCloseModalShowRefusedReason}
|
||||
showCancelButton={false}
|
||||
onAccept={this.onCloseModalShowRefusedReason}
|
||||
closeBtn
|
||||
header={"Motif du refus"}
|
||||
confirmText={"J’ai compris"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
||||
Votre document a été refusé pour la raison suivante :
|
||||
</Typography>
|
||||
<InputField textarea fakeplaceholder={"Description"} value={"this.state.refusedReason"} />
|
||||
</div>
|
||||
</Confirm>
|
||||
;
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -117,11 +157,63 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
files: this.props.defaultFiles.map((file) => ({
|
||||
index: this.index++,
|
||||
file: new File([""], file.file_path ?? "", {}),
|
||||
uid: file.uid!,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onCloseModalShowRefusedReason() {
|
||||
this.setState({
|
||||
isShowRefusedReasonModalVisible: false,
|
||||
});
|
||||
}
|
||||
|
||||
private onOpenModalShowRefusedReason() {
|
||||
this.setState({
|
||||
isShowRefusedReasonModalVisible: true,
|
||||
});
|
||||
}
|
||||
|
||||
private renderDocumentHistory(history: DocumentHistory): JSX.Element | null {
|
||||
switch (history.document_status) {
|
||||
case EDocumentStatus.ASKED:
|
||||
return (
|
||||
<Typography color={ITypoColor.GREY} typo={ITypo.CAPTION_14}>
|
||||
Demandé par votre notaire le {this.formatDate(history.created_at!)}
|
||||
</Typography>
|
||||
);
|
||||
case EDocumentStatus.VALIDATED:
|
||||
return (
|
||||
<Typography color={ITypoColor.GREY} typo={ITypo.CAPTION_14}>
|
||||
Validé par votre notaire le {this.formatDate(history.created_at!)}
|
||||
</Typography>
|
||||
);
|
||||
case EDocumentStatus.DEPOSITED:
|
||||
return (
|
||||
<Typography color={ITypoColor.GREY} typo={ITypo.CAPTION_14}>
|
||||
Déposé le {this.formatDate(history.created_at!)}
|
||||
</Typography>
|
||||
);
|
||||
|
||||
case EDocumentStatus.REFUSED:
|
||||
return (
|
||||
<Typography typo={ITypo.CAPTION_14} color={ITypoColor.RE_HOVER}>
|
||||
Document non conforme
|
||||
{history.refused_reason !== "" && (
|
||||
<Button
|
||||
variant={EButtonVariant.LINE}
|
||||
className={classes["refused-button"]}
|
||||
onClick={() => this.showRefusedReason(history.refused_reason ?? "")}>
|
||||
Voir le motif de refus
|
||||
</Button>
|
||||
)}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private shortName(name: string): string {
|
||||
const maxLength = 20;
|
||||
if (name.length > maxLength) {
|
||||
@ -139,6 +231,13 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
private showRefusedReason(refusedReason: string) {
|
||||
this.setState({
|
||||
refusedReason,
|
||||
});
|
||||
this.onOpenModalShowRefusedReason();
|
||||
}
|
||||
|
||||
private onDragLeave(event: React.DragEvent<HTMLDivElement>) {
|
||||
this.setState({
|
||||
isDragOver: false,
|
||||
@ -156,18 +255,6 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
}
|
||||
|
||||
private async addFile(file: File) {
|
||||
this.setState({
|
||||
files: [
|
||||
...this.state.files,
|
||||
{
|
||||
index: this.index++,
|
||||
file: file,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (this.props.onChange) this.props.onChange(this.state.files.map((file) => file.file));
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file, file.name);
|
||||
const query = JSON.stringify({ document: { uid: this.props.document.uid } });
|
||||
@ -175,25 +262,34 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
|
||||
const newFile = await Files.getInstance().post(formData);
|
||||
const files = this.state.currentFiles ? [...this.state.currentFiles, newFile] : [newFile];
|
||||
|
||||
this.setState({
|
||||
currentFiles: files,
|
||||
files: [
|
||||
...this.state.files,
|
||||
{
|
||||
index: this.index++,
|
||||
file: file,
|
||||
uid: newFile.uid!,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (this.props.onChange) this.props.onChange(this.state.files.map((file) => file.file));
|
||||
}
|
||||
|
||||
private async removeFile(e: any) {
|
||||
const image = e.target as HTMLElement;
|
||||
const indexToRemove = image.getAttribute("data-file");
|
||||
if (!indexToRemove) return;
|
||||
// const file = this.state.files.find((file) => file.index === parseInt(indexToRemove));
|
||||
const file = this.state.files.find((file) => file.index === parseInt(indexToRemove));
|
||||
if (!file) return;
|
||||
this.setState({
|
||||
files: this.state.files.filter((file) => file.index !== parseInt(indexToRemove)),
|
||||
});
|
||||
|
||||
if (this.props.onChange) this.props.onChange(this.state.files.map((file) => file.file));
|
||||
// TODO Finir la suppression de fichier
|
||||
// const deletedFileUid = this.props.document.files?.find((file) => file.file_path === newFile.file_path)?.uid;
|
||||
// console.log({ deletedFileUid });
|
||||
// await Files.getInstance().delete(file?.uid);
|
||||
await Files.getInstance().delete(file.uid);
|
||||
}
|
||||
|
||||
private async onFileChange() {
|
||||
@ -211,6 +307,7 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
}
|
||||
|
||||
private formatDate(date: Date) {
|
||||
return date.toLocaleDateString("fr-FR");
|
||||
const dateToConvert = new Date(date);
|
||||
return dateToConvert.toLocaleDateString("fr-FR");
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ export type IProps = IBaseFieldProps & {
|
||||
export default class InputField extends BaseField<IProps> {
|
||||
static override defaultProps: Partial<IBaseFieldProps> = {
|
||||
...BaseField.defaultProps,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
};
|
||||
|
||||
public override render(): ReactNode {
|
||||
let pattern;
|
||||
@ -51,9 +51,12 @@ export default class InputField extends BaseField<IProps> {
|
||||
className={
|
||||
this.props.className ? [classes["textarea"], classes[this.props.className]].join(" ") : classes["textarea"]
|
||||
}
|
||||
value={value}
|
||||
/>
|
||||
<div className={classes["fake-placeholder"]}>{this.props.fakeplaceholder} {!this.props.required && " (Facultatif)"}</div>
|
||||
value={value}>
|
||||
{value.toString()}
|
||||
</textarea>
|
||||
<div className={classes["fake-placeholder"]}>
|
||||
{this.props.fakeplaceholder} {!this.props.required && " (Facultatif)"}
|
||||
</div>
|
||||
</div>
|
||||
</Typography>
|
||||
);
|
||||
@ -74,7 +77,9 @@ export default class InputField extends BaseField<IProps> {
|
||||
}
|
||||
value={value}
|
||||
/>
|
||||
<div className={classes["fake-placeholder"]}>{this.props.fakeplaceholder} {!this.props.required && " (Facultatif)"}</div>
|
||||
<div className={classes["fake-placeholder"]}>
|
||||
{this.props.fakeplaceholder} {!this.props.required && " (Facultatif)"}
|
||||
</div>
|
||||
</div>
|
||||
</Typography>
|
||||
);
|
||||
@ -84,7 +89,7 @@ export default class InputField extends BaseField<IProps> {
|
||||
public override componentDidMount() {
|
||||
this.setState({
|
||||
value: this.props.defaultValue ?? "",
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// We filter the props we'll pass to the primitive input as they're useless for it
|
||||
|
@ -19,13 +19,18 @@ type IState = {
|
||||
const LightTooltip = styled(({ className, ...props }: TooltipProps) => <TooltipMUI {...props} classes={{ popper: className }} />)(
|
||||
({ theme }) => ({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
backgroundColor: "var(--turquoise-flash)",
|
||||
color: "white",
|
||||
//boxShadow: theme.shadows[1],
|
||||
fontSize: 11,
|
||||
backgroundColor: "var(--white)",
|
||||
color: "var(--black)",
|
||||
boxShadow: "0px 4px 24px 0px #00000026",
|
||||
fontSize: 14,
|
||||
fontWeight: 400,
|
||||
lineHeight: "22px",
|
||||
fontFamily: "var(--font-primary)",
|
||||
padding: "16px",
|
||||
borderRadius: "0px",
|
||||
},
|
||||
[`& .${tooltipClasses.arrow}`]: {
|
||||
color: "var(--turquoise-flash)",
|
||||
color: "var(--white)",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
import "reflect-metadata";
|
||||
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import DepositDocument from "@Front/Components/DesignSystem/DepositDocument";
|
||||
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField";
|
||||
@ -112,6 +113,8 @@ export default class ClientDashboard extends Base<IProps, IState> {
|
||||
where: { depositor: mockUsers?.uid },
|
||||
include: {
|
||||
files: true,
|
||||
document_history: true,
|
||||
document_type: true,
|
||||
},
|
||||
};
|
||||
const documents: Document[] = await Documents.getInstance().get(query);
|
||||
|
Loading…
x
Reference in New Issue
Block a user