From 1640f605a0a2923b37ea09f1f0533bee3a9cc26e Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 4 Nov 2023 17:47:39 +1100 Subject: [PATCH] [React] Currency table (#5858) * Add basic table for displaying exchange rates - Needed to extend current functionality for non-standard API endpoint data * Add heading * Add button to reload exchange rates --- .../src/components/nav/PanelGroup.tsx | 2 +- .../src/components/tables/InvenTreeTable.tsx | 13 ++- .../tables/settings/CurrencyTable.tsx | 82 +++++++++++++++++++ .../pages/Index/Settings/SystemSettings.tsx | 7 +- src/frontend/src/states/ApiState.tsx | 7 ++ 5 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 src/frontend/src/components/tables/settings/CurrencyTable.tsx diff --git a/src/frontend/src/components/nav/PanelGroup.tsx b/src/frontend/src/components/nav/PanelGroup.tsx index 058bf87763..1e939e2050 100644 --- a/src/frontend/src/components/nav/PanelGroup.tsx +++ b/src/frontend/src/components/nav/PanelGroup.tsx @@ -131,7 +131,7 @@ export function PanelGroup({ }} > - {panel.label} + {panel.label} {panel.content ?? } diff --git a/src/frontend/src/components/tables/InvenTreeTable.tsx b/src/frontend/src/components/tables/InvenTreeTable.tsx index 2e54456872..ff16019ada 100644 --- a/src/frontend/src/components/tables/InvenTreeTable.tsx +++ b/src/frontend/src/components/tables/InvenTreeTable.tsx @@ -40,6 +40,7 @@ const defaultPageSize: number = 25; * @param customFilters : TableFilter[] - List of custom filters * @param customActionGroups : any[] - List of custom action groups * @param printingActions : any[] - List of printing actions + * @param dataFormatter : (data: any) => any - Callback function to reformat data returned by server (if not in default format) * @param rowActions : (record: any) => RowAction[] - Callback function to generate row actions * @param onRowClick : (record: any, index: number, event: any) => void - Callback function when a row is clicked */ @@ -59,6 +60,7 @@ export type InvenTreeTableProps = { customActionGroups?: any[]; printingActions?: any[]; idAccessor?: string; + dataFormatter?: (data: any) => any; rowActions?: (record: any) => RowAction[]; onRowClick?: (record: any, index: number, event: any) => void; }; @@ -356,8 +358,15 @@ export function InvenTreeTable({ tableProps.noRecordsText ?? t`No records found` ); - // Extract returned data (accounting for pagination) and ensure it is a list - let results = response.data?.results ?? response.data ?? []; + let results = []; + + if (props.dataFormatter) { + // Custom data formatter provided + results = props.dataFormatter(response.data); + } else { + // Extract returned data (accounting for pagination) and ensure it is a list + results = response.data?.results ?? response.data ?? []; + } if (!Array.isArray(results)) { setMissingRecordsText(t`Server returned incorrect data type`); diff --git a/src/frontend/src/components/tables/settings/CurrencyTable.tsx b/src/frontend/src/components/tables/settings/CurrencyTable.tsx new file mode 100644 index 0000000000..eb4c205ddd --- /dev/null +++ b/src/frontend/src/components/tables/settings/CurrencyTable.tsx @@ -0,0 +1,82 @@ +import { t } from '@lingui/macro'; +import { showNotification } from '@mantine/notifications'; +import { IconReload } from '@tabler/icons-react'; +import { useCallback, useMemo } from 'react'; + +import { api } from '../../../App'; +import { useTableRefresh } from '../../../hooks/TableRefresh'; +import { ApiPaths, apiUrl } from '../../../states/ApiState'; +import { ActionButton } from '../../buttons/ActionButton'; +import { InvenTreeTable } from '../InvenTreeTable'; + +/* + * Table for displaying available currencies + */ +export function CurrencyTable() { + const { tableKey, refreshTable } = useTableRefresh('currency'); + + const columns = useMemo(() => { + return [ + { + accessor: 'currency', + title: t`Currency`, + switchable: false + }, + { + accessor: 'rate', + title: t`Rate`, + switchable: false + } + ]; + }, []); + + const refreshCurrencies = useCallback(() => { + api + .post(apiUrl(ApiPaths.currency_refresh), {}) + .then(() => { + refreshTable(); + showNotification({ + message: t`Exchange rates updated`, + color: 'green' + }); + }) + .catch((error) => { + showNotification({ + title: t`Exchange rate update error`, + message: error, + color: 'red' + }); + }); + }, []); + + const tableActions = useMemo(() => { + return [ + } + /> + ]; + }, []); + + return ( + { + let rates = data?.exchange_rates ?? {}; + + return Object.entries(rates).map(([currency, rate]) => { + return { + currency: currency, + rate: rate + }; + }); + } + }} + /> + ); +} diff --git a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx index 32c3c21795..fbeaa428f1 100644 --- a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx @@ -21,10 +21,11 @@ import { } from '@tabler/icons-react'; import { useMemo } from 'react'; -import { PlaceholderPill } from '../../../components/items/Placeholder'; +import { StylishText } from '../../../components/items/StylishText'; import { PanelGroup, PanelType } from '../../../components/nav/PanelGroup'; import { SettingsHeader } from '../../../components/nav/SettingsHeader'; import { GlobalSettingList } from '../../../components/settings/SettingList'; +import { CurrencyTable } from '../../../components/tables/settings/CurrencyTable'; import { CustomUnitsTable } from '../../../components/tables/settings/CustomUnitsTable'; import { ProjectCodeTable } from '../../../components/tables/settings/ProjectCodeTable'; import { useServerApiState } from '../../../states/ApiState'; @@ -152,7 +153,9 @@ export default function SystemSettings() { - + {t`Exchange Rates`} + + ) }, diff --git a/src/frontend/src/states/ApiState.tsx b/src/frontend/src/states/ApiState.tsx index 2aff116728..2bbac38c8d 100644 --- a/src/frontend/src/states/ApiState.tsx +++ b/src/frontend/src/states/ApiState.tsx @@ -71,6 +71,9 @@ export enum ApiPaths { settings_user_list = 'api-settings-user-list', notifications_list = 'api-notifications-list', + currency_list = 'api-currency-list', + currency_refresh = 'api-currency-refresh', + barcode = 'api-barcode', news = 'news', global_status = 'api-global-status', @@ -165,6 +168,10 @@ export function apiEndpoint(path: ApiPaths): string { return 'auth/emails/$id/verify/'; case ApiPaths.user_email_primary: return 'auth/emails/$id/primary/'; + case ApiPaths.currency_list: + return 'currency/exchange/'; + case ApiPaths.currency_refresh: + return 'currency/refresh/'; case ApiPaths.api_search: return 'search/'; case ApiPaths.settings_global_list: