Client: Filament name filtering/sorting now works

This commit is contained in:
Donkie
2023-09-16 10:56:27 +02:00
parent 6f94c17050
commit dc5e3a0fbd
5 changed files with 62 additions and 48 deletions

View File

@@ -18,6 +18,7 @@ dayjs.extend(utc);
interface BaseColumnProps<Obj> { interface BaseColumnProps<Obj> {
id: keyof Obj & string; id: keyof Obj & string;
dataId?: keyof Obj & string;
i18ncat?: string; i18ncat?: string;
i18nkey?: string; i18nkey?: string;
dataSource: Obj[]; dataSource: Obj[];
@@ -50,12 +51,15 @@ function Column<Obj>(props: BaseColumnProps<Obj> & FilteredColumnProps & CustomC
dataIndex: props.id, dataIndex: props.id,
title: t(props.i18nkey ?? `${props.i18ncat}.fields.${props.id}`), title: t(props.i18nkey ?? `${props.i18ncat}.fields.${props.id}`),
sorter: true, sorter: true,
sortOrder: getSortOrderForField(typedSorters, props.id), sortOrder: getSortOrderForField(typedSorters, props.dataId ?? props.id),
filterMultiple: props.allowMultipleFilters ?? true, filterMultiple: props.allowMultipleFilters ?? true,
}; };
if (props.filters && props.filteredValue) { if (props.filters && props.filteredValue) {
columnProps.filters = props.filters; columnProps.filters = props.filters;
columnProps.filteredValue = props.filteredValue; columnProps.filteredValue = props.filteredValue;
if (props.dataId) {
columnProps.key = props.dataId;
}
} }
if (props.render) { if (props.render) {
columnProps.render = props.render; columnProps.render = props.render;
@@ -80,7 +84,7 @@ export function RichColumn<Obj>(props: BaseColumnProps<Obj>) {
} }
interface FilteredQueryColumnProps<Obj> extends BaseColumnProps<Obj> { interface FilteredQueryColumnProps<Obj> extends BaseColumnProps<Obj> {
filterValueQuery: UseQueryResult<string[]>; filterValueQuery: UseQueryResult<string[] | ColumnFilterItem[]>;
allowMultipleFilters?: boolean; allowMultipleFilters?: boolean;
} }
@@ -90,10 +94,13 @@ export function FilteredQueryColumn<Obj>(props: FilteredQueryColumnProps<Obj>) {
let filters: ColumnFilterItem[] = []; let filters: ColumnFilterItem[] = [];
if (query.data) { if (query.data) {
filters = query.data.map((item) => { filters = query.data.map((item) => {
return { if (typeof item === "string") {
text: item, return {
value: item, text: item,
}; value: item,
};
}
return item;
}); });
} }
filters.push({ filters.push({
@@ -102,7 +109,7 @@ export function FilteredQueryColumn<Obj>(props: FilteredQueryColumnProps<Obj>) {
}); });
const typedFilters = typeFilters<Obj>(props.tableState.filters); const typedFilters = typeFilters<Obj>(props.tableState.filters);
const filteredValue = getFiltersForField(typedFilters, props.id); const filteredValue = getFiltersForField(typedFilters, props.dataId ?? props.id);
return Column({ ...props, filters, filteredValue }); return Column({ ...props, filters, filteredValue });
} }
@@ -160,10 +167,13 @@ export function SpoolIconColumn<Obj>(props: SpoolIconColumnProps<Obj>) {
let filters: ColumnFilterItem[] = []; let filters: ColumnFilterItem[] = [];
if (query.data) { if (query.data) {
filters = query.data.map((item) => { filters = query.data.map((item) => {
return { if (typeof item === "string") {
text: item, return {
value: item, text: item,
}; value: item,
};
}
return item;
}); });
} }
filters.push({ filters.push({
@@ -172,7 +182,7 @@ export function SpoolIconColumn<Obj>(props: SpoolIconColumnProps<Obj>) {
}); });
const typedFilters = typeFilters<Obj>(props.tableState.filters); const typedFilters = typeFilters<Obj>(props.tableState.filters);
const filteredValue = getFiltersForField(typedFilters, props.id); const filteredValue = getFiltersForField(typedFilters, props.dataId ?? props.id);
return Column({ return Column({
...props, ...props,

View File

@@ -1,10 +1,11 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { IFilament } from "../pages/filaments/model"; import { IFilament } from "../pages/filaments/model";
import { IVendor } from "../pages/vendors/model"; import { IVendor } from "../pages/vendors/model";
import { ColumnFilterItem } from "antd/es/table/interface";
export function useSpoolmanFilamentFullNames() { export function useSpoolmanFilamentFilter() {
const apiEndpoint = import.meta.env.VITE_APIURL; const apiEndpoint = import.meta.env.VITE_APIURL;
return useQuery<IFilament[], unknown, string[]>({ return useQuery<IFilament[], unknown, ColumnFilterItem[]>({
queryKey: ["filaments"], queryKey: ["filaments"],
queryFn: async () => { queryFn: async () => {
const response = await fetch(apiEndpoint + "/filament"); const response = await fetch(apiEndpoint + "/filament");
@@ -15,20 +16,28 @@ export function useSpoolmanFilamentFullNames() {
}, },
select: (data) => { select: (data) => {
// Concatenate vendor name and filament name // Concatenate vendor name and filament name
let names = data const names = data
// Remove empty names
.filter((filament) => { .filter((filament) => {
return filament.name !== null && filament.name !== undefined && filament.name !== ""; return filament.name !== null && filament.name !== undefined && filament.name !== "";
}) })
// Transform to ColumnFilterItem
.map((filament) => { .map((filament) => {
let name = "";
if (filament.vendor?.name) { if (filament.vendor?.name) {
return `${filament.vendor.name} - ${filament.name ?? "<unknown>"}`; name = `${filament.vendor.name} - ${filament.name ?? "<unknown>"}`;
} else { } else {
return `${filament.name ?? "<unknown>"}`; name = `${filament.name ?? "<unknown>"}`;
} }
return {
text: name,
value: filament.id,
};
}) })
.sort(); // Remove duplicates
// Remove duplicates .filter((item, index, self) => self.findIndex((t) => t.value === item.value) === index)
names = [...new Set(names)]; // Sort by name
.sort((a, b) => a.text.localeCompare(b.text));
return names; return names;
}, },
}); });

View File

@@ -5,7 +5,7 @@ import { FilteredQueryColumn, SortedColumn, SpoolIconColumn } from "./column";
import { TableState } from "../utils/saveload"; import { TableState } from "../utils/saveload";
import { useTable } from "@refinedev/antd"; import { useTable } from "@refinedev/antd";
import { t } from "i18next"; import { t } from "i18next";
import { useSpoolmanFilamentFullNames, useSpoolmanMaterials } from "./otherModels"; import { useSpoolmanFilamentFilter, useSpoolmanMaterials } from "./otherModels";
interface Props { interface Props {
visible: boolean; visible: boolean;
@@ -15,7 +15,8 @@ interface Props {
} }
interface ISpoolCollapsed extends ISpool { interface ISpoolCollapsed extends ISpool {
"filament.name": string; combined_name: string;
"filament.id": number;
"filament.material"?: string; "filament.material"?: string;
} }
@@ -37,10 +38,10 @@ const SpoolSelectModal: React.FC<Props> = ({ visible, description, onCancel, onC
pageSize: 10, pageSize: 10,
}, },
sorters: { sorters: {
mode: "off", mode: "server",
}, },
filters: { filters: {
mode: "off", mode: "server",
}, },
}); });
@@ -63,7 +64,8 @@ const SpoolSelectModal: React.FC<Props> = ({ visible, description, onCancel, onC
} }
return { return {
...element, ...element,
"filament.name": filament_name, combined_name: filament_name,
"filament.id": element.filament.id,
"filament.material": element.filament.material, "filament.material": element.filament.material,
}; };
}), }),
@@ -128,12 +130,13 @@ const SpoolSelectModal: React.FC<Props> = ({ visible, description, onCancel, onC
tableState, tableState,
})} })}
{SpoolIconColumn({ {SpoolIconColumn({
id: "filament.name", id: "combined_name",
i18ncat: "spool", dataId: "filament.id",
i18nkey: "spool.fields.filament_name",
color: (record: ISpoolCollapsed) => record.filament.color_hex, color: (record: ISpoolCollapsed) => record.filament.color_hex,
dataSource, dataSource,
tableState, tableState,
filterValueQuery: useSpoolmanFilamentFullNames(), filterValueQuery: useSpoolmanFilamentFilter(),
})} })}
{FilteredQueryColumn({ {FilteredQueryColumn({
id: "filament.material", id: "filament.material",

View File

@@ -125,7 +125,7 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
menu={{ menu={{
items: allColumns.map((column_id) => ({ items: allColumns.map((column_id) => ({
key: column_id, key: column_id,
label: t(`filament.fields.${column_id}`), label: t(`filament.fields.${column_id.replace(".", "_")}`),
})), })),
selectedKeys: showColumns, selectedKeys: showColumns,
selectable: true, selectable: true,
@@ -159,7 +159,6 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
dataSource, dataSource,
tableState, tableState,
filterValueQuery: useSpoolmanVendors(), filterValueQuery: useSpoolmanVendors(),
allowMultipleFilters: false,
})} })}
{SpoolIconColumn({ {SpoolIconColumn({
id: "name", id: "name",
@@ -168,7 +167,6 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
dataSource, dataSource,
tableState, tableState,
filterValueQuery: useSpoolmanFilamentNames(), filterValueQuery: useSpoolmanFilamentNames(),
allowMultipleFilters: false,
})} })}
{FilteredQueryColumn({ {FilteredQueryColumn({
id: "material", id: "material",
@@ -176,7 +174,6 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
dataSource, dataSource,
tableState, tableState,
filterValueQuery: useSpoolmanMaterials(), filterValueQuery: useSpoolmanMaterials(),
allowMultipleFilters: false,
})} })}
{SortedColumn({ {SortedColumn({
id: "price", id: "price",
@@ -222,7 +219,6 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
dataSource, dataSource,
tableState, tableState,
filterValueQuery: useSpoolmanArticleNumbers(), filterValueQuery: useSpoolmanArticleNumbers(),
allowMultipleFilters: false,
})} })}
{NumberColumn({ {NumberColumn({
id: "settings_extruder_temp", id: "settings_extruder_temp",

View File

@@ -18,7 +18,7 @@ import {
import { setSpoolArchived } from "./functions"; import { setSpoolArchived } from "./functions";
import SelectAndPrint from "../../components/selectAndPrintDialog"; import SelectAndPrint from "../../components/selectAndPrintDialog";
import { import {
useSpoolmanFilamentFullNames, useSpoolmanFilamentFilter,
useSpoolmanLocations, useSpoolmanLocations,
useSpoolmanLotNumbers, useSpoolmanLotNumbers,
useSpoolmanMaterials, useSpoolmanMaterials,
@@ -29,7 +29,8 @@ dayjs.extend(utc);
const { confirm } = Modal; const { confirm } = Modal;
interface ISpoolCollapsed extends ISpool { interface ISpoolCollapsed extends ISpool {
"filament.name": string; combined_name: string; // Eg. "Prusa - PLA Red"
"filament.id": number;
"filament.material"?: string; "filament.material"?: string;
} }
@@ -71,7 +72,7 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
// Create state for the columns to show // Create state for the columns to show
const allColumns: (keyof ISpoolCollapsed & string)[] = [ const allColumns: (keyof ISpoolCollapsed & string)[] = [
"id", "id",
"filament.name", "combined_name",
"filament.material", "filament.material",
"used_weight", "used_weight",
"remaining_weight", "remaining_weight",
@@ -110,16 +111,14 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
} }
return { return {
...element, ...element,
"filament.name": filament_name, combined_name: filament_name,
"filament.id": element.filament.id,
"filament.material": element.filament.material, "filament.material": element.filament.material,
}; };
}), }),
[tableProps.dataSource] [tableProps.dataSource]
); );
// Filter and sort the dataSource
const filteredDataSource = dataSource;
// Function for opening an ant design modal that asks for confirmation for archiving a spool // Function for opening an ant design modal that asks for confirmation for archiving a spool
const archiveSpool = async (spool: ISpoolCollapsed, archive: boolean) => { const archiveSpool = async (spool: ISpoolCollapsed, archive: boolean) => {
await setSpoolArchived(spool, archive); await setSpoolArchived(spool, archive);
@@ -182,7 +181,7 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
menu={{ menu={{
items: allColumns.map((column) => ({ items: allColumns.map((column) => ({
key: column, key: column,
label: t(`spool.fields.${column}`), label: t(`spool.fields.${column.replace(".", "_")}`),
})), })),
selectedKeys: showColumns, selectedKeys: showColumns,
selectable: true, selectable: true,
@@ -205,7 +204,7 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
> >
<Table <Table
{...tableProps} {...tableProps}
dataSource={filteredDataSource} dataSource={dataSource}
rowKey="id" rowKey="id"
// Make archived rows greyed out // Make archived rows greyed out
onRow={(record) => { onRow={(record) => {
@@ -228,13 +227,13 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
tableState, tableState,
})} })}
{SpoolIconColumn({ {SpoolIconColumn({
id: "filament.name", id: "combined_name",
i18nkey: "spool.fields.filament_name", i18nkey: "spool.fields.filament_name",
color: (record: ISpoolCollapsed) => record.filament.color_hex, color: (record: ISpoolCollapsed) => record.filament.color_hex,
dataSource, dataSource,
tableState, tableState,
filterValueQuery: useSpoolmanFilamentFullNames(), dataId: "filament.id",
allowMultipleFilters: false, filterValueQuery: useSpoolmanFilamentFilter(),
})} })}
{FilteredQueryColumn({ {FilteredQueryColumn({
id: "filament.material", id: "filament.material",
@@ -242,7 +241,6 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
dataSource, dataSource,
tableState, tableState,
filterValueQuery: useSpoolmanMaterials(), filterValueQuery: useSpoolmanMaterials(),
allowMultipleFilters: false,
})} })}
{NumberColumn({ {NumberColumn({
id: "used_weight", id: "used_weight",
@@ -284,7 +282,6 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
dataSource, dataSource,
tableState, tableState,
filterValueQuery: useSpoolmanLocations(), filterValueQuery: useSpoolmanLocations(),
allowMultipleFilters: false,
})} })}
{FilteredQueryColumn({ {FilteredQueryColumn({
id: "lot_nr", id: "lot_nr",
@@ -292,7 +289,6 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
dataSource, dataSource,
tableState, tableState,
filterValueQuery: useSpoolmanLotNumbers(), filterValueQuery: useSpoolmanLotNumbers(),
allowMultipleFilters: false,
})} })}
{DateColumn({ {DateColumn({
id: "first_used", id: "first_used",