diff --git a/api/app/account/models.py b/api/app/account/models.py index a3866a3..3af720a 100644 --- a/api/app/account/models.py +++ b/api/app/account/models.py @@ -1,10 +1,10 @@ from uuid import UUID, uuid4 from enum import Enum -from sqlmodel import Field, SQLModel, select +from sqlmodel import Field, SQLModel, select, Relationship from pydantic import Field as PydField -from category.models import CategoryRead +from category.models import CategoryRead, Category class AccountType(Enum): @@ -38,6 +38,7 @@ class AccountBaseId(AccountBase): id: UUID | None = Field(default_factory=uuid4, primary_key=True) class Account(AccountBaseId, table=True): + default_category: Category | None = Relationship() @classmethod def create(cls, account, session): @@ -50,7 +51,7 @@ class Account(AccountBaseId, table=True): @classmethod def list(cls): - return select(Account) + return select(Account).join(Category) @classmethod def get(cls, session, account_id): @@ -70,8 +71,7 @@ class Account(AccountBaseId, table=True): session.commit() class AccountRead(AccountBaseId): - # default_category: CategoryRead - pass + default_category: CategoryRead class AccountWrite(AccountBase): default_category_id: UUID = PydField(default=None, json_schema_extra={ diff --git a/api/app/account/routes.py b/api/app/account/routes.py index f26b9b4..371e0a4 100644 --- a/api/app/account/routes.py +++ b/api/app/account/routes.py @@ -12,8 +12,8 @@ router = APIRouter() @router.post("") def create_account(account: AccountCreate, session: SessionDep, current_user=Depends(get_current_user)) -> AccountRead: - Account.create(account, session) - return account + result = Account.create(account, session) + return result @router.get("") def read_accounts(session: SessionDep, current_user=Depends(get_current_user)) -> Page[AccountRead]: diff --git a/api/app/category/routes.py b/api/app/category/routes.py index e5aff90..0129007 100644 --- a/api/app/category/routes.py +++ b/api/app/category/routes.py @@ -4,7 +4,6 @@ from fastapi import APIRouter, HTTPException, Depends from fastapi_filter import FilterDepends from fastapi_pagination import Page from fastapi_pagination.ext.sqlmodel import paginate -from pydantic import BaseModel from category.models import Category, CategoryCreate, CategoryRead, CategoryUpdate, CategoryFilters from db import SessionDep diff --git a/api/app/user/manager.py b/api/app/user/manager.py index 185889c..6fd0630 100644 --- a/api/app/user/manager.py +++ b/api/app/user/manager.py @@ -6,7 +6,7 @@ from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models, ex from fastapi_users.authentication import BearerTransport, AuthenticationBackend from fastapi_users.authentication.strategy.db import AccessTokenDatabase, DatabaseStrategy -from .models import User, get_user_db, AccessToken, get_access_token_db, UserRead, UserUpdate, UserCreate +from user.models import User, get_user_db, AccessToken, get_access_token_db, UserRead, UserUpdate, UserCreate from db import get_session SECRET = "SECRET" diff --git a/api/app/user/routes.py b/api/app/user/routes.py index e1b81f9..ec5c2b8 100644 --- a/api/app/user/routes.py +++ b/api/app/user/routes.py @@ -2,8 +2,8 @@ import uuid from fastapi import APIRouter, Depends, HTTPException -from .models import User, UserCreate, UserRead, UserUpdate -from .manager import get_user_manager, get_current_user, get_current_superuser +from user.models import User, UserCreate, UserRead, UserUpdate +from user.manager import get_user_manager, get_current_user, get_current_superuser from fastapi_pagination import Page from fastapi_pagination.ext.sqlmodel import paginate diff --git a/gui/app/src/common/crud/widgets/crud-text-widget.tsx b/gui/app/src/common/crud/widgets/crud-text-widget.tsx index 1aaaf4c..49f5776 100644 --- a/gui/app/src/common/crud/widgets/crud-text-widget.tsx +++ b/gui/app/src/common/crud/widgets/crud-text-widget.tsx @@ -1,8 +1,7 @@ import TextWidget from "@rjsf/core/lib/components/widgets/TextWidget"; -import {FormContextType, getTemplate, RJSFSchema, StrictRJSFSchema, WidgetProps} from "@rjsf/utils"; - -import {ForeignKeyWidget} from "./foreign-key"; +import { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from "@rjsf/utils"; +import { ForeignKeyWidget } from "./foreign-key"; export default function CrudTextWidget( props: WidgetProps diff --git a/gui/app/src/common/crud/widgets/foreign-key.tsx b/gui/app/src/common/crud/widgets/foreign-key.tsx index 054d3e4..b7cbc57 100644 --- a/gui/app/src/common/crud/widgets/foreign-key.tsx +++ b/gui/app/src/common/crud/widgets/foreign-key.tsx @@ -1,45 +1,51 @@ -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 { WidgetProps } from '@rjsf/utils'; +import { Autocomplete } from "@mui/material"; +import { useState, useEffect } from "react"; import TextField from "@mui/material/TextField"; -import {axiosInstance} from "@refinedev/simple-rest"; -import {useList} from "@refinedev/core"; +import { useList, useOne } from "@refinedev/core"; export const ForeignKeyWidget = (props: WidgetProps) => { - const [inputValue, setInputValue] = useState(""); - const [selectedValue, setSelectedValue] = useState(null); - const [debouncedInputValue, setDebouncedInputValue] = useState(inputValue); - const resource = props.schema.foreign_key.reference.resource + const labelField = props.schema.foreign_key.reference.label + + const valueResult = useOne({ + resource: resource, + id: props.value + }); + + const [inputValue, setInputValue] = useState(""); + const [selectedValue, setSelectedValue] = useState(valueResult.data?.data || null); + const [debouncedInputValue, setDebouncedInputValue] = useState(inputValue); useEffect(() => { const handler = setTimeout(() => setDebouncedInputValue(inputValue), 300); // Adjust debounce delay as needed return () => clearTimeout(handler); }, [inputValue]); - const { data, isLoading } = useList({ + const listResult = useList({ resource: resource, pagination: { current: 1, pageSize: 10 }, filters: [{ field: "name", operator: "contains", value: debouncedInputValue }], sorters: [{ field: "name", order: "asc" }], }); - const options = data?.data || []; + const options = listResult.data?.data || []; + const isLoading = listResult.isLoading || valueResult.isLoading return ( setSelectedValue(newValue)} - inputValue={inputValue} + onChange={(event, newValue) => { + setSelectedValue(newValue) + props.onChange(newValue ? newValue.id : "") + }} + //inputValue={inputValue} onInputChange={(event, newInputValue) => setInputValue(newInputValue)} options={options} - getOptionLabel={(option) => option.name} + getOptionLabel={(option) => option ? option[labelField] : ""} loading={isLoading} renderInput={(params) => ( - + )} /> ); diff --git a/gui/app/src/providers/data-provider.tsx b/gui/app/src/providers/data-provider.tsx index 1f18d28..ea5d133 100644 --- a/gui/app/src/providers/data-provider.tsx +++ b/gui/app/src/providers/data-provider.tsx @@ -55,7 +55,7 @@ export const dataProvider: DataProvider = { if (filters && filters.length > 0) { filters.forEach((filter) => { - if ("field" in filter && filter.value.length > 0 && filter.operator === "contains") { + if ("field" in filter && filter.value && filter.operator === "contains") { params.append(filter.field + "__like", "%" + filter.value + "%"); } });