diff --git a/api/app/account/models.py b/api/app/account/models.py index 5380f19..a3866a3 100644 --- a/api/app/account/models.py +++ b/api/app/account/models.py @@ -2,16 +2,17 @@ from uuid import UUID, uuid4 from enum import Enum from sqlmodel import Field, SQLModel, select +from pydantic import Field as PydField from category.models import CategoryRead class AccountType(Enum): - Asset = "Asset" # < Denotes a generic asset account. Checkings = "Checkings" # < Standard checking account Savings = "Savings" # < Typical savings account Cash = "Cash" # < Denotes a shoe-box or pillowcase stuffed with cash + Liability = "Liability" # < Denotes a generic liability account. CreditCard = "CreditCard" # < Credit card accounts Loan = "Loan" # < Loan and mortgage accounts (liability) @@ -69,10 +70,19 @@ class Account(AccountBaseId, table=True): session.commit() class AccountRead(AccountBaseId): - default_category: CategoryRead + # default_category: CategoryRead + pass class AccountWrite(AccountBase): - pass + default_category_id: UUID = PydField(default=None, json_schema_extra={ + "foreign_key": { + "reference": { + "resource": "categories", + "schema": "CategoryRead", + "label": "name" + } + } + }) class AccountCreate(AccountWrite): pass diff --git a/gui/app/src/common/crud/crud-form.tsx b/gui/app/src/common/crud/crud-form.tsx index e437996..b38b478 100644 --- a/gui/app/src/common/crud/crud-form.tsx +++ b/gui/app/src/common/crud/crud-form.tsx @@ -1,8 +1,11 @@ import validator from "@rjsf/validator-ajv8"; import Form from "@rjsf/mui"; +import { RegistryWidgetsType } from "@rjsf/utils"; import { useEffect, useState } from "react"; import { jsonschemaProvider } from "../../providers/jsonschema-provider"; import { useForm } from "@refinedev/core"; +//import TextWidget from "@rjsf/core/src/components/widgets/TextWidget"; +import CrudTextWidget from "./widgets/crud-text-widget"; type Props = { schemaName: string, @@ -11,6 +14,8 @@ type Props = { //onSubmit: (data: IChangeEvent, event: FormEvent) => void } +const customWidgets: RegistryWidgetsType = { TextWidget: CrudTextWidget }; + export const CrudForm: React.FC = ({schemaName, resource, id}) => { const { onFinish, query, formLoading } = useForm({ resource: resource, @@ -47,6 +52,7 @@ export const CrudForm: React.FC = ({schemaName, resource, id}) => { onSubmit={(e) => onFinish(e.formData)} validator={validator} omitExtraData={true} + widgets={customWidgets} /> ) } \ No newline at end of file diff --git a/gui/app/src/common/crud/widgets/crud-text-widget.tsx b/gui/app/src/common/crud/widgets/crud-text-widget.tsx new file mode 100644 index 0000000..094ce65 --- /dev/null +++ b/gui/app/src/common/crud/widgets/crud-text-widget.tsx @@ -0,0 +1,15 @@ +import TextWidget from "@rjsf/core/lib/components/widgets/TextWidget"; +import {FormContextType, getTemplate, RJSFSchema, StrictRJSFSchema, WidgetProps} from "@rjsf/utils"; + +import {ForeignKeyWidget} from "./foreign-key"; + + +export default function CrudTextWidget( + props: WidgetProps +) { + if (props.schema.hasOwnProperty("foreign_key")) { + return (); + }else { + return (); + } +} \ No newline at end of file diff --git a/gui/app/src/common/crud/widgets/foreign-key.tsx b/gui/app/src/common/crud/widgets/foreign-key.tsx new file mode 100644 index 0000000..756ebc3 --- /dev/null +++ b/gui/app/src/common/crud/widgets/foreign-key.tsx @@ -0,0 +1,60 @@ +import { RJSFSchema, UiSchema, WidgetProps, RegistryWidgetsType } from '@rjsf/utils'; +import validator from '@rjsf/validator-ajv8'; +import {Autocomplete, AutocompleteRenderInputParams, debounce} from "@mui/material"; +import {useAutocomplete} from "@refinedev/mui"; +import { Controller } from "react-hook-form"; +import {useState, useEffect, useCallback} from "react"; +import TextField from "@mui/material/TextField"; +import {axiosInstance} from "@refinedev/simple-rest"; +import {useList} from "@refinedev/core"; + +export const ForeignKeyWidget = (props: WidgetProps) => { + const [inputValue, setInputValue] = useState(""); + const [selectedValue, setSelectedValue] = useState(null); + + const resource = props.schema.foreign_key.reference.resource + + const { data, isLoading } = useList({ + resource: resource, + pagination: { current: 1, pageSize: 10 }, + sorters: [{ field: "name", order: "asc" }], + filters: [{ field: "name", operator: "contains", value: "input" }], + }); + + const options = data?.data || []; + + // const fetchOptions = async (input: string) => { + // try { + // + // } catch (error) { + // console.error("Error fetching options:", error); + // } + // }; + + // // Debounced version of the fetch function + // const debouncedFetch = useCallback(debounce(fetchOptions, 300), []); + // // Trigger fetch whenever the inputValue changes + // useEffect(() => { + // if (inputValue) { + // debouncedFetch(inputValue); + // } else { + // setOptions([]); // Clear options when input is empty + // } + // }, [inputValue, debouncedFetch]); + + + return ( + setSelectedValue(String(newValue))} + inputValue={inputValue} + onInputChange={(event, newInputValue) => setInputValue(newInputValue)} + options={options} + getOptionLabel={(option) => option.name} + loading={isLoading} + renderInput={(params) => ( + + )} + /> + ); +}; \ No newline at end of file