Compare commits

...

6 Commits

10 changed files with 182 additions and 27 deletions

View File

@@ -159,7 +159,7 @@ class Contract(CrudDocument):
title: str = Field(title="Titre")
parties: List[Party] = Field(title="Parties")
provisions: List[Provision] = Field(
props={"items-per-row": "1", "numbered": True},
props={"items_per_row": "1", "numbered": True},
title='Clauses'
)
status: ContractStatus = Field(default=ContractStatus.published, title="Statut")

View File

@@ -20,12 +20,12 @@ class ContractDraftCreate(Writer):
title: str = Field(title='Titre')
parties: List[DraftParty] = Field(title='Parties')
provisions: List[DraftProvision] = Field(
props={"items-per-row": "1", "numbered": True},
props={"items_per_row": "1", "numbered": True},
title='Clauses'
)
variables: List[DictionaryEntry] = Field(
default=[],
format="dictionary",
props={"display": "dictionary"},
title='Variables'
)
@@ -50,7 +50,7 @@ class ForeignEntityRead(BaseModel):
class PartyRead(BaseModel):
signature_affixed: bool = Field(title='Signature apposée?')
signature_uuid: str = Field(format="signature-link", title="Lien vers signature")
signature_uuid: str = Field(props={"display": "signature-link"}, title="Lien vers signature")
part: str = Field(title='Rôle')
entity: ForeignEntityRead = Field(title='Client')
@@ -59,7 +59,10 @@ class PartyRead(BaseModel):
class ContractRead(Reader, Contract):
parties: List[PartyRead]
parties: List[PartyRead] = Field(
props={"items_per_row": "2"},
title='Parties'
)
lawyer: ForeignEntityRead
class Config:

View File

@@ -21,7 +21,7 @@ class Individual(EntityType):
lastname: Indexed(str) = Field(title='Nom de famille')
surnames: List[Indexed(str)] = Field(
default=[],
props={"items-per-row": "4", "numbered": True},
props={"items_per_row": "4", "numbered": True},
title="Surnoms"
)
day_of_birth: Optional[date] = Field(default=None, title='Date de naissance')

View File

@@ -16,13 +16,12 @@ class ContractTemplateCreate(Writer):
parties: List[PartyTemplate] = Field(default=[], title="Parties")
provisions: List[ProvisionTemplateReference] = Field(
default=[],
props={"items-per-row": "1", "numbered": True},
props={"items_per_row": "1", "numbered": True},
title="Clauses"
)
variables: List[DictionaryEntry] = Field(
default=[],
format="dictionary",
props={"required": False},
props={"display": "dictionary", "required": False},
title="Variables"
)

View File

