1 Commits

Author SHA1 Message Date
2ff15cdef4 Refactoring schema fetching in hooks 2025-05-14 19:08:15 +02:00
5 changed files with 24 additions and 107 deletions

View File

@@ -3,7 +3,6 @@ import os
import base64 import base64
from uuid import UUID from uuid import UUID
from beanie import PydanticObjectId
from fastapi import APIRouter, HTTPException, Request, Depends from fastapi import APIRouter, HTTPException, Request, Depends
from fastapi.responses import HTMLResponse, FileResponse from fastapi.responses import HTMLResponse, FileResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
@@ -19,22 +18,24 @@ from firm.template.models import ProvisionTemplate
from firm.contract.models import ContractDraft, Contract, ContractStatus, replace_variables_in_value from firm.contract.models import ContractDraft, Contract, ContractStatus, replace_variables_in_value
async def build_model(db, model): async def build_model(model):
parties = [] parties = []
for p in model.parties: for p in model.parties:
party = { party = {
"entity": await Entity.get(db, p.entity_id), "entity": await Entity.get(p.entity_id),
"part": p.part "part": p.part
} }
if p.representative_id: if p.representative_id:
party['representative'] = await Entity.get(db, p.representative_id) party['representative'] = await Entity.get(p.representative_id)
parties.append(party) parties.append(party)
model.parties = parties
provisions = [] provisions = []
for p in model.provisions: for p in model.provisions:
if p.provision.type == "template": if p.provision.type == "template":
provision = await ProvisionTemplate.get(db, p.provision.provision_template_id) provision = await ProvisionTemplate.get(p.provision.provision_template_id)
else: else:
provision = p.provision provision = p.provision
@@ -42,16 +43,16 @@ async def build_model(db, model):
provision.body = replace_variables_in_value(model.variables, provision.body) provision.body = replace_variables_in_value(model.variables, provision.body)
provisions.append(provision) provisions.append(provision)
model_dict = model.dict() model.provisions = provisions
model_dict['parties'] = parties
model_dict['provisions'] = provisions model = model.dict()
model_dict['location'] = "Los Santos, SA" model['location'] = "Los Santos, SA"
model_dict['date'] = datetime.date(1970, 1, 1) model['date'] = datetime.date(1970, 1, 1)
model_dict['lawyer'] = {'entity_data': { model['lawyer'] = {'entity_data': {
"firstname": "prénom avocat", "firstname": "prénom avocat",
"lastname": "nom avocat", "lastname": "nom avocat",
}} }}
return model_dict return model
BASE_PATH = Path(__file__).resolve().parent BASE_PATH = Path(__file__).resolve().parent
@@ -86,14 +87,8 @@ def retrieve_signature_png(filepath):
preview_router = APIRouter() preview_router = APIRouter()
@preview_router.get("/draft/{draft_id}", response_class=HTMLResponse, tags=["Contract Draft"]) @preview_router.get("/draft/{draft_id}", response_class=HTMLResponse, tags=["Contract Draft"])
async def preview_draft(draft_id: PydanticObjectId, reg=Depends(get_tenant_registry)) -> str: async def preview_draft(draft_id: str, reg=Depends(get_tenant_registry)) -> str:
record = await ContractDraft.get(reg.db, draft_id) draft = await build_model(await ContractDraft.get(reg.db, draft_id))
if not record:
raise HTTPException(
status_code=404,
detail=f"Contract Draft record not found!"
)
draft = await build_model(reg.db, record)
return await render_print('', draft) return await render_print('', draft)
@@ -109,7 +104,7 @@ async def preview_contract_by_signature(signature_id: UUID, reg=Depends(get_tena
@preview_router.get("/{contract_id}", response_class=HTMLResponse, tags=["Contract"]) @preview_router.get("/{contract_id}", response_class=HTMLResponse, tags=["Contract"])
async def preview_contract(contract_id: PydanticObjectId, reg=Depends(get_tenant_registry)) -> str: async def preview_contract(contract_id: str, reg=Depends(get_tenant_registry)) -> str:
contract = await Contract.get(reg.db, contract_id) contract = await Contract.get(reg.db, contract_id)
for p in contract.parties: for p in contract.parties:
if p.signature_affixed: if p.signature_affixed:
@@ -120,7 +115,7 @@ async def preview_contract(contract_id: PydanticObjectId, reg=Depends(get_tenant
print_router = APIRouter() print_router = APIRouter()
@print_router.get("/pdf/{contract_id}", response_class=FileResponse, tags=["Contract"]) @print_router.get("/pdf/{contract_id}", response_class=FileResponse, tags=["Contract"])
async def create_pdf(contract_id: PydanticObjectId, reg=Depends(get_tenant_registry)) -> str: async def create_pdf(contract_id: str, reg=Depends(get_tenant_registry)) -> str:
contract = await Contract.get(reg.db, contract_id) contract = await Contract.get(reg.db, contract_id)
contract_path = "media/contracts/{}.pdf".format(contract_id) contract_path = "media/contracts/{}.pdf".format(contract_id)
if not os.path.isfile(contract_path): if not os.path.isfile(contract_path):
@@ -147,7 +142,7 @@ async def create_pdf(contract_id: PydanticObjectId, reg=Depends(get_tenant_regis
@print_router.get("/opengraph/{signature_id}", response_class=HTMLResponse, tags=["Signature"]) @print_router.get("/opengraph/{signature_id}", response_class=HTMLResponse, tags=["Signature"])
async def get_signature_opengraph(signature_id: UUID, request: Request, reg=Depends(get_tenant_registry)) -> str: async def get_signature_opengraph(signature_id: str, request: Request, reg=Depends(get_tenant_registry)) -> str:
contract = await Contract.find_by_signature_id(reg.db, signature_id) contract = await Contract.find_by_signature_id(reg.db, signature_id)
signature = contract.get_signature(signature_id) signature = contract.get_signature(signature_id)
template = templates.get_template("opengraph.html") template = templates.get_template("opengraph.html")

View File

@@ -57,7 +57,7 @@
<div class="footer"> <div class="footer">
<hr/> <hr/>
<p>À {{ contract.location }} le {{ contract.date.strftime('%d/%m/%Y') }}</p> <p>À {{ contract.location }} le {{ contract.date.strftime('%d/%m/%Y') }}</p>
<p class="mention">(Signatures précédées de la mention « Lu et approuvé »)</p> <p class="mention">(Signatures précédée de la mention « Lu et approuvé »)</p>
<table class="signatures"> <table class="signatures">
<tr> <tr>
{% for party in contract.parties %} {% for party in contract.parties %}

View File

@@ -1,10 +1,7 @@
import React from "react"; import React from "react";
import { getDefaultRegistry } from "@rjsf/core"; import { getDefaultRegistry } from "@rjsf/core";
import { FormContextType, RJSFSchema, WidgetProps } from "@rjsf/utils"; import { FormContextType, RJSFSchema, WidgetProps } from "@rjsf/utils";
import { Button, InputAdornment } from "@mui/material";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import CopyAllIcon from '@mui/icons-material/CopyAll';
import ForeignKeyWidget from "./foreign-key"; import ForeignKeyWidget from "./foreign-key";
import RichtextWidget from "./richtext"; import RichtextWidget from "./richtext";
@@ -21,37 +18,8 @@ export default function CrudTextWidget<T = any, S extends CrudTextRJSFSchema = C
return <Typography >{schema.const as string}</Typography>; return <Typography >{schema.const as string}</Typography>;
} else if (schema.props?.hasOwnProperty("richtext")) { } else if (schema.props?.hasOwnProperty("richtext")) {
return <RichtextWidget {...props} />; return <RichtextWidget {...props} />;
} else if (schema.props?.hasOwnProperty("display") && schema.props.display == "signature-link") {
return <SignatureLink {...props} />
} else { } else {
const { widgets: { TextWidget } } = getDefaultRegistry<T,S,F>(); const { widgets: { TextWidget } } = getDefaultRegistry<T,S,F>();
return <TextWidget {...props} />; return <TextWidget {...props} />;
} }
} }
const SignatureLink = <T = any, S extends CrudTextRJSFSchema = CrudTextRJSFSchema, F extends FormContextType = any>(props: WidgetProps<T, S, F> )=> {
const { label, value } = props;
const basePath = "/contracts/signature/";
const url = location.origin + basePath + value
return <TextField
label={ label }
variant="outlined"
disabled={true}
value={url}
slotProps={{
input: {
endAdornment: (
<InputAdornment position="end">
<Button
variant="outlined"
onClick={() => navigator.clipboard.writeText(url)}
color="primary"
>
<CopyAllIcon />
</Button>
</InputAdornment>),
},
}}
/>
}

View File

@@ -14,7 +14,7 @@ export function useResourceSchema(schemaName: string, type: ResourceSchemaType)
let resourceSchema let resourceSchema
if (type == "create") { if (type == "create") {
resourceSchema = await jsonschemaProvider.getCreateResourceSchema(schemaName); resourceSchema = await jsonschemaProvider.getCreateResourceSchema(schemaName);
} else if (type == "card") { } else if (type == "update") {
resourceSchema = await jsonschemaProvider.getCardResourceSchema(schemaName); resourceSchema = await jsonschemaProvider.getCardResourceSchema(schemaName);
} else { } else {
resourceSchema = await jsonschemaProvider.getUpdateResourceSchema(schemaName); resourceSchema = await jsonschemaProvider.getUpdateResourceSchema(schemaName);

View File

@@ -1,9 +1,6 @@
import dayjs from "dayjs";
import React, { useContext, useState } from "react";
import { Navigate, Route, Routes, useParams } from "react-router"; import { Navigate, Route, Routes, useParams } from "react-router";
import { Box, Button, CircularProgress, Container, DialogContent, Modal, Paper } from "@mui/material"; import { CircularProgress } from "@mui/material";
import Stack from "@mui/material/Stack"; import React, { useContext, useState } from "react";
import PreviewIcon from '@mui/icons-material/Preview';
import { useOne, useTranslation } from "@refinedev/core"; import { useOne, useTranslation } from "@refinedev/core";
import { BaseForm } from "../../lib/crud/components/base-form"; import { BaseForm } from "../../lib/crud/components/base-form";
import { ForeignKeyReference, ForeignKeySchema } from "../../lib/crud/components/widgets/foreign-key"; import { ForeignKeyReference, ForeignKeySchema } from "../../lib/crud/components/widgets/foreign-key";
@@ -13,6 +10,7 @@ import List from "./base-page/List";
import Edit from "./base-page/Edit"; import Edit from "./base-page/Edit";
import New from "./base-page/New"; import New from "./base-page/New";
import { Contract } from "./ContractRoutes"; import { Contract } from "./ContractRoutes";
import dayjs from "dayjs";
type Draft = { type Draft = {
id: string, id: string,
@@ -52,7 +50,7 @@ const EditDraft = () => {
return <CircularProgress /> return <CircularProgress />
} }
if (record_id == undefined || !data?.data) { if (!data?.data) {
return <Navigate to="../" /> return <Navigate to="../" />
} }
@@ -65,56 +63,12 @@ const EditDraft = () => {
return ( return (
<> <>
<DraftPreview resourceBasePath={resourceBasePath} recordId={record_id}/>
<Edit<Draft> resource={"contracts/drafts"} schemaName={"ContractDraft"} uiSchema={uiSchema} /> <Edit<Draft> resource={"contracts/drafts"} schemaName={"ContractDraft"} uiSchema={uiSchema} />
<ContractCreate draft={draft}></ContractCreate> <ContractCreate draft={draft}></ContractCreate>
</> </>
) )
} }
const DraftPreview = (props: {resourceBasePath: string, recordId: string}) => {
const { resourceBasePath, recordId } = props
const [openPreviewModal, setOpenPreviewModal] = React.useState(false);
return (
<>
<Button variant="outlined" onClick={() => setOpenPreviewModal(true)} color="primary" >
<PreviewIcon />Preview
</Button>
<Modal
open={openPreviewModal}
onClose={() => setOpenPreviewModal(false)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<DialogContent>
<Container>
<Paper>
<Stack
direction={"row"}
spacing={2}
sx={{
justifyContent: "center",
alignItems: "center",
}}
>
<Box padding={"45px"}>
<iframe
src={`/api/v1/${resourceBasePath}/contracts/preview/draft/${recordId}`}
width="675px"
height="955px"
style={{ backgroundColor: "white", border: "1px solid black" }}
/>
</Box>
</Stack>
</Paper>
</Container>
</DialogContent>
</Modal>
</>
)
}
const ContractCreate = (props: { draft: any}) => { const ContractCreate = (props: { draft: any}) => {
const { translate: t } = useTranslation(); const { translate: t } = useTranslation();
const { draft } = props; const { draft } = props;