Skip to content

Commit

Permalink
add UI for document types filtering/sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
ciur committed Jan 25, 2025
1 parent a5ab556 commit 7405bbe
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 48 deletions.
2 changes: 1 addition & 1 deletion papermerge/core/features/custom_fields/db/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def get_custom_fields(
user_id: uuid.UUID,
page_size: int,
page_number: int,
order_by: str,
filter: str,
order_by: str = "name",
) -> schema.PaginatedResponse[schema.CustomField]:
stmt_total_cf = select(func.count(orm.CustomField.id)).where(
orm.CustomField.user_id == user_id
Expand Down
11 changes: 3 additions & 8 deletions papermerge/core/features/custom_fields/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from enum import Enum
from fastapi import Query
from pydantic import BaseModel

from papermerge.core.types import PaginatedQueryParams as BaseParams


class OrderBy(str, Enum):
Expand All @@ -10,10 +10,5 @@ class OrderBy(str, Enum):
type_desc = "-type"


class PaginatedQueryParams(BaseModel):
page_size: int = Query(5, ge=1, description="Number of items per page")
page_number: int = Query(
1, ge=1, description="Page number. It is first, second etc. page?"
)
class PaginatedQueryParams(BaseParams):
order_by: OrderBy | None = None
filter: str | None = None
23 changes: 22 additions & 1 deletion papermerge/core/features/document_types/db/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,43 @@ def get_document_types_without_pagination(
return items


ORDER_BY_MAP = {
"name": orm.DocumentType.name.asc(),
"-name": orm.DocumentType.name.desc(),
}


def get_document_types(
session: Session, user_id: uuid.UUID, page_size: int, page_number: int
session: Session,
user_id: uuid.UUID,
page_size: int,
page_number: int,
filter: str | None = None,
order_by: str = "name",
) -> schema.PaginatedResponse[schema.DocumentType]:

stmt_total_doc_types = select(func.count(DocumentType.id)).where(
DocumentType.user_id == user_id
)
if filter:
stmt_total_doc_types = stmt_total_doc_types.where(
orm.DocumentType.name.icontains(filter)
)
total_doc_types = session.execute(stmt_total_doc_types).scalar()
order_by_value = ORDER_BY_MAP.get(order_by, orm.CustomField.name.asc())

offset = page_size * (page_number - 1)

stmt = (
select(DocumentType)
.where(DocumentType.user_id == user_id)
.limit(page_size)
.offset(offset)
.order_by(order_by_value)
)
if filter:
stmt = stmt.where(orm.DocumentType.name.icontains(filter))

db_items = session.scalars(stmt).all()
items = [schema.DocumentType.model_validate(db_item) for db_item in db_items]

Expand Down
6 changes: 4 additions & 2 deletions papermerge/core/features/document_types/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from papermerge.core.features.auth import get_current_user
from papermerge.core.features.auth import scopes
from papermerge.core.routers.common import OPEN_API_GENERIC_JSON_DETAIL
from papermerge.core.routers.params import CommonQueryParams
from papermerge.core.features.users import schema as users_schema
from .types import PaginatedQueryParams

router = APIRouter(
prefix="/document-types",
Expand Down Expand Up @@ -46,7 +46,7 @@ def get_document_types(
user: Annotated[
users_schema.User, Security(get_current_user, scopes=[scopes.CUSTOM_FIELD_VIEW])
],
params: CommonQueryParams = Depends(),
params: PaginatedQueryParams = Depends(),
) -> schema.PaginatedResponse[schema.DocumentType]:
"""Get all (paginated) document types
Expand All @@ -58,6 +58,8 @@ def get_document_types(
user_id=user.id,
page_size=params.page_size,
page_number=params.page_number,
order_by=params.order_by,
filter=params.filter,
)

return paginated_response
Expand Down
12 changes: 12 additions & 0 deletions papermerge/core/features/document_types/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from enum import Enum

from papermerge.core.types import PaginatedQueryParams as BaseParams


class OrderBy(str, Enum):
name_asc = "name"
name_desc = "-name"


class PaginatedQueryParams(BaseParams):
order_by: OrderBy | None = None
9 changes: 9 additions & 0 deletions papermerge/core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import date
from enum import Enum
from typing import Generic, Literal, TypeAlias, TypeVar
from fastapi import Query

from pydantic import BaseModel, ConfigDict

Expand Down Expand Up @@ -53,3 +54,11 @@ class CFVValueColumn(str, Enum):
MONETARY = 'value_monetary'
BOOLEAN = 'value_boolean'
YEARMONTH = 'value_yearmonth'


class PaginatedQueryParams(BaseModel):
page_size: int = Query(5, ge=1, description="Number of items per page")
page_number: int = Query(
1, ge=1, description="Page number. It is first, second etc. page?"
)
filter: str | None = None
28 changes: 14 additions & 14 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 14 additions & 3 deletions ui2/src/features/document-types/apiSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,20 @@ export const apiSliceWithDocTypes = apiSlice.injectEndpoints({
>({
query: ({
page_number = 1,
page_size = PAGINATION_DEFAULT_ITEMS_PER_PAGES
}: PaginatedArgs) =>
`/document-types/?page_number=${page_number}&page_size=${page_size}`,
page_size = PAGINATION_DEFAULT_ITEMS_PER_PAGES,
sort_by = "name",
filter = undefined
}: PaginatedArgs) => {
let ret

if (filter) {
ret = `/document-types/?page_number=${page_number}&page_size=${page_size}&order_by=${sort_by}`
ret += `&filter=${filter}`
} else {
ret = `/document-types/?page_number=${page_number}&page_size=${page_size}&order_by=${sort_by}`
}
return ret
},
providesTags: (
result = {page_number: 1, page_size: 1, num_pages: 1, items: []},
_error,
Expand Down
47 changes: 36 additions & 11 deletions ui2/src/features/document-types/components/ActionButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
import {useAppSelector} from "@/app/hooks"
import {selectSelectedIds} from "@/features/document-types/documentTypesSlice"
import {Group} from "@mantine/core"
import QuickFilter from "@/components/QuickFilter"
import {
selectFilterText,
selectSelectedIds
} from "@/features/document-types/documentTypesSlice"
import {Group, Loader} from "@mantine/core"
import {DeleteDocumentTypesButton} from "./DeleteButton"
import EditButton from "./EditButton"
import NewButton from "./NewButton"

export default function ActionButtons() {
interface Args {
isFetching?: boolean
onQuickFilterChange: (value: string) => void
onQuickFilterClear: () => void
}

export default function ActionButtons({
isFetching,
onQuickFilterChange,
onQuickFilterClear
}: Args) {
const selectedIds = useAppSelector(selectSelectedIds)
const filterText = useAppSelector(selectFilterText)

return (
<Group>
<NewButton />
{selectedIds.length == 1 ? (
<EditButton documentTypeId={selectedIds[0]} />
) : (
""
)}
{selectedIds.length >= 1 ? <DeleteDocumentTypesButton /> : ""}
<Group justify="space-between">
<Group>
<NewButton />
{selectedIds.length == 1 ? (
<EditButton documentTypeId={selectedIds[0]} />
) : (
""
)}
{selectedIds.length >= 1 ? <DeleteDocumentTypesButton /> : ""}
{isFetching && <Loader size={"sm"} />}
</Group>
<Group>
<QuickFilter
onChange={onQuickFilterChange}
onClear={onQuickFilterClear}
filterText={filterText}
/>
</Group>
</Group>
)
}
38 changes: 31 additions & 7 deletions ui2/src/features/document-types/components/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import Th from "@/components/TableSort/Th"
import {useGetPaginatedDocumentTypesQuery} from "@/features/document-types/apiSlice"
import {
clearSelection,
filterUpdated,
lastPageSizeUpdate,
selectLastPageSize,
selectReverseSortedByName,
selectSelectedIds,
selectSortedByName,
selectTableSortColumns,
selectionAddMany,
sortByUpdated
} from "@/features/document-types/documentTypesSlice"
import type {DocumentTypeListColumnName} from "@/features/document-types/types"
import {Center, Checkbox, Group, Loader, Stack, Table} from "@mantine/core"
import {Center, Checkbox, Loader, Stack, Table} from "@mantine/core"
import {useState} from "react"
import {useDispatch, useSelector} from "react-redux"
import ActionButtons from "./ActionButtons"
Expand All @@ -22,13 +24,16 @@ export default function DocumentTypesList() {
const selectedIds = useSelector(selectSelectedIds)
const dispatch = useDispatch()
const lastPageSize = useSelector(selectLastPageSize)
const tablerSortCols = useSelector(selectTableSortColumns)
const sortedByName = useSelector(selectSortedByName)
const reverseSortedByName = useSelector(selectReverseSortedByName)
const [page, setPage] = useState<number>(1)
const [pageSize, setPageSize] = useState<number>(10)
const {data, isLoading, isFetching} = useGetPaginatedDocumentTypesQuery({
page_number: page,
page_size: pageSize
page_size: pageSize,
sort_by: tablerSortCols.sortBy,
filter: tablerSortCols.filter
})

const onCheckAll = (checked: boolean) => {
Expand Down Expand Up @@ -63,10 +68,23 @@ export default function DocumentTypesList() {
dispatch(sortByUpdated(columnName))
}

const onQuickFilterChange = (value: string) => {
dispatch(filterUpdated(value))
setPage(1)
}

const onQuickFilterClear = () => {
dispatch(filterUpdated(undefined))
setPage(1)
}

if (isLoading || !data) {
return (
<Stack>
<ActionButtons />
<ActionButtons
onQuickFilterChange={onQuickFilterChange}
onQuickFilterClear={onQuickFilterClear}
/>
<Center>
<Loader type="bars" />
</Center>
Expand All @@ -77,7 +95,10 @@ export default function DocumentTypesList() {
if (data.items.length == 0) {
return (
<div>
<ActionButtons />
<ActionButtons
onQuickFilterChange={onQuickFilterChange}
onQuickFilterClear={onQuickFilterClear}
/>
<Empty />
</div>
)
Expand All @@ -88,9 +109,12 @@ export default function DocumentTypesList() {

return (
<Stack>
<Group>
<ActionButtons /> {isFetching && <Loader size={"sm"} />}
</Group>
<ActionButtons
isFetching={isFetching}
onQuickFilterChange={onQuickFilterChange}
onQuickFilterClear={onQuickFilterClear}
/>

<Table>
<Table.Thead>
<Table.Tr>
Expand Down
Loading

0 comments on commit 7405bbe

Please sign in to comment.