@@ -3,6 +3,7 @@ import Form from "@rjsf/mui";
import { RegistryFieldsType, RegistryWidgetsType, RJSFSchema, UiSchema } from "@rjsf/utils";
import CrudTextWidget from "./widgets/crud-text-widget";
import UnionEnumField from "./fields/union-enum";
import ArrayFieldTemplate from "./templates/ArrayFieldTemplate"
import { ResourceContext } from "../contexts/ResourceContext";
import { ReactNode } from "react";
@@ -24,6 +25,10 @@ export const customFields: RegistryFieldsType = {
AnyOfField: UnionEnumField
}
const customTemplates = {
ArrayFieldTemplate
}
export const BaseForm: React.FC<BaseFormProps> = (props) => {
const { schema, uiSchema, resourceBasePath, formData, children, onSubmit, onChange } = props;
@@ -38,6 +43,7 @@ export const BaseForm: React.FC<BaseFormProps> = (props) => {
omitExtraData={true}
widgets={customWidgets}
fields={customFields}
templates={customTemplates}
onChange={(e, id) => onChange != undefined && onChange(e.formData)}
children={children}
/>

View File

@@ -10,21 +10,30 @@ type CrudFormProps = {
uiSchema?: UiSchema,
record?: any,
resourceBasePath: string,
onSubmit: (data: any) => void,
onSubmit?: (data: any) => void,
defaultValue?: any,
children?: ReactNode
card?: boolean
}
export const CrudForm: React.FC<CrudFormProps> = (props) => {
const { schemaName, uiSchema, record, resourceBasePath, defaultValue, children, onSubmit } = props;
const { schemaName, uiSchema, record, resourceBasePath, defaultValue, children, onSubmit=(data: any) => {}, card=false } = props;
const [schema, setSchema] = useState({});
const [schemaLoading, setSchemaLoading] = useState(true);
useEffect(() => {
const fetchSchema = async () => {
try {
const resourceSchema = record === undefined ? await jsonschemaProvider.getCreateResourceSchema(schemaName)
: await jsonschemaProvider.getUpdateResourceSchema(schemaName);
let resourceSchema
if (record === undefined) {
resourceSchema = await jsonschemaProvider.getCreateResourceSchema(schemaName);
} else {
if (card) {
resourceSchema = await jsonschemaProvider.getCardResourceSchema(schemaName);
} else {
resourceSchema = await jsonschemaProvider.getUpdateResourceSchema(schemaName);
}
}
setSchema(resourceSchema);
setSchemaLoading(false);
} catch (error) {

View File

@@ -0,0 +1,93 @@
import Box from '@mui/material/Box';
import Grid2 from '@mui/material/Grid2';
import Paper from '@mui/material/Paper';
import {
getTemplate,
getUiOptions,
ArrayFieldTemplateProps,
ArrayFieldTemplateItemType,
FormContextType,
} from '@rjsf/utils';
import { CrudTextRJSFSchema } from "../widgets/crud-text-widget";
/** The `ArrayFieldTemplate` component is the template used to render all items in an array.
*
* @param props - The `ArrayFieldTemplateItemType` props for the component
*/
export default function ArrayFieldTemplate<
T = any,
S extends CrudTextRJSFSchema = CrudTextRJSFSchema,
F extends FormContextType = any
>(props: ArrayFieldTemplateProps<T, S, F>) {
const { canAdd, disabled, idSchema, uiSchema, items, onAddClick, readonly, registry, required, schema, title } =
props;
let gridSize = 12;
if (schema.props) {
if (schema.props.hasOwnProperty("items_per_row")) {
gridSize = gridSize / schema.props.items_per_row;
}
}
const uiOptions = getUiOptions<T, S, F>(uiSchema);
const ArrayFieldDescriptionTemplate = getTemplate<'ArrayFieldDescriptionTemplate', T, S, F>(
'ArrayFieldDescriptionTemplate',
registry,
uiOptions
);
const ArrayFieldItemTemplate = getTemplate<'ArrayFieldItemTemplate', T, S, F>(
'ArrayFieldItemTemplate',
registry,
uiOptions
);
const ArrayFieldTitleTemplate = getTemplate<'ArrayFieldTitleTemplate', T, S, F>(
'ArrayFieldTitleTemplate',
registry,
uiOptions
);
// Button templates are not overridden in the uiSchema
const {
ButtonTemplates: { AddButton },
} = registry.templates;
return (
<Paper elevation={2}>
<Box p={2}>
<ArrayFieldTitleTemplate
idSchema={idSchema}
title={uiOptions.title || title}
schema={schema}
uiSchema={uiSchema}
required={required}
registry={registry}
/>
<ArrayFieldDescriptionTemplate
idSchema={idSchema}
description={uiOptions.description || schema.description}
schema={schema}
uiSchema={uiSchema}
registry={registry}
/>
<Grid2 container justifyContent='flex-end'>
{items &&
items.map(({ key, ...itemProps }: ArrayFieldTemplateItemType<T, S, F>) => (
<Grid2 key={key} size={gridSize} ><ArrayFieldItemTemplate key={key} {...itemProps} /></Grid2>
))}
</Grid2>
{canAdd && (
<Grid2 container justifyContent='flex-end'>
<Grid2>
<Box mt={2}>
<AddButton
className='array-item-add'
onClick={onAddClick}
disabled={disabled || readonly}
uiSchema={uiSchema}
registry={registry}
/>
</Box>
</Grid2>
</Grid2>
)}
</Box>
</Paper>
);
}

View File

@@ -57,7 +57,7 @@ const StyledLabelledOutlined = styled(LabelledOutlined)(({ theme }) => [{
const RichtextWidget = <T = any, S extends CrudTextRJSFSchema = CrudTextRJSFSchema, F extends FormContextType = any>(
props: WidgetProps<T, S, F>
) => {
const { schema, value, onChange, label, id } = props;
const { schema, value, onChange, label, id, readonly } = props;
const isMultiline = schema.props.multiline === true;
let editorOptions: UseEditorOptions;
@@ -92,14 +92,15 @@ const RichtextWidget = <T = any, S extends CrudTextRJSFSchema = CrudTextRJSFSche
<TextContainer>
<RichTextEditorProvider editor={editor}>
<TableBubbleMenu />
<RichTextField
controls={
<MenuControlsContainer>
{isMultiline ? multilineButtons : singlelineButtons}
</MenuControlsContainer>
}
variant="standard"
/>
{!readonly && <RichTextField
controls={
<MenuControlsContainer>
{isMultiline ? multilineButtons : singlelineButtons}
</MenuControlsContainer>
}
variant="standard"
/>}
{readonly && <RichTextField variant="standard" disabled={true}/>}
</RichTextEditorProvider>
</TextContainer>
<RightContainer>&nbsp;</RightContainer>

View File

@@ -12,13 +12,17 @@ type CrudRJSFSchema = RJSFSchema & {
} | undefined;
}
const meta_fields = ["label", "created_at", "created_by", "updated_at", "updated_by"]
export const jsonschemaProvider = {
getCardResourceSchema: async (resourceName: string): Promise<CrudRJSFSchema> => {
const updateSchema = await getResourceSchema(`${resourceName}Update`);
const readSchema = await getResourceSchema(`${resourceName}Read`);
for (let prop_name in readSchema.properties) {
if (! updateSchema.hasOwnProperty(prop_name)) {
if (meta_fields.indexOf(prop_name) > -1) {
delete readSchema.properties[prop_name];
} else if (! updateSchema.hasOwnProperty(prop_name)) {
if (is_reference(readSchema.properties[prop_name])) {
let subresourceName = get_reference_name(readSchema.properties[prop_name]);
readSchema.components.schemas[subresourceName].readOnly = true;

View File

@@ -1,6 +1,12 @@
import { Route, Routes } from "react-router";
import { useContext } from "react";
import { Route, Routes, useParams } from "react-router";
import { useOne, useTranslation } from "@refinedev/core";
import { DeleteButton } from "@refinedev/mui";
import { CircularProgress, Stack } from "@mui/material";
import { CrudForm } from "../../lib/crud/components/crud-form";
import { FirmContext } from "../../contexts/FirmContext";
import List from "./base-page/List";
import Edit from "./base-page/Edit";
import Cartouche from "../../components/Cartouche";
export type Contract = {
id: string,
@@ -24,5 +30,39 @@ const ListContract = () => {
}
const EditContract = () => {
return <Edit<Contract> resource={`contracts`} schemaName={"Contract"} />
}
const { currentFirm } = useContext(FirmContext);
const { translate: t } = useTranslation();
const resourceBasePath = `firm/${currentFirm.instance}/${currentFirm.firm}`
const { record_id } = useParams();
const { data, isLoading } = useOne({resource: `${resourceBasePath}/contracts`, id: record_id,});
if (isLoading || data?.data === undefined) {
return <CircularProgress />
}
const record = data.data;
return (
<>
<h2>{record.label}</h2>
<Cartouche record={record}/>
<CrudForm
resourceBasePath={resourceBasePath}
schemaName={"Contract"}
uiSchema={{"ui:readonly": true }}
record={record}
card={true}
>
<Stack
direction="row"
spacing={2}
sx={{
justifyContent: "flex-end",
alignItems: "center",
}}>
{ record.status == "published" && (<DeleteButton variant="contained" size="large" color="error" recordItemId={record_id}/>) }
</Stack>
</CrudForm>
</>
)
}