Merge branch 'master' into adjust-spool-button-on-show
This commit is contained in:
@@ -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")}
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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 ?? ""}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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)];
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
</>);
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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()}
|
||||
|
||||
14
client/src/pages/vendors/list.tsx
vendored
14
client/src/pages/vendors/list.tsx
vendored
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user