Client: Support spool archiving in frontend
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
"undo": "Undo",
|
||||
"import": "Import",
|
||||
"clone": "Clone",
|
||||
"archive": "Archive",
|
||||
"unArchive": "Unarchive",
|
||||
"hideArchived": "Hide Archived",
|
||||
"showArchived": "Show Archived",
|
||||
"notAccessTitle": "You don't have permission to access",
|
||||
"hideColumns": "Hide Columns",
|
||||
"clearFilters": "Clear Filters"
|
||||
@@ -41,6 +45,8 @@
|
||||
"loading": "Loading",
|
||||
"version": "Version",
|
||||
"unknown": "Unknown",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"tags": {
|
||||
"clone": "Clone"
|
||||
},
|
||||
@@ -63,7 +69,8 @@
|
||||
"first_used": "First Used",
|
||||
"last_used": "Last Used",
|
||||
"registered": "Registered",
|
||||
"comment": "Comment"
|
||||
"comment": "Comment",
|
||||
"archived": "Archived"
|
||||
},
|
||||
"fields_help": {
|
||||
"used_weight": "How much filament has been used from the spool. A new spool should have 0g used.",
|
||||
@@ -75,7 +82,11 @@
|
||||
"clone": "Clone Spool",
|
||||
"edit": "Edit Spool",
|
||||
"list": "Spools",
|
||||
"show": "Show Spool"
|
||||
"show": "Show Spool",
|
||||
"archive": "Archive Spool"
|
||||
},
|
||||
"messages": {
|
||||
"archive": "Are you sure you want to archive this spool?"
|
||||
}
|
||||
},
|
||||
"filament": {
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
"undo": "Ångra",
|
||||
"import": "Importera",
|
||||
"clone": "Klona",
|
||||
"archive": "Arkivera",
|
||||
"unArchive": "Avarkivera",
|
||||
"hideArchived": "Dölj arkiverade",
|
||||
"showArchived": "Visa arkiverade",
|
||||
"notAccessTitle": "Du har inte tillgång till denna sida",
|
||||
"hideColumns": "Dölj kolumner",
|
||||
"clearFilters": "Rensa filter"
|
||||
@@ -41,6 +45,8 @@
|
||||
"loading": "Laddar...",
|
||||
"version": "Version",
|
||||
"unknown": "Okänd",
|
||||
"yes": "Ja",
|
||||
"no": "Nej",
|
||||
"tags": {
|
||||
"clone": "Klona"
|
||||
},
|
||||
@@ -63,7 +69,8 @@
|
||||
"first_used": "Användes först",
|
||||
"last_used": "Användes senast",
|
||||
"registered": "Registrerad",
|
||||
"comment": "Kommentar"
|
||||
"comment": "Kommentar",
|
||||
"archived": "Arkiverad"
|
||||
},
|
||||
"fields_help": {
|
||||
"used_weight": "Hur mycket filament som använts på spolen. En ny spole har värdet 0g.",
|
||||
@@ -75,7 +82,11 @@
|
||||
"clone": "Klona spole",
|
||||
"edit": "Ändra spole",
|
||||
"list": "Spolar",
|
||||
"show": "Visa spole"
|
||||
"show": "Visa spole",
|
||||
"archive": "Arkivera spole"
|
||||
},
|
||||
"messages": {
|
||||
"archive": "Är du säker på att du vill arkivera denna spole?"
|
||||
}
|
||||
},
|
||||
"filament": {
|
||||
|
||||
17
client/src/pages/spools/functions.ts
Normal file
17
client/src/pages/spools/functions.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { ISpool } from "./model";
|
||||
|
||||
|
||||
export async function setSpoolArchived(spool: ISpool, archived: boolean) {
|
||||
const apiEndpoint = import.meta.env.VITE_APIURL;
|
||||
const init: RequestInit = {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
archived: archived,
|
||||
}),
|
||||
};
|
||||
const request = new Request(apiEndpoint + "/spool/" + spool.id);
|
||||
await fetch(request, init);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import {
|
||||
IResourceComponentsProps,
|
||||
BaseRecord,
|
||||
useInvalidate,
|
||||
useTranslate,
|
||||
} from "@refinedev/core";
|
||||
import {
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
ShowButton,
|
||||
CloneButton,
|
||||
} from "@refinedev/antd";
|
||||
import { Table, Space, Button, Dropdown } from "antd";
|
||||
import { Table, Space, Button, Dropdown, Modal } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { genericSorter, typeSorters } from "../../utils/sorting";
|
||||
@@ -20,9 +20,15 @@ import { ISpool } from "./model";
|
||||
import {
|
||||
TableState,
|
||||
useInitialTableState,
|
||||
useShowArchive,
|
||||
useStoreInitialState,
|
||||
} from "../../utils/saveload";
|
||||
import { EditOutlined, FilterOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
EditOutlined,
|
||||
FilterOutlined,
|
||||
InboxOutlined,
|
||||
ToTopOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import {
|
||||
DateColumn,
|
||||
FilteredColumn,
|
||||
@@ -30,9 +36,12 @@ import {
|
||||
SortedColumn,
|
||||
SpoolIconColumn,
|
||||
} from "../../components/column";
|
||||
import { setSpoolArchived } from "./functions";
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
const { confirm } = Modal;
|
||||
|
||||
interface ISpoolCollapsed extends ISpool {
|
||||
filament_name: string;
|
||||
material?: string;
|
||||
@@ -40,10 +49,14 @@ interface ISpoolCollapsed extends ISpool {
|
||||
|
||||
export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
||||
const t = useTranslate();
|
||||
const invalidate = useInvalidate();
|
||||
|
||||
// Load initial state
|
||||
const initialState = useInitialTableState("spoolList");
|
||||
|
||||
// State for the switch to show archived spools
|
||||
const [showArchived, setShowArchived] = useShowArchive("spoolList");
|
||||
|
||||
// Fetch data from the API
|
||||
const {
|
||||
tableProps,
|
||||
@@ -56,6 +69,11 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
||||
setCurrent,
|
||||
setPageSize,
|
||||
} = useTable<ISpool>({
|
||||
meta: {
|
||||
queryParams: {
|
||||
["allow_archived"]: showArchived,
|
||||
},
|
||||
},
|
||||
syncWithLocation: false,
|
||||
pagination: {
|
||||
mode: "off", // Perform pagination in antd's Table instead. Otherwise client-side sorting/filtering doesn't work.
|
||||
@@ -138,10 +156,49 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
||||
return filtered;
|
||||
}, [dataSource, typedFilters, typedSorters]);
|
||||
|
||||
// Function for opening an ant design modal that asks for confirmation for archiving a spool
|
||||
const archiveSpool = async (spool: ISpoolCollapsed, archive: boolean) => {
|
||||
await setSpoolArchived(spool, archive);
|
||||
invalidate({
|
||||
resource: "spool",
|
||||
id: spool.id,
|
||||
invalidates: ["list", "detail"],
|
||||
});
|
||||
};
|
||||
|
||||
const archiveSpoolPopup = async (spool: ISpoolCollapsed) => {
|
||||
// If the spool has no remaining weight, archive it immediately since it's likely not a mistake
|
||||
if (spool.remaining_weight && spool.remaining_weight == 0) {
|
||||
await archiveSpool(spool, true);
|
||||
} else {
|
||||
confirm({
|
||||
title: t("spool.titles.archive"),
|
||||
content: t("spool.messages.archive"),
|
||||
okText: t("buttons.archive"),
|
||||
okType: "primary",
|
||||
cancelText: t("buttons.cancel"),
|
||||
onOk() {
|
||||
return archiveSpool(spool, true);
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<List
|
||||
headerButtons={({ defaultButtons }) => (
|
||||
<>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<InboxOutlined />}
|
||||
onClick={() => {
|
||||
setShowArchived(!showArchived);
|
||||
}}
|
||||
>
|
||||
{showArchived
|
||||
? t("buttons.hideArchived")
|
||||
: t("buttons.showArchived")}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<FilterOutlined />}
|
||||
@@ -192,6 +249,19 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
||||
},
|
||||
}}
|
||||
rowKey="id"
|
||||
// Make archived rows greyed out
|
||||
onRow={(record) => {
|
||||
if (record.archived) {
|
||||
return {
|
||||
style: {
|
||||
fontStyle: "italic",
|
||||
color: "#999",
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}}
|
||||
>
|
||||
{SortedColumn({
|
||||
id: "id",
|
||||
@@ -284,7 +354,7 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
||||
})}
|
||||
<Table.Column
|
||||
title={t("table.actions")}
|
||||
render={(_, record: BaseRecord) => (
|
||||
render={(_, record: ISpoolCollapsed) => (
|
||||
<Space>
|
||||
<EditButton
|
||||
hideText
|
||||
@@ -304,6 +374,21 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
||||
size="small"
|
||||
recordItemId={record.id}
|
||||
/>
|
||||
{record.archived ? (
|
||||
<Button
|
||||
icon={<ToTopOutlined />}
|
||||
title={t("buttons.unArchive")}
|
||||
size="small"
|
||||
onClick={() => archiveSpool(record, false)}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
icon={<InboxOutlined />}
|
||||
title={t("buttons.archive")}
|
||||
size="small"
|
||||
onClick={() => archiveSpoolPopup(record)}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -13,4 +13,5 @@ export interface ISpool {
|
||||
location?: string;
|
||||
lot_nr?: string;
|
||||
comment?: string;
|
||||
archived: boolean;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,8 @@ export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
|
||||
<TextField value={record?.lot_nr} />
|
||||
<Title level={5}>{t("spool.fields.comment")}</Title>
|
||||
<TextField value={record?.comment} />
|
||||
<Title level={5}>{t("spool.fields.archived")}</Title>
|
||||
<TextField value={record?.archived ? t("yes") : t("no")} />
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -59,3 +59,18 @@ export function useStoreInitialState(tableId: string, state: TableState) {
|
||||
}
|
||||
}, [tableId, state.showColumns]);
|
||||
}
|
||||
|
||||
export function useShowArchive(tableId: string) {
|
||||
const [showArchive, setShowArchive] = React.useState(() => {
|
||||
const savedShowArchive = isLocalStorageAvailable ? localStorage.getItem(`${tableId}-showArchive`) : null;
|
||||
return savedShowArchive ? JSON.parse(savedShowArchive) : false;
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isLocalStorageAvailable) {
|
||||
localStorage.setItem(`${tableId}-showArchive`, JSON.stringify(showArchive));
|
||||
}
|
||||
}, [tableId, showArchive]);
|
||||
|
||||
return [showArchive, setShowArchive] as const;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user