Add setting to toggle rounding of prices
This commit is contained in:
@@ -316,6 +316,10 @@
|
|||||||
"base_url": {
|
"base_url": {
|
||||||
"label": "Base URL",
|
"label": "Base URL",
|
||||||
"tooltip": "The base URL to use when generating features such as QR codes."
|
"tooltip": "The base URL to use when generating features such as QR codes."
|
||||||
|
},
|
||||||
|
"round_prices": {
|
||||||
|
"label": "Round prices",
|
||||||
|
"tooltip": "Round prices to the nearest whole number."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extra_fields": {
|
"extra_fields": {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
import { removeUndefined } from "../../utils/filtering";
|
import { removeUndefined } from "../../utils/filtering";
|
||||||
import { EntityType, useGetFields } from "../../utils/queryFields";
|
import { EntityType, useGetFields } from "../../utils/queryFields";
|
||||||
import { TableState, useInitialTableState, useStoreInitialState } from "../../utils/saveload";
|
import { TableState, useInitialTableState, useStoreInitialState } from "../../utils/saveload";
|
||||||
import { useCurrency } from "../../utils/settings";
|
import { useCurrencyFormatter } from "../../utils/settings";
|
||||||
import { IFilament } from "./model";
|
import { IFilament } from "./model";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
@@ -77,7 +77,7 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
|
|||||||
const invalidate = useInvalidate();
|
const invalidate = useInvalidate();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const extraFields = useGetFields(EntityType.filament);
|
const extraFields = useGetFields(EntityType.filament);
|
||||||
const currency = useCurrency();
|
const currencyFormatter = useCurrencyFormatter();
|
||||||
|
|
||||||
const allColumnsWithExtraFields = [...allColumns, ...(extraFields.data?.map((field) => "extra." + field.key) ?? [])];
|
const allColumnsWithExtraFields = [...allColumns, ...(extraFields.data?.map((field) => "extra." + field.key) ?? [])];
|
||||||
|
|
||||||
@@ -263,12 +263,10 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
|
|||||||
align: "right",
|
align: "right",
|
||||||
width: 80,
|
width: 80,
|
||||||
render: (_, obj: IFilamentCollapsed) => {
|
render: (_, obj: IFilamentCollapsed) => {
|
||||||
return obj.price?.toLocaleString(undefined, {
|
if (obj.price === undefined) {
|
||||||
style: "currency",
|
return "";
|
||||||
currencyDisplay: "narrowSymbol",
|
}
|
||||||
currency: currency,
|
return currencyFormatter.format(obj.price);
|
||||||
notation: "compact",
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
NumberColumn({
|
NumberColumn({
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { NumberFieldUnit } from "../../components/numberField";
|
|||||||
import SpoolIcon from "../../components/spoolIcon";
|
import SpoolIcon from "../../components/spoolIcon";
|
||||||
import { enrichText } from "../../utils/parsing";
|
import { enrichText } from "../../utils/parsing";
|
||||||
import { EntityType, useGetFields } from "../../utils/queryFields";
|
import { EntityType, useGetFields } from "../../utils/queryFields";
|
||||||
import { useCurrency } from "../../utils/settings";
|
import { useCurrencyFormatter } from "../../utils/settings";
|
||||||
import { IFilament } from "./model";
|
import { IFilament } from "./model";
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
|||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const extraFields = useGetFields(EntityType.filament);
|
const extraFields = useGetFields(EntityType.filament);
|
||||||
const currency = useCurrency();
|
const currencyFormatter = useCurrencyFormatter();
|
||||||
const { queryResult } = useShow<IFilament>({
|
const { queryResult } = useShow<IFilament>({
|
||||||
liveMode: "auto",
|
liveMode: "auto",
|
||||||
});
|
});
|
||||||
@@ -92,15 +92,7 @@ export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
|||||||
<Title level={5}>{t("filament.fields.material")}</Title>
|
<Title level={5}>{t("filament.fields.material")}</Title>
|
||||||
<TextField value={record?.material} />
|
<TextField value={record?.material} />
|
||||||
<Title level={5}>{t("filament.fields.price")}</Title>
|
<Title level={5}>{t("filament.fields.price")}</Title>
|
||||||
<NumberField
|
<TextField value={record?.price ? currencyFormatter.format(record.price) : ""} />
|
||||||
value={record?.price ?? ""}
|
|
||||||
options={{
|
|
||||||
style: "currency",
|
|
||||||
currency: currency,
|
|
||||||
currencyDisplay: "narrowSymbol",
|
|
||||||
notation: "compact",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Title level={5}>{t("filament.fields.density")}</Title>
|
<Title level={5}>{t("filament.fields.density")}</Title>
|
||||||
<NumberFieldUnit
|
<NumberFieldUnit
|
||||||
value={record?.density ?? ""}
|
value={record?.density ?? ""}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useTranslate } from "@refinedev/core";
|
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 { useEffect } from "react";
|
||||||
import { useGetSettings, useSetSetting } from "../../utils/querySettings";
|
import { useGetSettings, useSetSetting } from "../../utils/querySettings";
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ export function GeneralSettings() {
|
|||||||
const settings = useGetSettings();
|
const settings = useGetSettings();
|
||||||
const setBaseUrl = useSetSetting("base_url");
|
const setBaseUrl = useSetSetting("base_url");
|
||||||
const setCurrency = useSetSetting("currency");
|
const setCurrency = useSetSetting("currency");
|
||||||
|
const setRoundPrices = useSetSetting("round_prices");
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [messageApi, contextHolder] = message.useMessage();
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
@@ -17,6 +18,7 @@ export function GeneralSettings() {
|
|||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
currency: JSON.parse(settings.data.currency.value),
|
currency: JSON.parse(settings.data.currency.value),
|
||||||
base_url: JSON.parse(settings.data.base_url.value),
|
base_url: JSON.parse(settings.data.base_url.value),
|
||||||
|
round_prices: JSON.parse(settings.data.round_prices.value),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [settings.data, form]);
|
}, [settings.data, form]);
|
||||||
@@ -29,7 +31,7 @@ export function GeneralSettings() {
|
|||||||
}, [setCurrency.isSuccess, messageApi, t]);
|
}, [setCurrency.isSuccess, messageApi, t]);
|
||||||
|
|
||||||
// Handle form submit
|
// 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
|
// Check if the currency has changed
|
||||||
if (settings.data?.currency.value !== JSON.stringify(values.currency)) {
|
if (settings.data?.currency.value !== JSON.stringify(values.currency)) {
|
||||||
setCurrency.mutate(values.currency);
|
setCurrency.mutate(values.currency);
|
||||||
@@ -38,6 +40,11 @@ export function GeneralSettings() {
|
|||||||
if (settings.data?.base_url.value !== JSON.stringify(values.base_url)) {
|
if (settings.data?.base_url.value !== JSON.stringify(values.base_url)) {
|
||||||
setBaseUrl.mutate(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 (
|
return (
|
||||||
@@ -48,6 +55,7 @@ export function GeneralSettings() {
|
|||||||
wrapperCol={{ span: 16 }}
|
wrapperCol={{ span: 16 }}
|
||||||
initialValues={{
|
initialValues={{
|
||||||
currency: settings.data?.currency.value,
|
currency: settings.data?.currency.value,
|
||||||
|
round_prices: settings.data?.round_prices.value,
|
||||||
}}
|
}}
|
||||||
onFinish={onFinish}
|
onFinish={onFinish}
|
||||||
style={{
|
style={{
|
||||||
@@ -86,6 +94,15 @@ export function GeneralSettings() {
|
|||||||
<Input placeholder="https://example.com:8000" />
|
<Input placeholder="https://example.com:8000" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
<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 }}>
|
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
|
||||||
<Button type="primary" htmlType="submit" loading={settings.isFetching || setCurrency.isLoading}>
|
<Button type="primary" htmlType="submit" loading={settings.isFetching || setCurrency.isLoading}>
|
||||||
{t("buttons.save")}
|
{t("buttons.save")}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
import { removeUndefined } from "../../utils/filtering";
|
import { removeUndefined } from "../../utils/filtering";
|
||||||
import { EntityType, useGetFields } from "../../utils/queryFields";
|
import { EntityType, useGetFields } from "../../utils/queryFields";
|
||||||
import { TableState, useInitialTableState, useSavedState, useStoreInitialState } from "../../utils/saveload";
|
import { TableState, useInitialTableState, useSavedState, useStoreInitialState } from "../../utils/saveload";
|
||||||
import { useCurrency } from "../../utils/settings";
|
import { useCurrencyFormatter } from "../../utils/settings";
|
||||||
import { setSpoolArchived, useSpoolAdjustModal } from "./functions";
|
import { setSpoolArchived, useSpoolAdjustModal } from "./functions";
|
||||||
import { ISpool } from "./model";
|
import { ISpool } from "./model";
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
|||||||
const invalidate = useInvalidate();
|
const invalidate = useInvalidate();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const extraFields = useGetFields(EntityType.spool);
|
const extraFields = useGetFields(EntityType.spool);
|
||||||
const currency = useCurrency();
|
const currencyFormatter = useCurrencyFormatter();
|
||||||
const { openSpoolAdjustModal, spoolAdjustModal } = useSpoolAdjustModal();
|
const { openSpoolAdjustModal, spoolAdjustModal } = useSpoolAdjustModal();
|
||||||
|
|
||||||
const allColumnsWithExtraFields = [...allColumns, ...(extraFields.data?.map((field) => "extra." + field.key) ?? [])];
|
const allColumnsWithExtraFields = [...allColumns, ...(extraFields.data?.map((field) => "extra." + field.key) ?? [])];
|
||||||
@@ -381,12 +381,10 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
|||||||
align: "right",
|
align: "right",
|
||||||
width: 80,
|
width: 80,
|
||||||
render: (_, obj: ISpoolCollapsed) => {
|
render: (_, obj: ISpoolCollapsed) => {
|
||||||
return obj.price?.toLocaleString(undefined, {
|
if (obj.price === undefined) {
|
||||||
style: "currency",
|
return "";
|
||||||
currencyDisplay: "narrowSymbol",
|
}
|
||||||
currency: currency,
|
return currencyFormatter.format(obj.price);
|
||||||
notation: "compact",
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
NumberColumn({
|
NumberColumn({
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { NumberFieldUnit } from "../../components/numberField";
|
|||||||
import SpoolIcon from "../../components/spoolIcon";
|
import SpoolIcon from "../../components/spoolIcon";
|
||||||
import { enrichText } from "../../utils/parsing";
|
import { enrichText } from "../../utils/parsing";
|
||||||
import { EntityType, useGetFields } from "../../utils/queryFields";
|
import { EntityType, useGetFields } from "../../utils/queryFields";
|
||||||
import { useCurrency } from "../../utils/settings";
|
import { useCurrencyFormatter } from "../../utils/settings";
|
||||||
import { IFilament } from "../filaments/model";
|
import { IFilament } from "../filaments/model";
|
||||||
import { setSpoolArchived } from "./functions";
|
import { setSpoolArchived } from "./functions";
|
||||||
import { ISpool } from "./model";
|
import { ISpool } from "./model";
|
||||||
@@ -23,7 +23,7 @@ const { confirm } = Modal;
|
|||||||
export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
|
export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const extraFields = useGetFields(EntityType.spool);
|
const extraFields = useGetFields(EntityType.spool);
|
||||||
const currency = useCurrency();
|
const currencyFormatter = useCurrencyFormatter();
|
||||||
const invalidate = useInvalidate();
|
const invalidate = useInvalidate();
|
||||||
|
|
||||||
const { queryResult } = useShow<ISpool>({
|
const { queryResult } = useShow<ISpool>({
|
||||||
@@ -33,13 +33,12 @@ export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
|
|||||||
|
|
||||||
const record = data?.data;
|
const record = data?.data;
|
||||||
|
|
||||||
const spoolPrice = (item: ISpool) => {
|
const spoolPrice = (item?: ISpool) => {
|
||||||
let spoolPrice = "";
|
const price = item?.price ?? item?.filament.price;
|
||||||
if (item.price === undefined) {
|
if (price === undefined) {
|
||||||
spoolPrice = `${item.filament.price}`;
|
return "";
|
||||||
return spoolPrice;
|
|
||||||
}
|
}
|
||||||
return item.price;
|
return currencyFormatter.format(price);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function for opening an ant design modal that asks for confirmation for archiving a spool
|
// Function for opening an ant design modal that asks for confirmation for archiving a spool
|
||||||
@@ -141,15 +140,7 @@ export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
|
|||||||
<Title level={5}>{t("spool.fields.filament")}</Title>
|
<Title level={5}>{t("spool.fields.filament")}</Title>
|
||||||
{colorObj && <SpoolIcon color={colorObj} />} <TextField value={record ? filamentURL(record?.filament) : ""} />
|
{colorObj && <SpoolIcon color={colorObj} />} <TextField value={record ? filamentURL(record?.filament) : ""} />
|
||||||
<Title level={5}>{t("spool.fields.price")}</Title>
|
<Title level={5}>{t("spool.fields.price")}</Title>
|
||||||
<NumberField
|
<TextField value={spoolPrice(record)} />
|
||||||
value={record ? spoolPrice(record) : ""}
|
|
||||||
options={{
|
|
||||||
style: "currency",
|
|
||||||
currency: currency,
|
|
||||||
currencyDisplay: "narrowSymbol",
|
|
||||||
notation: "compact",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Title level={5}>{t("spool.fields.registered")}</Title>
|
<Title level={5}>{t("spool.fields.registered")}</Title>
|
||||||
<DateField
|
<DateField
|
||||||
value={dayjs.utc(record?.registered).local()}
|
value={dayjs.utc(record?.registered).local()}
|
||||||
|
|||||||
@@ -17,3 +17,15 @@ export function getCurrencySymbol(locale: string | undefined, currency: string)
|
|||||||
.replace(/\d/g, "")
|
.replace(/\d/g, "")
|
||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useCurrencyFormatter() {
|
||||||
|
const currency = useCurrency();
|
||||||
|
const roundPrices = JSON.parse(useGetSetting("round_prices").data?.value ?? "false");
|
||||||
|
|
||||||
|
return new Intl.NumberFormat(undefined, {
|
||||||
|
style: "currency",
|
||||||
|
currency: currency,
|
||||||
|
currencyDisplay: "narrowSymbol",
|
||||||
|
notation: roundPrices ? "compact" : "standard",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ def parse_setting(key: str) -> SettingDefinition:
|
|||||||
|
|
||||||
|
|
||||||
register_setting("currency", SettingType.STRING, json.dumps("EUR"))
|
register_setting("currency", SettingType.STRING, json.dumps("EUR"))
|
||||||
|
register_setting("round_prices", SettingType.BOOLEAN, json.dumps(obj=False))
|
||||||
register_setting("print_presets", SettingType.ARRAY, json.dumps([]))
|
register_setting("print_presets", SettingType.ARRAY, json.dumps([]))
|
||||||
|
|
||||||
register_setting("extra_fields_vendor", SettingType.ARRAY, json.dumps([]))
|
register_setting("extra_fields_vendor", SettingType.ARRAY, json.dumps([]))
|
||||||
|
|||||||
Reference in New Issue
Block a user