Client: Filament name filtering/sorting now works
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user