diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index d077fb83b..8b5162d20 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -177,9 +177,17 @@ "list": "Spools", "show": "Show Spool", "show_title": "[Spool #{{id}}] {{name}}", - "archive": "Archive Spool" + "archive": "Archive Spool", + "adjust": "Adjust Spool Filament" }, "form": { + "measurement_type": { + "length": "Length", + "weight": "Weight" + }, + "measurement_type_label": "Measurement Type", + "adjust_filament_help": "Here you can directly add or subtract filament from the spool. A positive value will consume filament, a negative value will add it.", + "adjust_filament_value": "Consume Amount", "new_location_prompt": "Enter a new location", "spool_updated": "This spool has been updated by someone/something else since you opened this page. Saving will overwrite those changes!" }, diff --git a/client/src/pages/spools/functions.tsx b/client/src/pages/spools/functions.tsx index 8dfba021b..93a0398d9 100644 --- a/client/src/pages/spools/functions.tsx +++ b/client/src/pages/spools/functions.tsx @@ -4,8 +4,10 @@ import { getAPIURL } from "../../utils/url"; import { ISpool } from "./model"; import { IFilament } from "../filaments/model"; import { SpoolType, useGetExternalDBFilaments } from "../../utils/queryExternalDB"; -import { useMemo } from "react"; +import { useCallback, useMemo, useState } from "react"; import { useQueries } from "@tanstack/react-query"; +import { Button, Form, InputNumber, Modal, Radio } from "antd"; +import { useForm } from "antd/es/form/Form"; export async function setSpoolArchived(spool: ISpool, archived: boolean) { const init: RequestInit = { @@ -21,6 +23,27 @@ export async function setSpoolArchived(spool: ISpool, archived: boolean) { await fetch(request, init); } +/** + * Use some spool filament from this spool. Either specify length or weight. + * @param spool The spool + * @param length The length to add/subtract from the spool, in mm + * @param weight The weight to add/subtract from the spool, in g + */ +export async function useSpoolFilament(spool: ISpool, length?: number, weight?: number) { + const init: RequestInit = { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + use_length: length, + use_weight: weight, + }), + }; + const request = new Request(`${getAPIURL()}/spool/${spool.id}/use`); + await fetch(request, init); +} + /** * Returns an array of queries using the useQueries hook from @tanstack/react-query. * Each query fetches a spool by its ID from the server. @@ -149,3 +172,67 @@ export function useGetFilamentSelectOptions() { allExternalFilaments: externalFilaments.data, }; } + +type MeasurementType = "length" | "weight"; + +export function useSpoolAdjustModal() { + const t = useTranslate(); + const [form] = useForm(); + + const [curSpool, setCurSpool] = useState(null); + const [measurementType, setMeasurementType] = useState("length"); + + const openSpoolAdjustModal = useCallback((spool: ISpool) => { + setCurSpool(spool); + }, []); + + const spoolAdjustModal = useMemo(() => { + if (curSpool === null) { + return null; + } + + const onSubmit = async () => { + if (curSpool === null) { + return; + } + + const value = form.getFieldValue("filament_value"); + if (value === undefined || value === null) { + return; + } + + if (measurementType === "length") { + await useSpoolFilament(curSpool, value, undefined); + } else { + await useSpoolFilament(curSpool, undefined, value); + } + + setCurSpool(null); + }; + + return ( + setCurSpool(null)} onOk={form.submit}> +

{t("spool.form.adjust_filament_help")}

+
+ + setMeasurementType(value as MeasurementType)} + > + {t("spool.form.measurement_type.length")} + {t("spool.form.measurement_type.weight")} + + + + + +
+
+ ); + }, [curSpool, measurementType, t]); + + return { + openSpoolAdjustModal, + spoolAdjustModal, + }; +} diff --git a/client/src/pages/spools/list.tsx b/client/src/pages/spools/list.tsx index 7497e6297..943dd8971 100644 --- a/client/src/pages/spools/list.tsx +++ b/client/src/pages/spools/list.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useCallback, useMemo } from "react"; import { IResourceComponentsProps, useInvalidate, useNavigation, useTranslate } from "@refinedev/core"; import { useTable, List } from "@refinedev/antd"; import { Table, Button, Dropdown, Modal } from "antd"; @@ -13,6 +13,7 @@ import { InboxOutlined, PlusSquareOutlined, PrinterOutlined, + ToolOutlined, ToTopOutlined, } from "@ant-design/icons"; import { @@ -26,7 +27,7 @@ import { ActionsColumn, CustomFieldColumn, } from "../../components/column"; -import { setSpoolArchived } from "./functions"; +import { setSpoolArchived, useSpoolAdjustModal } from "./functions"; import { useSpoolmanFilamentFilter, useSpoolmanLocations, @@ -102,6 +103,7 @@ export const SpoolList: React.FC = () => { const navigate = useNavigate(); const extraFields = useGetFields(EntityType.spool); const currency = useCurrency(); + const { openSpoolAdjustModal, spoolAdjustModal } = useSpoolAdjustModal(); const allColumnsWithExtraFields = [...allColumns, ...(extraFields.data?.map((field) => "extra." + field.key) ?? [])]; @@ -208,23 +210,27 @@ export const SpoolList: React.FC = () => { } const { editUrl, showUrl, cloneUrl } = useNavigation(); - const actions = (record: ISpoolCollapsed) => { - const actions: Action[] = [ - { name: t("buttons.show"), icon: , link: showUrl("spool", record.id) }, - { name: t("buttons.edit"), icon: , link: editUrl("spool", record.id) }, - { name: t("buttons.clone"), icon: , link: cloneUrl("spool", record.id) }, - ]; - if (record.archived) { - actions.push({ - name: t("buttons.unArchive"), - icon: , - onClick: () => archiveSpool(record, false), - }); - } else { - actions.push({ name: t("buttons.archive"), icon: , onClick: () => archiveSpoolPopup(record) }); - } - return actions; - }; + const actions = useCallback( + (record: ISpoolCollapsed) => { + const actions: Action[] = [ + { name: t("buttons.show"), icon: , link: showUrl("spool", record.id) }, + { name: t("buttons.edit"), icon: , link: editUrl("spool", record.id) }, + { name: t("buttons.clone"), icon: , link: cloneUrl("spool", record.id) }, + { name: t("spool.titles.adjust"), icon: , onClick: () => openSpoolAdjustModal(record) }, + ]; + if (record.archived) { + actions.push({ + name: t("buttons.unArchive"), + icon: , + onClick: () => archiveSpool(record, false), + }); + } else { + actions.push({ name: t("buttons.archive"), icon: , onClick: () => archiveSpoolPopup(record) }); + } + return actions; + }, + [t, editUrl, showUrl, cloneUrl, openSpoolAdjustModal, archiveSpool, archiveSpoolPopup] + ); const originalOnChange = tableProps.onChange; tableProps.onChange = (pagination, filters, sorter, extra) => { @@ -319,6 +325,7 @@ export const SpoolList: React.FC = () => { )} > + {spoolAdjustModal}