[PUI] Add more formatters (#5771)

* Added general data formatters

* Added usage of new date formatter

* Added usage of new date formatter

* Added usage of currency formatter

* style cleanup

* Moved to use real user and server settings

* fixed type

* Added in formatters again

* cleaned up unsued imports
This commit is contained in:
Matthias Mair 2023-11-05 23:25:13 +01:00 committed by GitHub
parent ea249c1dc5
commit 2d6a8a4bcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 151 additions and 16 deletions

View File

@ -3,6 +3,7 @@
*/
import { t } from '@lingui/macro';
import { formatCurrency, renderDate } from '../../defaults/formatters';
import { ProgressBar } from '../items/ProgressBar';
import { ModelType } from '../render/ModelType';
import { RenderOwner } from '../render/User';
@ -77,8 +78,9 @@ export function TargetDateColumn(): TableColumn {
return {
accessor: 'target_date',
title: t`Target Date`,
sortable: true
sortable: true,
// TODO: custom renderer which alerts user if target date is overdue
render: (record: any) => renderDate(record.target_date)
};
}
@ -86,7 +88,8 @@ export function CreationDateColumn(): TableColumn {
return {
accessor: 'creation_date',
title: t`Creation Date`,
sortable: true
sortable: true,
render: (record: any) => renderDate(record.creation_date)
};
}
@ -94,7 +97,8 @@ export function ShipmentDateColumn(): TableColumn {
return {
accessor: 'shipment_date',
title: t`Shipment Date`,
sortable: true
sortable: true,
render: (record: any) => renderDate(record.shipment_date)
};
}
@ -116,12 +120,10 @@ export function CurrencyColumn({
title: title ?? t`Currency`,
sortable: sortable ?? true,
render: (record: any) => {
let value = record[accessor];
let currency_key = currency_accessor ?? `${accessor}_currency`;
currency = currency ?? record[currency_key];
// TODO: A better render which correctly formats money values
return `${value} ${currency}`;
return formatCurrency(record[accessor], {
currency: currency ?? record[currency_key]
});
}
};
}

View File

@ -2,6 +2,7 @@ import { t } from '@lingui/macro';
import { useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { renderDate } from '../../../defaults/formatters';
import { useTableRefresh } from '../../../hooks/TableRefresh';
import { ApiPaths, apiUrl } from '../../../states/ApiState';
import { ThumbnailHoverCard } from '../../images/Thumbnail';
@ -78,7 +79,8 @@ function buildOrderTableColumns(): TableColumn[] {
{
accessor: 'completion_date',
sortable: true,
title: t`Completed`
title: t`Completed`,
render: (record: any) => renderDate(record.completion_date)
},
{
accessor: 'issued_by',

View File

@ -3,6 +3,7 @@ import { Group, Text } from '@mantine/core';
import { ReactNode, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { formatCurrency, renderDate } from '../../../defaults/formatters';
import { useTableRefresh } from '../../../hooks/TableRefresh';
import { ApiPaths, apiUrl } from '../../../states/ApiState';
import { Thumbnail } from '../../images/Thumbnail';
@ -167,13 +168,34 @@ function stockItemTableColumns(): TableColumn[] {
// TODO: Note, if not "In stock" we don't want to display the actual location here
return record?.location_detail?.pathstring ?? record.location ?? '-';
}
}
},
// TODO: stocktake column
// TODO: expiry date
// TODO: last updated
{
accessor: 'expiry_date',
sortable: true,
title: t`Expiry Date`,
switchable: true,
render: (record: any) => renderDate(record.expiry_date)
},
{
accessor: 'updated',
sortable: true,
title: t`Last Updated`,
switchable: true,
render: (record: any) => renderDate(record.updated)
},
// TODO: purchase order
// TODO: Supplier part
// TODO: purchase price
{
accessor: 'purchase_price',
sortable: true,
title: t`Purchase Price`,
switchable: true,
render: (record: any) =>
formatCurrency(record.purchase_price, {
currency: record.purchase_price_currency
})
}
// TODO: stock value
// TODO: packaging
// TODO: notes

View File

@ -0,0 +1,86 @@
import dayjs from 'dayjs';
import {
useGlobalSettingsState,
useUserSettingsState
} from '../states/SettingsState';
interface formatCurrencyOptionsType {
digits?: number;
minDigits?: number;
currency?: string;
locale?: string;
}
/*
* format currency (money) value based on current settings
*
* Options:
* - currency: Currency code (uses default value if none provided)
* - locale: Locale specified (uses default value if none provided)
* - digits: Maximum number of significant digits (default = 10)
*/
export function formatCurrency(
value: number,
options: formatCurrencyOptionsType = {}
) {
if (value == null) {
return null;
}
const global_settings = useGlobalSettingsState.getState().lookup;
let maxDigits = options.digits || global_settings.PRICING_DECIMAL_PLACES || 6;
maxDigits = Number(maxDigits);
let minDigits =
options.minDigits || global_settings.PRICING_DECIMAL_PLACES_MIN || 0;
minDigits = Number(minDigits);
// Extract default currency information
let currency =
options.currency || global_settings.INVENTREE_DEFAULT_CURRENCY || 'USD';
// Extract locale information
let locale = options.locale || navigator.language || 'en-US';
let formatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency,
maximumFractionDigits: maxDigits,
minimumFractionDigits: minDigits
});
return formatter.format(value);
}
interface renderDateOptionsType {
showTime?: boolean;
}
/*
* Render the provided date in the user-specified format.
*
* The provided "date" variable is a string, nominally ISO format e.g. 2022-02-22
* The user-configured setting DATE_DISPLAY_FORMAT determines how the date should be displayed.
*/
export function renderDate(date: string, options: renderDateOptionsType = {}) {
if (!date) {
return '-';
}
const user_settings = useUserSettingsState.getState().lookup;
let fmt = user_settings.DATE_DISPLAY_FORMAT || 'YYYY-MM-DD';
if (options.showTime) {
fmt += ' HH:mm';
}
const m = dayjs(date);
if (m.isValid()) {
return m.format(fmt);
} else {
// Invalid input string, simply return provided value
return date;
}
}

View File

@ -5,10 +5,11 @@ import { create } from 'zustand';
import { api } from '../App';
import { ApiPaths, apiUrl } from './ApiState';
import { Setting } from './states';
import { Setting, SettingsLookup } from './states';
export interface SettingsStateProps {
settings: Setting[];
lookup: SettingsLookup;
fetchSettings: () => void;
endpoint: ApiPaths;
}
@ -19,12 +20,16 @@ export interface SettingsStateProps {
export const useGlobalSettingsState = create<SettingsStateProps>(
(set, get) => ({
settings: [],
lookup: {},
endpoint: ApiPaths.settings_global_list,
fetchSettings: async () => {
await api
.get(apiUrl(ApiPaths.settings_global_list))
.then((response) => {
set({ settings: response.data });
set({
settings: response.data,
lookup: generate_lookup(response.data)
});
})
.catch((error) => {
console.error('Error fetching global settings:', error);
@ -38,15 +43,30 @@ export const useGlobalSettingsState = create<SettingsStateProps>(
*/
export const useUserSettingsState = create<SettingsStateProps>((set, get) => ({
settings: [],
lookup: {},
endpoint: ApiPaths.settings_user_list,
fetchSettings: async () => {
await api
.get(apiUrl(ApiPaths.settings_user_list))
.then((response) => {
set({ settings: response.data });
set({
settings: response.data,
lookup: generate_lookup(response.data)
});
})
.catch((error) => {
console.error('Error fetching user settings:', error);
});
}
}));
/*
return a lookup dictionary for the value of the provided Setting list
*/
function generate_lookup(data: Setting[]) {
let lookup_dir: SettingsLookup = {};
for (let setting of data) {
lookup_dir[setting.key] = setting.value;
}
return lookup_dir;
}

View File

@ -87,3 +87,6 @@ export type ErrorResponse = {
statusText: string;
message?: string;
};
export type SettingsLookup = {
[key: string]: string;
};