Merge branch 'master' into adjust-spool-button-on-show

This commit is contained in:
Charalampos Tsachakis
2025-03-19 09:07:08 +02:00
committed by GitHub
62 changed files with 3668 additions and 1888 deletions

View File

@@ -8,7 +8,7 @@ import { useEffect, useState } from "react";
import { ExtraFieldFormItem, ParsedExtras, StringifiedExtras } from "../../components/extraFields";
import { FilamentImportModal } from "../../components/filamentImportModal";
import { MultiColorPicker } from "../../components/multiColorPicker";
import { numberFormatter, numberParser, numberParserAllowEmpty } from "../../utils/parsing";
import { formatNumberOnUserInput, numberParser, numberParserAllowEmpty } from "../../utils/parsing";
import { ExternalFilament } from "../../utils/queryExternalDB";
import { EntityType, useGetFields } from "../../utils/queryFields";
import { getCurrencySymbol, useCurrency } from "../../utils/settings";
@@ -250,7 +250,7 @@ export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrClonePr
<InputNumber
addonAfter={getCurrencySymbol(undefined, currency)}
precision={2}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParserAllowEmpty}
/>
</Form.Item>
@@ -266,7 +266,7 @@ export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrClonePr
},
]}
>
<InputNumber addonAfter="g/cm³" precision={2} formatter={numberFormatter} parser={numberParser} />
<InputNumber addonAfter="g/cm³" precision={2} formatter={formatNumberOnUserInput} parser={numberParser} />
</Form.Item>
<Form.Item
label={t("filament.fields.diameter")}
@@ -280,7 +280,7 @@ export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrClonePr
},
]}
>
<InputNumber addonAfter="mm" precision={2} formatter={numberFormatter} parser={numberParser} />
<InputNumber addonAfter="mm" precision={2} formatter={formatNumberOnUserInput} parser={numberParser} />
</Form.Item>
<Form.Item
label={t("filament.fields.weight")}

View File

@@ -6,7 +6,7 @@ import dayjs from "dayjs";
import React, { useEffect, useState } from "react";
import { ExtraFieldFormItem, ParsedExtras, StringifiedExtras } from "../../components/extraFields";
import { MultiColorPicker } from "../../components/multiColorPicker";
import { numberFormatter, numberParser, numberParserAllowEmpty } from "../../utils/parsing";
import { formatNumberOnUserInput, numberParser, numberParserAllowEmpty } from "../../utils/parsing";
import { EntityType, useGetFields } from "../../utils/queryFields";
import { getCurrencySymbol, useCurrency } from "../../utils/settings";
import { IVendor } from "../vendors/model";
@@ -63,6 +63,9 @@ export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
const originalOnFinish = formProps.onFinish;
formProps.onFinish = (allValues: IFilamentParsedExtras) => {
if (allValues !== undefined && allValues !== null) {
if (colorType == "single") {
allValues.multi_color_hexes = "";
}
// Lot of stupidity here to make types work
const stringifiedAllValues = StringifiedExtras<IFilamentParsedExtras>(allValues);
originalOnFinish?.({
@@ -166,7 +169,7 @@ export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
return e?.toHex();
}}
>
<ColorPicker format="hex" />
<ColorPicker />
</Form.Item>
)}
{colorType == "multi" && (
@@ -225,7 +228,7 @@ export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
<InputNumber
addonAfter={getCurrencySymbol(undefined, currency)}
precision={2}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParserAllowEmpty}
/>
</Form.Item>
@@ -241,7 +244,7 @@ export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
},
]}
>
<InputNumber addonAfter="g/cm³" precision={2} formatter={numberFormatter} parser={numberParser} />
<InputNumber addonAfter="g/cm³" precision={2} formatter={formatNumberOnUserInput} parser={numberParser} />
</Form.Item>
<Form.Item
label={t("filament.fields.diameter")}
@@ -255,7 +258,7 @@ export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
},
]}
>
<InputNumber addonAfter="mm" precision={2} formatter={numberFormatter} parser={numberParser} />
<InputNumber addonAfter="mm" precision={2} formatter={formatNumberOnUserInput} parser={numberParser} />
</Form.Item>
<Form.Item
label={t("filament.fields.weight")}

