Compare commits

...

6 Commits

Author SHA1 Message Date
76a5c0b454 Repairing Edit Form buttons 2025-04-27 17:26:12 +02:00
2b7a92097c Importing Skip jsonSchema 2025-04-27 15:54:19 +02:00
c9f8c69e42 Adding labels to drafts 2025-04-27 15:53:48 +02:00
bc41823dc3 Créating an official foreign key field 2025-04-27 01:21:05 +02:00
6c2047033b Prefilled drafts 2025-04-26 01:07:39 +02:00
6c3f6c8d03 Correcting data provider path 2025-04-26 01:07:08 +02:00
7 changed files with 144 additions and 44 deletions

View File

@@ -5,8 +5,9 @@ from uuid import UUID
from beanie import PydanticObjectId from beanie import PydanticObjectId
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from pydantic.json_schema import SkipJsonSchema
from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry, ForeignKey
from firm.core.filter import Filter, FilterSchema from firm.core.filter import Filter, FilterSchema
from firm.entity.models import Entity from firm.entity.models import Entity
@@ -25,27 +26,10 @@ class ContractDraftStatus(str, Enum):
class DraftParty(BaseModel): class DraftParty(BaseModel):
entity_id: PydanticObjectId = Field( entity_id: PydanticObjectId = ForeignKey("entities", "Entity", default="", title="Partie")
foreignKey={ entity: SkipJsonSchema[Entity] = Field(default=None, exclude=True, )
"reference": {
"resource": "entities",
"schema": "Entity",
}
},
default="",
title="Partie"
)
part: str = Field(title="Rôle") part: str = Field(title="Rôle")
representative_id: PydanticObjectId = Field( representative_id: PydanticObjectId = ForeignKey("entities", "Entity", default="", title="Représentant")
foreignKey={
"reference": {
"resource": "entities",
"schema": "Entity",
}
},
default="",
title="Représentant"
)
class Config: class Config:
title = 'Partie' title = 'Partie'
@@ -74,14 +58,10 @@ class ProvisionGenuine(BaseModel):
class ContractProvisionTemplateReference(BaseModel): class ContractProvisionTemplateReference(BaseModel):
type: Literal['template'] = ContractProvisionType.template type: Literal['template'] = ContractProvisionType.template
provision_template_id: PydanticObjectId = Field( provision_template_id: PydanticObjectId = ForeignKey(
foreignKey={ "templates/provisions",
"reference": { "ProvisionTemplate",
"resource": "templates/provisions", displayed_fields=['title', 'body'],
"schema": "ProvisionTemplate",
"displayedFields": ['title', 'body']
},
},
props={"parametrized": True}, props={"parametrized": True},
default="", default="",
title="Template de clause" title="Template de clause"
@@ -173,6 +153,9 @@ class ContractDraft(CrudDocument):
update = ContractDraftUpdateStatus(status=status) update = ContractDraftUpdateStatus(status=status)
await self.update(db, self, update) await self.update(db, self, update)
def compute_label(self) -> str:
return f"{self.name} - {self.title}"
class Contract(CrudDocument): class Contract(CrudDocument):
""" """
Contrat publié. Les contrats ne peuvent pas être modifiés. Contrat publié. Les contrats ne peuvent pas être modifiés.

View File

@@ -114,6 +114,20 @@ def RichtextSingleline(*args, **kwargs):
return Field(*args, **kwargs) return Field(*args, **kwargs)
def ForeignKey(resource, schema, displayed_fields=None, *args, **kwargs):
kwargs["foreignKey"] = {
"reference": {
"resource": resource,
"schema": schema,
}
}
if displayed_fields:
kwargs["foreignKey"]["reference"]["displayedFields"] = displayed_fields
return Field(*args, **kwargs)
class DictionaryEntry(BaseModel): class DictionaryEntry(BaseModel):
key: str key: str
value: str = "" value: str = ""

View File

@@ -4,6 +4,7 @@ import { RegistryFieldsType, RegistryWidgetsType, RJSFSchema, UiSchema } from "@
import CrudTextWidget from "./widgets/crud-text-widget"; import CrudTextWidget from "./widgets/crud-text-widget";
import UnionEnumField from "./fields/union-enum"; import UnionEnumField from "./fields/union-enum";
import { ResourceContext } from "../contexts/ResourceContext"; import { ResourceContext } from "../contexts/ResourceContext";
import { ReactNode } from "react";
type BaseFormProps = { type BaseFormProps = {
schema: RJSFSchema, schema: RJSFSchema,
@@ -12,6 +13,7 @@ type BaseFormProps = {
onChange?: (data: any) => void, onChange?: (data: any) => void,
uiSchema?: UiSchema, uiSchema?: UiSchema,
formData?: any, formData?: any,
children?: ReactNode
} }
export const customWidgets: RegistryWidgetsType = { export const customWidgets: RegistryWidgetsType = {
@@ -23,7 +25,7 @@ export const customFields: RegistryFieldsType = {
} }
export const BaseForm: React.FC<BaseFormProps> = (props) => { export const BaseForm: React.FC<BaseFormProps> = (props) => {
const { schema, uiSchema, resourceBasePath, formData, onSubmit, onChange } = props; const { schema, uiSchema, resourceBasePath, formData, children, onSubmit, onChange } = props;
return ( return (
<ResourceContext.Provider value={{basePath: resourceBasePath}} > <ResourceContext.Provider value={{basePath: resourceBasePath}} >
@@ -37,6 +39,7 @@ export const BaseForm: React.FC<BaseFormProps> = (props) => {
widgets={customWidgets} widgets={customWidgets}
fields={customFields} fields={customFields}
onChange={(e, id) => onChange != undefined && onChange(e.formData)} onChange={(e, id) => onChange != undefined && onChange(e.formData)}
children={children}
/> />
</ResourceContext.Provider> </ResourceContext.Provider>
) )

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { ReactNode, useEffect, useState } from "react";
import { CircularProgress } from "@mui/material"; import { CircularProgress } from "@mui/material";
import { useForm } from "@refinedev/core"; import { useForm } from "@refinedev/core";
import { UiSchema } from "@rjsf/utils"; import { UiSchema } from "@rjsf/utils";
@@ -12,11 +12,12 @@ type CrudFormProps = {
resource: string, resource: string,
id?: string, id?: string,
onSuccess?: (data: any) => void, onSuccess?: (data: any) => void,
defaultValue?: any defaultValue?: any,
children?: ReactNode
} }
export const CrudForm: React.FC<CrudFormProps> = (props) => { export const CrudForm: React.FC<CrudFormProps> = (props) => {
const { schemaName, uiSchema, resourceBasePath="" ,resource, id, onSuccess, defaultValue } = props; const { schemaName, uiSchema, resourceBasePath="" ,resource, id, onSuccess, defaultValue, children } = props;
const { onFinish, query, formLoading } = useForm({ const { onFinish, query, formLoading } = useForm({
resource: resourceBasePath == "" ? resource : `${resourceBasePath}/${resource}`, resource: resourceBasePath == "" ? resource : `${resourceBasePath}/${resource}`,
@@ -57,6 +58,7 @@ export const CrudForm: React.FC<CrudFormProps> = (props) => {
onSubmit={ onSubmit={
(data: any) => onFinish(data) (data: any) => onFinish(data)
} }
children={children}
/> />
) )
} }

View File

@@ -1,4 +1,11 @@
import { Route, Routes } from "react-router"; import { Route, Routes } from "react-router";
import { CircularProgress } from "@mui/material";
import React, { useContext, useState } from "react";
import { useOne } from "@refinedev/core";
import { BaseForm } from "../../lib/crud/components/base-form";
import { ForeignKeyReference, ForeignKeySchema } from "../../lib/crud/components/widgets/foreign-key";
import { FirmContext } from "../../contexts/FirmContext";
import List from "./base-page/List"; 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";
@@ -29,6 +36,79 @@ const EditDraft = () => {
return <Edit<Draft> resource={`contracts/drafts`} schemaName={"ContractDraft"} /> return <Edit<Draft> resource={`contracts/drafts`} schemaName={"ContractDraft"} />
} }
const CreateDraft = () => { type ForeignKeySubSchema = ForeignKeySchema & {
return <New<Draft> resource={`contracts/drafts`} schemaName={"ContractDraft"} /> properties: { [key: string]: { foreignKey: { reference: ForeignKeyReference } } }
}
const CreateDraft = () => {
const [chosenDraft, setChosenDraft] = useState<string|null>(null)
const { currentFirm } = useContext(FirmContext);
const resourceBasePath = `firm/${currentFirm.instance}/${currentFirm.firm}`
const templateFieldSchema: ForeignKeySubSchema = {
type: "object",
properties: {
template_id: {
type: "string",
title: "Find a template",
foreignKey: {
reference: {
resource: "templates/contracts",
schema: "ContractTemplate"
}
}
}
},
};
const templateForm = (
<BaseForm
schema={templateFieldSchema}
formData={{template_id: chosenDraft}}
resourceBasePath={resourceBasePath}
onChange={(data) => {
const { template_id } = data;
setChosenDraft(template_id);
}}
>
&nbsp;
</BaseForm>
)
if (chosenDraft !== null) {
return (
<>
{templateForm}
<CreateDraftFromTemplate template_id={chosenDraft}/>
</>
)
}
return (
<>
{templateForm}
<New<Draft> resource={`contracts/drafts`} schemaName={"ContractDraft"} />
</>
)
}
const CreateDraftFromTemplate = (props: { template_id: string }) => {
const { template_id } = props;
const { currentFirm } = useContext(FirmContext);
const resourceBasePath = `firm/${currentFirm.instance}/${currentFirm.firm}`
const resource = "templates/contracts"
const { data, isLoading } = useOne({
resource: `${resourceBasePath}/${resource}`,
id: template_id
});
if (isLoading || data === undefined) {
return <CircularProgress />
}
let template = { ...data.data };
template.provisions = data.data.provisions.map((item: any) => {
return { provision: {type: "template", provision_template_id: item.provision_template_id} }
})
return <New<Draft> resource={`contracts/drafts`} schemaName={"ContractDraft"} defaultValue={ template }/>
} }

View File

@@ -1,8 +1,13 @@
import { CrudForm } from "../../../lib/crud/components/crud-form";
import { UiSchema } from "@rjsf/utils"; import { UiSchema } from "@rjsf/utils";
import { useParams } from "react-router"; import { useParams } from "react-router";
import { useContext } from "react"; import { useContext } from "react";
import { Button } from "@mui/material";
import DeleteIcon from '@mui/icons-material/Delete';
import SaveIcon from '@mui/icons-material/Save';
import { FirmContext } from "../../../contexts/FirmContext"; import { FirmContext } from "../../../contexts/FirmContext";
import { CrudForm } from "../../../lib/crud/components/crud-form";
import Stack from "@mui/material/Stack";
import { DeleteButton } from "@refinedev/mui";
type EditProps = { type EditProps = {
resource: string, resource: string,
@@ -17,13 +22,26 @@ const Edit = <T,>(props: EditProps) => {
const { record_id } = useParams(); const { record_id } = useParams();
return ( return (
<CrudForm <>
schemaName={schemaName} <CrudForm
uiSchema={uiSchema} schemaName={schemaName}
resourceBasePath={resourceBasePath} uiSchema={uiSchema}
resource={resource} resourceBasePath={resourceBasePath}
id={record_id} resource={resource}
/> id={record_id}
>
<Stack
direction="row"
spacing={2}
sx={{
justifyContent: "space-between",
alignItems: "center",
}}>
<Button type='submit' variant="contained" size="large"><SaveIcon />Save</Button>
<DeleteButton variant="contained" size="large" color="error" recordItemId={record_id}/>
</Stack>
</CrudForm>
</>
) )
} }

View File

@@ -96,7 +96,7 @@ export const dataProvider: DataProvider = {
}; };
}, },
create: async ({ resource, variables }) => { create: async ({ resource, variables }) => {
const response = await fetch(`${API_URL}/${resource}`, { const response = await fetch(`${API_URL}/${resource}/`, {
method: "POST", method: "POST",
body: JSON.stringify(variables), body: JSON.stringify(variables),
headers: { headers: {