diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index 53160f8..8425dfd 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -144,6 +144,8 @@ "measured_weight": "Measured Weight", "used_length": "Used Length", "remaining_length": "Remaining Length", + "initial_weight": "Initial Weight", + "spool_weight": "Empty Weight", "location": "Location", "lot_nr": "Lot Nr", "first_used": "First Used", @@ -158,6 +160,8 @@ "used_weight": "How much filament has been used from the spool. A new spool should have 0g used.", "remaining_weight": "How much filament is left on the spool. For a new spool this should match the spool weight.", "measured_weight": "How much the filament and spool weigh.", + "initial_weight": "The initial Weight of the filament (net weight). This value may come from the filament, but is tracked at the spool level to allow for individual variations the weight of filament on the spool.", + "spool_weight": "The weight of the spool when it is empty. This value may come from either the filament or the vendor, but is tracked at the spool level to allow for individual variations of the weight of an empty spool.", "location": "Where the spool is located if you have multiple locations where you store your spools.", "lot_nr": "Manufacturer's lot number. Can be used to ensure a print has consistent color if multiple spools are used." }, @@ -227,9 +231,13 @@ "fields": { "id": "ID", "name": "Name", + "empty_spool_weight": "Empty Spool Weight", "registered": "Registered", "comment": "Comment" }, + "fields_help": { + "empty_spool_weight": "The weight of an empty spool from this vendor." + }, "titles": { "create": "Create Vendor", "clone": "Clone Vendor", diff --git a/client/src/pages/spools/create.tsx b/client/src/pages/spools/create.tsx index 16a18a9..c89d9dc 100644 --- a/client/src/pages/spools/create.tsx +++ b/client/src/pages/spools/create.tsx @@ -5,7 +5,7 @@ import { Form, Input, DatePicker, Select, InputNumber, Radio, Divider, Button, T import dayjs from "dayjs"; import TextArea from "antd/es/input/TextArea"; import { IFilament } from "../filaments/model"; -import { ISpool, ISpoolParsedExtras } from "./model"; +import { ISpool, ISpoolParsedExtras, WeightToEnter } from "./model"; import { numberFormatter, numberParser } from "../../utils/parsing"; import { useSpoolmanLocations } from "../../components/otherModels"; import { MinusOutlined, PlusOutlined } from "@ant-design/icons"; @@ -14,6 +14,7 @@ import { EntityType, useGetFields } from "../../utils/queryFields"; import { ExtraFieldFormItem, StringifiedExtras } from "../../components/extraFields"; import utc from "dayjs/plugin/utc"; import { getCurrencySymbol, useCurrency } from "../../utils/settings"; +import { ValueType } from "rc-input-number"; dayjs.extend(utc); @@ -39,6 +40,9 @@ export const SpoolCreate: React.FC { return obj.value === selectedFilamentID; }); - const filamentWeight = selectedFilament?.weight || 0; - const spoolWeight = selectedFilament?.spool_weight || 0; - + const filamentChange = (newID: number) => { + const newSelectedFilament = filamentOptions?.find((obj) => { return obj.value === newID; }); + + const initial_weight = initialWeightValue ?? 0; + const spool_weight = spoolWeightValue ?? 0; + const newFilamentWeight = newSelectedFilament?.weight || 0; const newSpoolWeight = newSelectedFilament?.spool_weight || 0; - if (weightToEnter >= 3) { - if (!(newFilamentWeight && newSpoolWeight)) { - setWeightToEnter(2); - } + const currentCalculatedFilamentWeight = getTotalWeightFromFilament(); + if ((initial_weight === 0 || initial_weight === currentCalculatedFilamentWeight) && newFilamentWeight > 0) { + form.setFieldValue("initial_weight", newFilamentWeight); } - if (weightToEnter >= 2) { - if (!newFilamentWeight) { - setWeightToEnter(1); - } + + if ((spool_weight === 0 || spool_weight === (selectedFilament?.spool_weight ?? 0)) && newSpoolWeight > 0) { + form.setFieldValue("spool_weight", newSpoolWeight); } }; @@ -162,6 +167,75 @@ export const SpoolCreate: React.FC { + return spoolWeightValue ?? (selectedFilament?.spool_weight ?? 0); + } + + const getFilamentWeight = (): number => { + return initialWeightValue ?? (selectedFilament?.weight ?? 0) + } + + const getGrossWeight = (): number => { + const net_weight = getFilamentWeight(); + const spool_weight = getSpoolWeight(); + return net_weight + spool_weight; + }; + + const getTotalWeightFromFilament = (): number => { + return (selectedFilament?.weight ?? 0) + (selectedFilament?.spool_weight ?? 0); + } + + const getMeasuredWeight = (): number => { + const grossWeight = getGrossWeight(); + + return grossWeight - usedWeight; + } + + const getRemainingWeight = (): number => { + const initial_weight = getFilamentWeight(); + + return initial_weight - usedWeight; + } + + const isMeasuredWeightEnabled = (): boolean => { + + if (!isRemainingWeightEnabled()) { + return false; + } + + const spool_weight = spoolWeightValue; + + return (spool_weight || selectedFilament?.spool_weight) ? true : false; + } + + const isRemainingWeightEnabled = (): boolean => { + const initial_weight = initialWeightValue; + + if (initial_weight) { + return true; + } + + return selectedFilament?.weight ? true : false; + } + + React.useEffect(() => { + if (weightToEnter >= WeightToEnter.measured_weight) + { + if (!isMeasuredWeightEnabled()) { + setWeightToEnter(WeightToEnter.remaining_weight); + return; + } + } + if (weightToEnter >= WeightToEnter.remaining_weight) + { + if (!isRemainingWeightEnabled()) { + setWeightToEnter(WeightToEnter.used_weight); + return; + } + } + }, [selectedFilament]) + + return ( + + + + + + + + @@ -265,14 +369,14 @@ export const SpoolCreate: React.FC { setWeightToEnter(value.target.value); }} - defaultValue={1} + defaultValue={WeightToEnter.used_weight} value={weightToEnter} > - {t("spool.fields.used_weight")} - + {t("spool.fields.used_weight")} + {t("spool.fields.remaining_weight")} - + {t("spool.fields.measured_weight")} @@ -285,7 +389,7 @@ export const SpoolCreate: React.FC { weightChange(value ?? 0); @@ -303,10 +407,10 @@ export const SpoolCreate: React.FC { - weightChange(filamentWeight - (value ?? 0)); + weightChange(getFilamentWeight() - (value ?? 0)); }} /> @@ -321,10 +425,11 @@ export const SpoolCreate: React.FC { - weightChange(filamentWeight - ((value ?? 0) - spoolWeight)); + const totalWeight = getGrossWeight(); + weightChange(totalWeight - (value ?? 0)); }} /> diff --git a/client/src/pages/spools/edit.tsx b/client/src/pages/spools/edit.tsx index a592dfc..cb5b9cb 100644 --- a/client/src/pages/spools/edit.tsx +++ b/client/src/pages/spools/edit.tsx @@ -5,7 +5,7 @@ import { Form, Input, DatePicker, Select, InputNumber, Radio, Divider, Alert, Ty import dayjs from "dayjs"; import TextArea from "antd/es/input/TextArea"; import { IFilament } from "../filaments/model"; -import { ISpool, ISpoolParsedExtras } from "./model"; +import { ISpool, ISpoolParsedExtras, WeightToEnter } from "./model"; import { numberFormatter, numberParser } from "../../utils/parsing"; import { useSpoolmanLocations } from "../../components/otherModels"; import { message } from "antd/lib"; @@ -21,12 +21,6 @@ We also need to stringify them again before sending them back to the API, which the form's onFinish method. Form.Item's normalize should do this, but it doesn't seem to work. */ -enum WeightToEnter { - used_weight = 1, - remaining_weight = 2, - measured_weight = 3, -} - export const SpoolEdit: React.FC = () => { const t = useTranslate(); const [messageApi, contextHolder] = message.useMessage(); @@ -43,6 +37,8 @@ export const SpoolEdit: React.FC = () => { }, }); + const initialWeightValue = Form.useWatch("initial_weight", form); + const spoolWeightValue = Form.useWatch("spool_weight", form); // Get filament selection options const { queryResult } = useSelect({ resource: "filament", @@ -100,31 +96,34 @@ export const SpoolEdit: React.FC = () => { const selectedFilament = filamentOptions?.find((obj) => { return obj.value === selectedFilamentID; }); - const filamentWeight = selectedFilament?.weight || 0; - const spoolWeight = selectedFilament?.spool_weight || 0; - + const filamentChange = (newID: number) => { + const newSelectedFilament = filamentOptions?.find((obj) => { return obj.value === newID; }); - const filamentHasWeight = newSelectedFilament?.weight || 0; - const filamentHasSpoolWeight = newSelectedFilament?.spool_weight || 0; - if (weightToEnter == WeightToEnter.measured_weight) { - if (!(filamentHasWeight && filamentHasSpoolWeight)) { - setWeightToEnter(WeightToEnter.remaining_weight); - } + const initial_weight = initialWeightValue ?? 0; + const spool_weight = spoolWeightValue ?? 0; + + const newFilamentWeight = newSelectedFilament?.weight || 0; + const newSpoolWeight = newSelectedFilament?.spool_weight || 0; + + const currentCalculatedFilamentWeight = getTotalWeightFromFilament(); + if ((initial_weight === 0 || initial_weight === currentCalculatedFilamentWeight) && newFilamentWeight > 0) { + form.setFieldValue("initial_weight", newFilamentWeight); } - if (weightToEnter == WeightToEnter.remaining_weight || weightToEnter == WeightToEnter.measured_weight) { - if (!filamentHasWeight) { - setWeightToEnter(WeightToEnter.used_weight); - } + + if ((spool_weight === 0 || spool_weight === (selectedFilament?.spool_weight ?? 0)) && newSpoolWeight > 0) { + form.setFieldValue("spool_weight", newSpoolWeight); } }; const weightChange = (weight: number) => { setUsedWeight(weight); - form.setFieldValue("used_weight", weight); + form.setFieldsValue({ + used_weight: weight, + }); }; const locations = useSpoolmanLocations(true); @@ -135,6 +134,74 @@ export const SpoolEdit: React.FC = () => { allLocations.push(newLocation.trim()); } +const getSpoolWeight = (): number => { + return spoolWeightValue ?? (selectedFilament?.spool_weight ?? 0); + } + + const getFilamentWeight = (): number => { + return initialWeightValue ?? (selectedFilament?.weight ?? 0) + } + + const getGrossWeight = (): number => { + const net_weight = getFilamentWeight(); + const spool_weight = getSpoolWeight(); + return net_weight + spool_weight; + }; + + const getTotalWeightFromFilament = (): number => { + return (selectedFilament?.weight ?? 0) + (selectedFilament?.spool_weight ?? 0); + } + + const getMeasuredWeight = (): number => { + const grossWeight = getGrossWeight(); + + return grossWeight - usedWeight; + } + + const getRemainingWeight = (): number => { + const initial_weight = getFilamentWeight(); + + return initial_weight - usedWeight; + } + + const isMeasuredWeightEnabled = (): boolean => { + + if (!isRemainingWeightEnabled()) { + return false; + } + + const spool_weight = spoolWeightValue; + + return (spool_weight || selectedFilament?.spool_weight) ? true : false; + } + + const isRemainingWeightEnabled = (): boolean => { + const initial_weight = initialWeightValue; + + if (initial_weight) { + return true; + } + + return selectedFilament?.weight ? true : false; + } + + React.useEffect(() => { + if (weightToEnter >= WeightToEnter.measured_weight) + { + if (!isMeasuredWeightEnabled()) { + setWeightToEnter(WeightToEnter.remaining_weight); + return; + } + } + if (weightToEnter >= WeightToEnter.remaining_weight) + { + if (!isRemainingWeightEnabled()) { + setWeightToEnter(WeightToEnter.used_weight); + return; + } + } + }, [selectedFilament]) + const initialUsedWeight = formProps.initialValues?.used_weight || 0; useEffect(() => { if (initialUsedWeight) { @@ -238,9 +305,40 @@ export const SpoolEdit: React.FC = () => { parser={numberParser} /> + + + + + + + + + { @@ -250,10 +348,10 @@ export const SpoolEdit: React.FC = () => { value={weightToEnter} > {t("spool.fields.used_weight")} - + {t("spool.fields.remaining_weight")} - + {t("spool.fields.measured_weight")} @@ -272,7 +370,10 @@ export const SpoolEdit: React.FC = () => { }} /> - + = () => { formatter={numberFormatter} parser={numberParser} disabled={weightToEnter != WeightToEnter.remaining_weight} - value={filamentWeight ? filamentWeight - usedWeight : 0} + value={getRemainingWeight()} onChange={(value) => { - weightChange(filamentWeight - (value ?? 0)); + weightChange(getFilamentWeight() - (value ?? 0)); }} /> - + = () => { formatter={numberFormatter} parser={numberParser} disabled={weightToEnter != WeightToEnter.measured_weight} - value={filamentWeight && spoolWeight ? filamentWeight - usedWeight + spoolWeight : 0} + value={getMeasuredWeight()} onChange={(value) => { - weightChange(filamentWeight - ((value ?? 0) - spoolWeight)); + const totalWeight = getGrossWeight(); + weightChange(totalWeight - (value ?? 0)); }} /> diff --git a/client/src/pages/spools/model.tsx b/client/src/pages/spools/model.tsx index a98a49f..626afb2 100644 --- a/client/src/pages/spools/model.tsx +++ b/client/src/pages/spools/model.tsx @@ -1,5 +1,11 @@ import { IFilament } from "../filaments/model"; +export enum WeightToEnter { + used_weight = 1, + remaining_weight = 2, + measured_weight = 3, +} + export interface ISpool { id: number; registered: string; @@ -7,6 +13,8 @@ export interface ISpool { last_used?: string; filament: IFilament; price?: number; + initial_weight?: number; + spool_weight?: number; remaining_weight?: number; used_weight: number; remaining_length?: number; diff --git a/client/src/pages/vendors/create.tsx b/client/src/pages/vendors/create.tsx index 9649ccd..69ac0ae 100644 --- a/client/src/pages/vendors/create.tsx +++ b/client/src/pages/vendors/create.tsx @@ -1,7 +1,7 @@ import React from "react"; import { HttpError, IResourceComponentsProps, useTranslate } from "@refinedev/core"; import { Create, useForm } from "@refinedev/antd"; -import { Button, Form, Input, Typography } from "antd"; +import { Button, Form, Input, Typography, InputNumber } from "antd"; import TextArea from "antd/es/input/TextArea"; import { IVendor, IVendorParsedExtras } from "./model"; import { EntityType, useGetFields } from "../../utils/queryFields"; @@ -85,6 +85,22 @@ export const VendorCreate: React.FC