View File

@@ -5,28 +5,28 @@ import { Button, Dropdown, Table } from "antd";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import { useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import {
ActionsColumn,
CustomFieldColumn,
DateColumn,
FilteredQueryColumn,
NumberColumn,
RichColumn,
SortedColumn,
SpoolIconColumn,
ActionsColumn,
CustomFieldColumn,
DateColumn,
FilteredQueryColumn,
NumberColumn,
RichColumn,
SortedColumn,
SpoolIconColumn,
} from "../../components/column";
import { useLiveify } from "../../components/liveify";
import {
useSpoolmanArticleNumbers,
useSpoolmanFilamentNames,
useSpoolmanMaterials,
useSpoolmanVendors,
useSpoolmanArticleNumbers,
useSpoolmanFilamentNames,
useSpoolmanMaterials,
useSpoolmanVendors,
} from "../../components/otherModels";
import { removeUndefined } from "../../utils/filtering";
import { EntityType, useGetFields } from "../../utils/queryFields";
import { TableState, useInitialTableState, useStoreInitialState } from "../../utils/saveload";
import { useCurrency } from "../../utils/settings";
import { useCurrencyFormatter } from "../../utils/settings";
import { IFilament } from "./model";
dayjs.extend(utc);
@@ -77,7 +77,7 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
const invalidate = useInvalidate();
const navigate = useNavigate();
const extraFields = useGetFields(EntityType.filament);
const currency = useCurrency();
const currencyFormatter = useCurrencyFormatter();
const allColumnsWithExtraFields = [...allColumns, ...(extraFields.data?.map((field) => "extra." + field.key) ?? [])];
@@ -263,12 +263,10 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
align: "right",
width: 80,
render: (_, obj: IFilamentCollapsed) => {
return obj.price?.toLocaleString(undefined, {
style: "currency",
currencyDisplay: "narrowSymbol",
currency: currency,
notation: "compact",
});
if (obj.price === undefined) {
return "";
}
return currencyFormatter.format(obj.price);
},
}),
NumberColumn({

View File

@@ -4,13 +4,13 @@ import { Button, Typography } from "antd";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import React from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import { ExtraFieldDisplay } from "../../components/extraFields";
import { NumberFieldUnit } from "../../components/numberField";
import SpoolIcon from "../../components/spoolIcon";
import { enrichText } from "../../utils/parsing";
import { EntityType, useGetFields } from "../../utils/queryFields";
import { useCurrency } from "../../utils/settings";
import { useCurrencyFormatter } from "../../utils/settings";
import { IFilament } from "./model";
dayjs.extend(utc);
@@ -20,7 +20,7 @@ export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
const t = useTranslate();
const navigate = useNavigate();
const extraFields = useGetFields(EntityType.filament);
const currency = useCurrency();
const currencyFormatter = useCurrencyFormatter();
const { queryResult } = useShow<IFilament>({
liveMode: "auto",
});
@@ -88,19 +88,12 @@ export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
<Title level={5}>{t("filament.fields.name")}</Title>
<TextField value={record?.name} />
<Title level={5}>{t("filament.fields.color_hex")}</Title>
{colorObj && <SpoolIcon color={colorObj} size="large" />}
{colorObj && <SpoolIcon color={colorObj} size="large" no_margin />}
{record?.color_hex && <TextField value={`#${record?.color_hex}`} />}
<Title level={5}>{t("filament.fields.material")}</Title>
<TextField value={record?.material} />
<Title level={5}>{t("filament.fields.price")}</Title>
<NumberField
value={record?.price ?? ""}
options={{
style: "currency",
currency: currency,
currencyDisplay: "narrowSymbol",
notation: "compact",
}}
/>
<TextField value={record?.price ? currencyFormatter.format(record.price) : ""} />
<Title level={5}>{t("filament.fields.density")}</Title>
<NumberFieldUnit
value={record?.density ?? ""}

View File

@@ -7,7 +7,7 @@ import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import React from "react";
import { Trans } from "react-i18next";
import { Link } from "react-router-dom";
import { Link } from "react-router";
dayjs.extend(utc);

View File

@@ -6,7 +6,7 @@ import Title from "antd/es/typography/Title";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import React, { ReactNode } from "react";
import { Link } from "react-router-dom";
import { Link } from "react-router";
import Logo from "../../icon.svg?react";
import { ISpool } from "../spools/model";

View File

@@ -8,7 +8,7 @@ import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import utc from "dayjs/plugin/utc";
import { useEffect, useRef } from "react";
import { Link } from "react-router-dom";
import { Link } from "react-router";
import SpoolIcon from "../../../components/spoolIcon";
import { formatWeight } from "../../../utils/parsing";
import { ISpool } from "../../spools/model";

View File

@@ -5,7 +5,7 @@ import { Content } from "antd/es/layout/layout";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import React from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { useNavigate, useSearchParams } from "react-router";
import SpoolQRCodePrintingDialog from "./spoolQrCodePrintingDialog";
import SpoolSelectModal from "./spoolSelectModal";

View File

@@ -1,3 +1,4 @@
import { ReactElement } from "react";
import { v4 as uuidv4 } from "uuid";
import { useGetSetting, useSetSetting } from "../../utils/querySettings";
import { ISpool } from "../spools/model";
@@ -75,7 +76,7 @@ function getTagValue(tag: string, obj: GenericObject): any {
return value;
}
function applyNewline(text: string): JSX.Element[] {
function applyNewline(text: string): ReactElement[] {
return text.split("\n").map((line, idx, arr) => (
<span key={idx}>
{line}
@@ -84,7 +85,7 @@ function applyNewline(text: string): JSX.Element[] {
));
}
function applyTextFormatting(text: string): JSX.Element[] {
function applyTextFormatting(text: string): ReactElement[] {
const regex = /\*\*([\w\W]*?)\*\*/g;
const parts = text.split(regex);
// Map over the parts and wrap matched text with <b> tags
@@ -96,7 +97,7 @@ function applyTextFormatting(text: string): JSX.Element[] {
return elements;
}
export function renderLabelContents(template: string, spool: ISpool): JSX.Element {
export function renderLabelContents(template: string, spool: ISpool): ReactElement {
// Find all {tags} in the template string and loop over them
// let matches = [...template.matchAll(/(?:{(.*?))?{(.*?)}(.*?)(?:}(.*?))?/gs)];
let matches = [...template.matchAll(/{(?:[^}{]|{[^}{]*})*}/gs)];

View File

@@ -15,19 +15,19 @@ import {
Space,
} from "antd";
import * as htmlToImage from "html-to-image";
import React, { useRef } from "react";
import ReactToPrint from "react-to-print";
import React, { ReactElement, useRef } from "react";
import { useReactToPrint } from "react-to-print";
import { useSavedState } from "../../utils/saveload";
import { PrintSettings } from "./printing";
interface PrintingDialogProps {
items: JSX.Element[];
items: ReactElement[];
printSettings: PrintSettings;
setPrintSettings: (setPrintSettings: PrintSettings) => void;
style?: string;
extraSettings?: JSX.Element;
extraSettingsStart?: JSX.Element;
extraButtons?: JSX.Element;
extraSettings?: ReactElement;
extraSettingsStart?: ReactElement;
extraButtons?: ReactElement;
}
interface PaperDimensions {
@@ -90,7 +90,8 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
const paperWidth = paperSize === "custom" ? customPaperSize.width : paperDimensions[paperSize].width;
const paperHeight = paperSize === "custom" ? customPaperSize.height : paperDimensions[paperSize].height;
const printRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
const reactToPrintFn = useReactToPrint({ contentRef });
const itemWidth = (paperWidth - margin.left - margin.right - spacing.horizontal) / paperColumns - spacing.horizontal;
const itemHeight = (paperHeight - margin.top - margin.bottom - spacing.vertical) / paperRows - spacing.vertical;
@@ -223,7 +224,7 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
>
<div
className="print-container"
ref={printRef}
ref={contentRef}
style={{
transform: `scale(${previewScale})`,
transformOrigin: "top left",
@@ -817,15 +818,9 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
<Button type="primary" icon={<FileImageOutlined />} size="large" onClick={saveAsImage}>
{t("printing.generic.saveAsImage")}
</Button>
<ReactToPrint
key="print-button"
trigger={() => (
<Button type="primary" icon={<PrinterOutlined />} size="large">
{t("printing.generic.print")}
</Button>
)}
content={() => printRef.current}
/>
<Button type="primary" icon={<PrinterOutlined />} size="large" onClick={() => reactToPrintFn()}>
{t("printing.generic.print")}
</Button>
</Space>
</Col>
</Row>

View File

@@ -1,5 +1,7 @@
import { useTranslate } from "@refinedev/core";
import { Col, Form, InputNumber, QRCode, Radio, RadioChangeEvent, Row, Slider, Switch, Typography } from "antd";
import { ReactElement } from "react";
import { getBasePath } from "../../utils/url";
import { QRCodePrintSettings } from "./printing";
import PrintingDialog from "./printingDialog";
@@ -7,7 +9,7 @@ const { Text } = Typography;
interface QRCodeData {
value: string;
label?: JSX.Element;
label?: ReactElement;
errorLevel?: "L" | "M" | "Q" | "H";
}
@@ -15,9 +17,9 @@ interface QRCodePrintingDialogProps {
items: QRCodeData[];
printSettings: QRCodePrintSettings;
setPrintSettings: (setPrintSettings: QRCodePrintSettings) => void;
extraSettings?: JSX.Element;
extraSettingsStart?: JSX.Element;
extraButtons?: JSX.Element;
extraSettings?: ReactElement;
extraSettingsStart?: ReactElement;
extraButtons?: ReactElement;
baseUrlRoot: string;
useHTTPUrl: boolean;
setUseHTTPUrl: (value: boolean) => void;
@@ -47,7 +49,7 @@ const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({
<div className="print-qrcode-container">
<QRCode
className="print-qrcode"
icon={showQRCodeMode === "withIcon" ? "/favicon.svg" : undefined}
icon={showQRCodeMode === "withIcon" ? getBasePath() + "/favicon.svg" : undefined}
value={item.value}
errorLevel={item.errorLevel}
type="svg"

View File

@@ -3,7 +3,7 @@ import { useTable } from "@refinedev/antd";
import { Button, Checkbox, Col, message, Row, Space, Table } from "antd";
import { t } from "i18next";
import { useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import { FilteredQueryColumn, SortedColumn, SpoolIconColumn } from "../../components/column";
import { useSpoolmanFilamentFilter, useSpoolmanMaterials } from "../../components/otherModels";
import { removeUndefined } from "../../utils/filtering";

View File

@@ -22,7 +22,7 @@ import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
import { useState } from "react";
import { Trans } from "react-i18next";
import { useParams } from "react-router-dom";
import { useParams } from "react-router";
import { DateTimePicker } from "../../components/dateTimePicker";
import { InputNumberRange } from "../../components/inputNumberRange";
import { EntityType, Field, FieldType, useDeleteField, useGetFields, useSetField } from "../../utils/queryFields";
@@ -488,10 +488,16 @@ export function ExtraFieldsSettings() {
} else if (Array.isArray(val) && record.field.field_type === FieldType.choice) {
return val.join(", ");
} else if (
(Array.isArray(val) && record.field.field_type === FieldType.integer_range) ||
record.field.field_type === FieldType.float_range
Array.isArray(val) && (
record.field.field_type === FieldType.integer_range || record.field.field_type === FieldType.float_range
)
) {
return `${val[0]} \u2013 ${val[1]}`;
let lower = val[0] ?? "";
let upper = val[1] ?? "";
if (lower === "" && upper === "") {
return null;
}
return `${lower} \u2013 ${upper}`;
} else {
return null;
}

View File

@@ -1,5 +1,5 @@
import { useTranslate } from "@refinedev/core";
import { Button, Form, Input, message } from "antd";
import { Button, Checkbox, Form, Input, message } from "antd";
import { useEffect } from "react";
import { useGetSettings, useSetSetting } from "../../utils/querySettings";
@@ -7,6 +7,7 @@ export function GeneralSettings() {
const settings = useGetSettings();
const setBaseUrl = useSetSetting("base_url");
const setCurrency = useSetSetting("currency");
const setRoundPrices = useSetSetting("round_prices");
const [form] = Form.useForm();
const [messageApi, contextHolder] = message.useMessage();
const t = useTranslate();
@@ -17,6 +18,7 @@ export function GeneralSettings() {
form.setFieldsValue({
currency: JSON.parse(settings.data.currency.value),
base_url: JSON.parse(settings.data.base_url.value),
round_prices: JSON.parse(settings.data.round_prices.value),
});
}
}, [settings.data, form]);
@@ -29,7 +31,7 @@ export function GeneralSettings() {
}, [setCurrency.isSuccess, messageApi, t]);
// Handle form submit
const onFinish = (values: { currency: string; base_url: string }) => {
const onFinish = (values: { currency: string; base_url: string, round_prices: boolean }) => {
// Check if the currency has changed
if (settings.data?.currency.value !== JSON.stringify(values.currency)) {
setCurrency.mutate(values.currency);
@@ -38,61 +40,74 @@ export function GeneralSettings() {
if (settings.data?.base_url.value !== JSON.stringify(values.base_url)) {
setBaseUrl.mutate(values.base_url);
}
// Check if the setting to round prices has changed
if (settings.data?.round_prices.value !== JSON.stringify(values.round_prices)) {
setRoundPrices.mutate(values.round_prices);
}
};
return (
<>
<Form
form={form}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
initialValues={{
currency: settings.data?.currency.value,
}}
onFinish={onFinish}
style={{
maxWidth: "600px",
margin: "0 auto",
}}
return (<>
<Form
form={form}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
initialValues={{
currency: settings.data?.currency.value,
round_prices: settings.data?.round_prices.value,
}}
onFinish={onFinish}
style={{
maxWidth: "600px",
margin: "0 auto",
}}
>
<Form.Item
label={t("settings.general.currency.label")}
name="currency"
rules={[
{
required: true,
},
{
pattern: /^[A-Z]{3}$/,
},
]}
>
<Form.Item
label={t("settings.general.currency.label")}
name="currency"
rules={[
{
required: true,
},
{
pattern: /^[A-Z]{3}$/,
},
]}
>
<Input />
</Form.Item>
<Input />
</Form.Item>
<Form.Item
label={t("settings.general.base_url.label")}
tooltip={t("settings.general.base_url.tooltip")}
name="base_url"
rules={[
{
required: false,
},
{
pattern: /^https?:\/\/.+(?<!\/)$/,
},
]}
>
<Input placeholder="https://example.com:8000" />
</Form.Item>
<Form.Item
label={t("settings.general.base_url.label")}
tooltip={t("settings.general.base_url.tooltip")}
name="base_url"
rules={[
{
required: false,
},
{
pattern: /^https?:\/\/.+(?<!\/)$/,
},
]}
>
<Input placeholder="https://example.com:8000" />
</Form.Item>
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
<Button type="primary" htmlType="submit" loading={settings.isFetching || setCurrency.isLoading}>
{t("buttons.save")}
</Button>
</Form.Item>
</Form>
{contextHolder}
</>
);
<Form.Item
label={t("settings.general.round_prices.label")}
tooltip={t("settings.general.round_prices.tooltip")}
name="round_prices"
valuePropName="checked"
>
<Checkbox />
</Form.Item>
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
<Button type="primary" htmlType="submit" loading={settings.isFetching || setCurrency.isLoading}>
{t("buttons.save")}
</Button>
</Form.Item>
</Form>
{contextHolder}
</>);
}

View File

@@ -5,7 +5,7 @@ import { Content } from "antd/es/layout/layout";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import React from "react";
import { Route, Routes, useNavigate } from "react-router-dom";
import { Route, Routes, useNavigate } from "react-router";
import { ExtraFieldsSettings } from "./extraFieldsSettings";
import { GeneralSettings } from "./generalSettings";

View File

@@ -10,7 +10,7 @@ import { ExtraFieldFormItem, ParsedExtras, StringifiedExtras } from "../../compo
import { useSpoolmanLocations } from "../../components/otherModels";
import { searchMatches } from "../../utils/filtering";
import "../../utils/overrides.css";
import { numberFormatter, numberParser, numberParserAllowEmpty } from "../../utils/parsing";
import { formatNumberOnUserInput, numberParser, numberParserAllowEmpty } from "../../utils/parsing";
import { EntityType, useGetFields } from "../../utils/queryFields";
import { getCurrencySymbol, useCurrency } from "../../utils/settings";
import { createFilamentFromExternal } from "../filaments/functions";
@@ -330,7 +330,7 @@ export const SpoolCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps
<InputNumber
addonAfter={getCurrencySymbol(undefined, currency)}
precision={2}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParserAllowEmpty}
/>
</Form.Item>
@@ -391,7 +391,7 @@ export const SpoolCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps
min={0}
addonAfter="g"
precision={1}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParser}
disabled={weightToEnter != WeightToEnter.used_weight}
value={usedWeight}
@@ -409,7 +409,7 @@ export const SpoolCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps
min={0}
addonAfter="g"
precision={1}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParser}
disabled={weightToEnter != WeightToEnter.remaining_weight}
value={getRemainingWeight()}
@@ -427,7 +427,7 @@ export const SpoolCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps
min={0}
addonAfter="g"
precision={1}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParser}
disabled={weightToEnter != WeightToEnter.measured_weight}
value={getMeasuredWeight()}

View File

@@ -5,11 +5,11 @@ import TextArea from "antd/es/input/TextArea";
import { message } from "antd/lib";
import dayjs from "dayjs";
import { useEffect, useMemo, useState } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { useNavigate, useSearchParams } from "react-router";
import { ExtraFieldFormItem, ParsedExtras, StringifiedExtras } from "../../components/extraFields";
import { useSpoolmanLocations } from "../../components/otherModels";
import { searchMatches } from "../../utils/filtering";
import { numberFormatter, numberParser, numberParserAllowEmpty } from "../../utils/parsing";
import { formatNumberOnUserInput, numberParser, numberParserAllowEmpty } from "../../utils/parsing";
import { EntityType, useGetFields } from "../../utils/queryFields";
import { getCurrencySymbol, useCurrency } from "../../utils/settings";
import { createFilamentFromExternal } from "../filaments/functions";
@@ -320,7 +320,7 @@ export const SpoolEdit: React.FC<IResourceComponentsProps> = () => {
<InputNumber
addonAfter={getCurrencySymbol(undefined, currency)}
precision={2}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParserAllowEmpty}
/>
</Form.Item>
@@ -380,7 +380,7 @@ export const SpoolEdit: React.FC<IResourceComponentsProps> = () => {
min={0}
addonAfter="g"
precision={1}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParser}
disabled={weightToEnter != WeightToEnter.used_weight}
value={usedWeight}
@@ -394,7 +394,7 @@ export const SpoolEdit: React.FC<IResourceComponentsProps> = () => {
min={0}
addonAfter="g"
precision={1}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParser}
disabled={weightToEnter != WeightToEnter.remaining_weight}
value={getRemainingWeight()}
@@ -408,7 +408,7 @@ export const SpoolEdit: React.FC<IResourceComponentsProps> = () => {
min={0}
addonAfter="g"
precision={1}
formatter={numberFormatter}
formatter={formatNumberOnUserInput}
parser={numberParser}
disabled={weightToEnter != WeightToEnter.measured_weight}
value={getMeasuredWeight()}

View File

@@ -2,7 +2,7 @@ import { useSelect, useTranslate } from "@refinedev/core";
import { useQueries } from "@tanstack/react-query";
import { Form, InputNumber, Modal, Radio } from "antd";
import { useForm } from "antd/es/form/Form";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useMemo, useRef, useState } from "react";
import { formatLength, formatWeight } from "../../utils/parsing";
import { SpoolType, useGetExternalDBFilaments } from "../../utils/queryExternalDB";
import { getAPIURL } from "../../utils/url";
@@ -181,9 +181,13 @@ export function useSpoolAdjustModal() {
const [curSpool, setCurSpool] = useState<ISpool | null>(null);
const [measurementType, setMeasurementType] = useState<MeasurementType>("length");
const inputNumberRef = useRef<HTMLInputElement | null>(null);
const openSpoolAdjustModal = useCallback((spool: ISpool) => {
setCurSpool(spool);
setTimeout(() => {
inputNumberRef.current?.focus();
}, 0);
}, []);
const spoolAdjustModal = useMemo(() => {
@@ -224,7 +228,7 @@ export function useSpoolAdjustModal() {
</Radio.Group>
</Form.Item>
<Form.Item label={t("spool.form.adjust_filament_value")} name="filament_value">
<InputNumber precision={1} addonAfter={measurementType === "length" ? "mm" : "g"} />
<InputNumber ref={inputNumberRef} precision={1} addonAfter={measurementType === "length" ? "mm" : "g"} />
</Form.Item>
</Form>
</Modal>

View File

@@ -1,12 +1,12 @@
import {
EditOutlined,
EyeOutlined,
FilterOutlined,
InboxOutlined,
PlusSquareOutlined,
PrinterOutlined,
ToolOutlined,
ToTopOutlined,
EditOutlined,
EyeOutlined,
FilterOutlined,
InboxOutlined,
PlusSquareOutlined,
PrinterOutlined,
ToolOutlined,
ToTopOutlined,
} from "@ant-design/icons";
import { List, useTable } from "@refinedev/antd";
import { IResourceComponentsProps, useInvalidate, useNavigation, useTranslate } from "@refinedev/core";
@@ -14,29 +14,29 @@ import { Button, Dropdown, Modal, Table } from "antd";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import { useCallback, useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import {
Action,
ActionsColumn,
CustomFieldColumn,
DateColumn,
FilteredQueryColumn,
NumberColumn,
RichColumn,
SortedColumn,
SpoolIconColumn,
Action,
ActionsColumn,
CustomFieldColumn,
DateColumn,
FilteredQueryColumn,
NumberColumn,
RichColumn,
SortedColumn,
SpoolIconColumn,
} from "../../components/column";
import { useLiveify } from "../../components/liveify";
import {
useSpoolmanFilamentFilter,
useSpoolmanLocations,
useSpoolmanLotNumbers,
useSpoolmanMaterials,
useSpoolmanFilamentFilter,
useSpoolmanLocations,
useSpoolmanLotNumbers,
useSpoolmanMaterials,
} from "../../components/otherModels";
import { removeUndefined } from "../../utils/filtering";
import { EntityType, useGetFields } from "../../utils/queryFields";
import { TableState, useInitialTableState, useSavedState, useStoreInitialState } from "../../utils/saveload";
import { useCurrency } from "../../utils/settings";
import { useCurrencyFormatter } from "../../utils/settings";
import { setSpoolArchived, useSpoolAdjustModal } from "./functions";
import { ISpool } from "./model";
@@ -102,7 +102,7 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
const invalidate = useInvalidate();
const navigate = useNavigate();
const extraFields = useGetFields(EntityType.spool);
const currency = useCurrency();
const currencyFormatter = useCurrencyFormatter();
const { openSpoolAdjustModal, spoolAdjustModal } = useSpoolAdjustModal();
const allColumnsWithExtraFields = [...allColumns, ...(extraFields.data?.map((field) => "extra." + field.key) ?? [])];
@@ -381,12 +381,10 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
align: "right",
width: 80,
render: (_, obj: ISpoolCollapsed) => {
return obj.price?.toLocaleString(undefined, {
style: "currency",
currencyDisplay: "narrowSymbol",
currency: currency,
notation: "compact",
});
if (obj.price === undefined) {
return "";
}
return currencyFormatter.format(obj.price);
},
}),
NumberColumn({

View File

@@ -1,3 +1,4 @@
import { getBasePath } from "../../utils/url";
import { InboxOutlined, PrinterOutlined, ToTopOutlined, ToolOutlined } from "@ant-design/icons";
import { DateField, NumberField, Show, TextField } from "@refinedev/antd";
import { IResourceComponentsProps, useInvalidate, useShow, useTranslate } from "@refinedev/core";
@@ -10,7 +11,7 @@ import { NumberFieldUnit } from "../../components/numberField";
import SpoolIcon from "../../components/spoolIcon";
import { enrichText } from "../../utils/parsing";
import { EntityType, useGetFields } from "../../utils/queryFields";
import { useCurrency } from "../../utils/settings";
import { useCurrencyFormatter } from "../../utils/settings";
import { IFilament } from "../filaments/model";
import { setSpoolArchived, useSpoolAdjustModal } from "./functions";
import { ISpool } from "./model";
@@ -23,7 +24,7 @@ const { confirm } = Modal;
export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
const t = useTranslate();
const extraFields = useGetFields(EntityType.spool);
const currency = useCurrency();
const currencyFormatter = useCurrencyFormatter();
const invalidate = useInvalidate();
const { queryResult } = useShow<ISpool>({
@@ -33,13 +34,12 @@ export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
const record = data?.data;
const spoolPrice = (item: ISpool) => {
let spoolPrice = "";
if (item.price === undefined) {
spoolPrice = `${item.filament.price}`;
return spoolPrice;
const spoolPrice = (item?: ISpool) => {
const price = item?.price ?? item?.filament.price;
if (price === undefined) {
return "";
}
return item.price;
return currencyFormatter.format(price);
};
// Provides the function to open the spool adjustment modal and the modal component itself
@@ -128,7 +128,7 @@ export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
<Button
type="primary"
icon={<PrinterOutlined />}
href={"/spool/print?spools=" + record?.id + "&return=" + encodeURIComponent(window.location.pathname)}
href={getBasePath() + "/spool/print?spools=" + record?.id + "&return=" + encodeURIComponent(window.location.pathname)}
>
{t("printing.qrcode.button")}
</Button>
@@ -150,17 +150,10 @@ export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
<Title level={5}>{t("spool.fields.id")}</Title>
<NumberField value={record?.id ?? ""} />
<Title level={5}>{t("spool.fields.filament")}</Title>
{colorObj && <SpoolIcon color={colorObj} />} <TextField value={record ? filamentURL(record?.filament) : ""} />
{colorObj && <SpoolIcon color={colorObj} size="large" no_margin />}
<TextField value={record ? filamentURL(record?.filament) : ""} />
<Title level={5}>{t("spool.fields.price")}</Title>
<NumberField
value={record ? spoolPrice(record) : ""}
options={{
style: "currency",
currency: currency,
currencyDisplay: "narrowSymbol",
notation: "compact",
}}
/>
<TextField value={spoolPrice(record)} />
<Title level={5}>{t("spool.fields.registered")}</Title>
<DateField
value={dayjs.utc(record?.registered).local()}

View File

@@ -5,14 +5,14 @@ import { Button, Dropdown, Table } from "antd";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import { useCallback, useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import {
ActionsColumn,
CustomFieldColumn,
DateColumn,
NumberColumn,
RichColumn,
SortedColumn,
ActionsColumn,
CustomFieldColumn,
DateColumn,
NumberColumn,
RichColumn,
SortedColumn,
} from "../../components/column";
import { useLiveify } from "../../components/liveify";
import { removeUndefined } from "../../utils/filtering";