Merge pull request #582 from ErikMichelson/feat/round-prices-setting

Add setting to toggle rounding of prices
This commit is contained in:
Donkie
2025-02-24 20:56:44 +01:00
committed by GitHub
8 changed files with 59 additions and 46 deletions

View File

@@ -316,6 +316,10 @@
"base_url": {
"label": "Base URL",
"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": {

View File

@@ -26,7 +26,7 @@ import {
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

@@ -10,7 +10,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 "./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",
});
@@ -93,15 +93,7 @@ export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
<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

@@ -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,6 +40,11 @@ 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 (
@@ -48,6 +55,7 @@ export function GeneralSettings() {
wrapperCol={{ span: 16 }}
initialValues={{
currency: settings.data?.currency.value,
round_prices: settings.data?.round_prices.value,
}}
onFinish={onFinish}
style={{
@@ -86,6 +94,15 @@ export function GeneralSettings() {
<Input placeholder="https://example.com:8000" />
</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 }}>
<Button type="primary" htmlType="submit" loading={settings.isFetching || setCurrency.isLoading}>
{t("buttons.save")}

View File

@@ -36,7 +36,7 @@ import {
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

@@ -10,7 +10,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 } from "./functions";
import { ISpool } from "./model";
@@ -23,7 +23,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 +33,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);
};
// Function for opening an ant design modal that asks for confirmation for archiving a spool
@@ -142,15 +141,7 @@ export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
{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

@@ -17,3 +17,15 @@ export function getCurrencySymbol(locale: string | undefined, currency: string)
.replace(/\d/g, "")
.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",
});
}

View File

@@ -62,6 +62,7 @@ def parse_setting(key: str) -> SettingDefinition:
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("extra_fields_vendor", SettingType.ARRAY, json.dumps([]))