mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[PUI] Render status labels (#5759)
* added deepsource coverage settings * Ignore missing coverage * trigger full CI run * typo * Added general status lookup endpoint * Bumped API version * cleaned up branch * Fixed PlaygroundArea accordion behaviour * Added dummy area for status labels * Added StatusRenderer skeleton * Fetch data from server * Made server api state session persistant * cleanup * Added StatusLabel lookups based on ModelType * Made use of translated status fields * Added new ModelTypes * Used new StatusRenderer * Simplified renderer * style fixes * revert style change * Squashed commit of the following: commit 5e8ea099068475fd257d8c172348dc6f3edf9bcf Author: Matthias Mair <code@mjmair.com> Date: Tue Oct 24 09:22:38 2023 +0200 Update ui_plattform.spec.ts commit 49da3312beff7fd6837ea741e621df221c445d19 Author: Matthias Mair <code@mjmair.com> Date: Tue Oct 24 07:56:25 2023 +0200 more logging commit 5337be4c3990051b805a6fce2e79ca4030b4afe5 Author: Matthias Mair <code@mjmair.com> Date: Tue Oct 24 07:56:11 2023 +0200 added filter method for undefined settings that overwrite defaults commit 5df8a0b3e77cd5dcf04c39ad7638ac845df75e4c Author: Matthias Mair <code@mjmair.com> Date: Tue Oct 24 03:05:06 2023 +0200 you do not need to string a string commit 0650d3b3a0132889c2a76de38db38224e974d205 Author: Matthias Mair <code@mjmair.com> Date: Tue Oct 24 03:04:34 2023 +0200 fix things that were borken for no good reason commit a40dbfd1364cf01465037350184f59d2a2a8afab Author: Matthias Mair <code@mjmair.com> Date: Tue Oct 24 02:39:34 2023 +0200 reduce unneeded blocking timeouts commit bf9046a5361ae919e70662e717d6156434b6fe43 Author: Matthias Mair <code@mjmair.com> Date: Tue Oct 24 02:34:10 2023 +0200 catch server fetching errors commit aa01e67e8c8e789fdf755ac4481e730fe5ea4183 Author: Matthias Mair <code@mjmair.com> Date: Tue Oct 24 02:33:29 2023 +0200 move init as things are now plugged together different commit 290c33bd3125d50779497d6fc5981d5813b58f5d Author: Matthias Mair <code@mjmair.com> Date: Tue Oct 24 01:49:32 2023 +0200 do not log a failed automatic login try - why would you?
This commit is contained in:
parent
2ff2c0801a
commit
53c16510a1
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 141
|
INVENTREE_API_VERSION = 142
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
||||||
|
|
||||||
|
v142 -> 2023-10-20: https://github.com/inventree/InvenTree/pull/5759
|
||||||
|
- Adds generic API endpoints for looking up status models
|
||||||
|
|
||||||
v141 -> 2023-10-23 : https://github.com/inventree/InvenTree/pull/5774
|
v141 -> 2023-10-23 : https://github.com/inventree/InvenTree/pull/5774
|
||||||
- Changed 'part.responsible' from User to Owner
|
- Changed 'part.responsible' from User to Owner
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ from rest_framework.views import APIView
|
|||||||
|
|
||||||
import common.models
|
import common.models
|
||||||
import common.serializers
|
import common.serializers
|
||||||
|
from generic.states.api import AllStatusViews, StatusView
|
||||||
from InvenTree.api import BulkDeleteMixin, MetadataView
|
from InvenTree.api import BulkDeleteMixin, MetadataView
|
||||||
from InvenTree.config import CONFIG_LOOKUPS
|
from InvenTree.config import CONFIG_LOOKUPS
|
||||||
from InvenTree.filters import ORDER_FILTER, SEARCH_ORDER_FILTER
|
from InvenTree.filters import ORDER_FILTER, SEARCH_ORDER_FILTER
|
||||||
@ -617,6 +618,14 @@ common_api_urls = [
|
|||||||
path('<str:key>/', FlagDetail.as_view(), name='api-flag-detail'),
|
path('<str:key>/', FlagDetail.as_view(), name='api-flag-detail'),
|
||||||
re_path(r'^.*$', FlagList.as_view(), name='api-flag-list'),
|
re_path(r'^.*$', FlagList.as_view(), name='api-flag-list'),
|
||||||
])),
|
])),
|
||||||
|
|
||||||
|
# Status
|
||||||
|
path('generic/status/', include([
|
||||||
|
path(f'<str:{StatusView.MODEL_REF}>/', include([
|
||||||
|
path('', StatusView.as_view(), name='api-status'),
|
||||||
|
])),
|
||||||
|
path('', AllStatusViews.as_view(), name='api-status-all'),
|
||||||
|
])),
|
||||||
]
|
]
|
||||||
|
|
||||||
admin_api_urls = [
|
admin_api_urls = [
|
||||||
|
@ -26,7 +26,7 @@ class StatusView(APIView):
|
|||||||
MODEL_REF = 'statusmodel'
|
MODEL_REF = 'statusmodel'
|
||||||
|
|
||||||
def get_status_model(self, *args, **kwargs):
|
def get_status_model(self, *args, **kwargs):
|
||||||
"""Return the StatusCode moedl based on extra parameters passed to the view"""
|
"""Return the StatusCode model based on extra parameters passed to the view"""
|
||||||
status_model = self.kwargs.get(self.MODEL_REF, None)
|
status_model = self.kwargs.get(self.MODEL_REF, None)
|
||||||
|
|
||||||
if status_model is None:
|
if status_model is None:
|
||||||
@ -50,3 +50,23 @@ class StatusView(APIView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
|
class AllStatusViews(StatusView):
|
||||||
|
"""Endpoint for listing all defined status models."""
|
||||||
|
|
||||||
|
permission_classes = [
|
||||||
|
permissions.IsAuthenticated,
|
||||||
|
]
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
"""Perform a GET request to learn information about status codes"""
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
for status_class in StatusCode.__subclasses__():
|
||||||
|
data[status_class.__name__] = {
|
||||||
|
'class': status_class.__name__,
|
||||||
|
'values': status_class.dict(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(data)
|
||||||
|
@ -324,9 +324,9 @@ export function SearchDrawer({
|
|||||||
// Callback when one of the search results is clicked
|
// Callback when one of the search results is clicked
|
||||||
function onResultClick(query: ModelType, pk: number) {
|
function onResultClick(query: ModelType, pk: number) {
|
||||||
closeDrawer();
|
closeDrawer();
|
||||||
navigate(
|
const targetModel = ModelInformationDict[query];
|
||||||
ModelInformationDict[query].url_detail.replace(':pk', pk.toString())
|
if (targetModel.url_detail == undefined) return;
|
||||||
);
|
navigate(targetModel.url_detail.replace(':pk', pk.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -46,11 +46,13 @@ const RendererLookup: EnumDictionary<
|
|||||||
[ModelType.partcategory]: RenderPartCategory,
|
[ModelType.partcategory]: RenderPartCategory,
|
||||||
[ModelType.partparametertemplate]: RenderPartParameterTemplate,
|
[ModelType.partparametertemplate]: RenderPartParameterTemplate,
|
||||||
[ModelType.purchaseorder]: RenderPurchaseOrder,
|
[ModelType.purchaseorder]: RenderPurchaseOrder,
|
||||||
|
[ModelType.purchaseorderline]: RenderPurchaseOrder,
|
||||||
[ModelType.returnorder]: RenderReturnOrder,
|
[ModelType.returnorder]: RenderReturnOrder,
|
||||||
[ModelType.salesorder]: RenderSalesOrder,
|
[ModelType.salesorder]: RenderSalesOrder,
|
||||||
[ModelType.salesordershipment]: RenderSalesOrderShipment,
|
[ModelType.salesordershipment]: RenderSalesOrderShipment,
|
||||||
[ModelType.stocklocation]: RenderStockLocation,
|
[ModelType.stocklocation]: RenderStockLocation,
|
||||||
[ModelType.stockitem]: RenderStockItem,
|
[ModelType.stockitem]: RenderStockItem,
|
||||||
|
[ModelType.stockhistory]: RenderStockItem,
|
||||||
[ModelType.supplierpart]: RenderSupplierPart,
|
[ModelType.supplierpart]: RenderSupplierPart,
|
||||||
[ModelType.user]: RenderUser,
|
[ModelType.user]: RenderUser,
|
||||||
[ModelType.manufacturerpart]: RenderPart
|
[ModelType.manufacturerpart]: RenderPart
|
||||||
|
@ -8,9 +8,11 @@ export enum ModelType {
|
|||||||
partparametertemplate = 'partparametertemplate',
|
partparametertemplate = 'partparametertemplate',
|
||||||
stockitem = 'stockitem',
|
stockitem = 'stockitem',
|
||||||
stocklocation = 'stocklocation',
|
stocklocation = 'stocklocation',
|
||||||
|
stockhistory = 'stockhistory',
|
||||||
build = 'build',
|
build = 'build',
|
||||||
company = 'company',
|
company = 'company',
|
||||||
purchaseorder = 'purchaseorder',
|
purchaseorder = 'purchaseorder',
|
||||||
|
purchaseorderline = 'purchaseorderline',
|
||||||
salesorder = 'salesorder',
|
salesorder = 'salesorder',
|
||||||
salesordershipment = 'salesordershipment',
|
salesordershipment = 'salesordershipment',
|
||||||
returnorder = 'returnorder',
|
returnorder = 'returnorder',
|
||||||
@ -23,8 +25,8 @@ export enum ModelType {
|
|||||||
interface ModelInformatonInterface {
|
interface ModelInformatonInterface {
|
||||||
label: string;
|
label: string;
|
||||||
label_multiple: string;
|
label_multiple: string;
|
||||||
url_overview: string;
|
url_overview?: string;
|
||||||
url_detail: string;
|
url_detail?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ModelDictory = {
|
type ModelDictory = {
|
||||||
@ -74,6 +76,10 @@ export const ModelInformationDict: ModelDictory = {
|
|||||||
url_overview: '/stocklocation',
|
url_overview: '/stocklocation',
|
||||||
url_detail: '/stocklocation/:pk/'
|
url_detail: '/stocklocation/:pk/'
|
||||||
},
|
},
|
||||||
|
stockhistory: {
|
||||||
|
label: t`Stock History`,
|
||||||
|
label_multiple: t`Stock Histories`
|
||||||
|
},
|
||||||
build: {
|
build: {
|
||||||
label: t`Build`,
|
label: t`Build`,
|
||||||
label_multiple: t`Builds`,
|
label_multiple: t`Builds`,
|
||||||
@ -92,6 +98,10 @@ export const ModelInformationDict: ModelDictory = {
|
|||||||
url_overview: '/purchaseorder',
|
url_overview: '/purchaseorder',
|
||||||
url_detail: '/purchaseorder/:pk/'
|
url_detail: '/purchaseorder/:pk/'
|
||||||
},
|
},
|
||||||
|
purchaseorderline: {
|
||||||
|
label: t`Purchase Order Line`,
|
||||||
|
label_multiple: t`Purchase Order Lines`
|
||||||
|
},
|
||||||
salesorder: {
|
salesorder: {
|
||||||
label: t`Sales Order`,
|
label: t`Sales Order`,
|
||||||
label_multiple: t`Sales Orders`,
|
label_multiple: t`Sales Orders`,
|
||||||
|
96
src/frontend/src/components/renderers/StatusRenderer.tsx
Normal file
96
src/frontend/src/components/renderers/StatusRenderer.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { Badge, MantineSize } from '@mantine/core';
|
||||||
|
|
||||||
|
import { colorMap } from '../../defaults/backendMappings';
|
||||||
|
import { useServerApiState } from '../../states/ApiState';
|
||||||
|
import { ModelType } from '../render/ModelType';
|
||||||
|
|
||||||
|
interface StatusCodeInterface {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatusCodeListInterface {
|
||||||
|
[key: string]: StatusCodeInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface renderStatusLabelOptionsInterface {
|
||||||
|
size?: MantineSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic function to render a status label
|
||||||
|
*/
|
||||||
|
function renderStatusLabel(
|
||||||
|
key: string,
|
||||||
|
codes: StatusCodeListInterface,
|
||||||
|
options: renderStatusLabelOptionsInterface = {}
|
||||||
|
) {
|
||||||
|
let text = null;
|
||||||
|
let color = null;
|
||||||
|
|
||||||
|
// Find the entry which matches the provided key
|
||||||
|
for (let name in codes) {
|
||||||
|
let entry = codes[name];
|
||||||
|
|
||||||
|
if (entry.key == key) {
|
||||||
|
text = entry.label;
|
||||||
|
color = entry.color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!text) {
|
||||||
|
console.error(`renderStatusLabel could not find match for code ${key}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallbacks
|
||||||
|
if (color == null) color = 'default';
|
||||||
|
color = colorMap[color] || colorMap['default'];
|
||||||
|
const size = options.size || 'xs';
|
||||||
|
|
||||||
|
if (!text) {
|
||||||
|
text = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge color={color} variant="filled" size={size}>
|
||||||
|
{text}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Render the status for a object.
|
||||||
|
* Uses the values specified in "status_codes.py"
|
||||||
|
*/
|
||||||
|
export const StatusRenderer = ({
|
||||||
|
status,
|
||||||
|
type,
|
||||||
|
options
|
||||||
|
}: {
|
||||||
|
status: string;
|
||||||
|
type: ModelType;
|
||||||
|
options?: renderStatusLabelOptionsInterface;
|
||||||
|
}) => {
|
||||||
|
const [statusCodeList] = useServerApiState((state) => [state.status]);
|
||||||
|
if (statusCodeList === undefined) {
|
||||||
|
console.log('StatusRenderer: statusCodeList is undefined');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const statusCodes = statusCodeList[type];
|
||||||
|
if (statusCodes === undefined) {
|
||||||
|
console.log('StatusRenderer: statusCodes is undefined');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderStatusLabel(status, statusCodes, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Render the status badge in a table
|
||||||
|
*/
|
||||||
|
export function TableStatusRenderer(
|
||||||
|
type: ModelType
|
||||||
|
): ((record: any) => any) | undefined {
|
||||||
|
return (record: any) => StatusRenderer({ status: record.status, type: type });
|
||||||
|
}
|
@ -1,11 +1,13 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Progress, Text } from '@mantine/core';
|
import { Progress } from '@mantine/core';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { ThumbnailHoverCard } from '../../images/Thumbnail';
|
import { ThumbnailHoverCard } from '../../images/Thumbnail';
|
||||||
|
import { ModelType } from '../../render/ModelType';
|
||||||
|
import { TableStatusRenderer } from '../../renderers/StatusRenderer';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
@ -82,8 +84,8 @@ function buildOrderTableColumns(): TableColumn[] {
|
|||||||
accessor: 'status',
|
accessor: 'status',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
title: t`Status`,
|
title: t`Status`,
|
||||||
switchable: true
|
switchable: true,
|
||||||
// TODO: Custom render function here (status label)
|
render: TableStatusRenderer(ModelType.build)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'priority',
|
accessor: 'priority',
|
||||||
|
@ -5,6 +5,8 @@ import { useMemo } from 'react';
|
|||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { ModelType } from '../../render/ModelType';
|
||||||
|
import { StatusRenderer } from '../../renderers/StatusRenderer';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
export function PurchaseOrderTable({ params }: { params?: any }) {
|
export function PurchaseOrderTable({ params }: { params?: any }) {
|
||||||
@ -59,8 +61,12 @@ export function PurchaseOrderTable({ params }: { params?: any }) {
|
|||||||
accessor: 'status',
|
accessor: 'status',
|
||||||
title: t`Status`,
|
title: t`Status`,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
switchable: true
|
switchable: true,
|
||||||
// TODO: Custom formatter
|
render: (record: any) =>
|
||||||
|
StatusRenderer({
|
||||||
|
status: record.status,
|
||||||
|
type: ModelType.purchaseorder
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'creation_date',
|
accessor: 'creation_date',
|
||||||
|
@ -5,6 +5,8 @@ import { useMemo } from 'react';
|
|||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { ModelType } from '../../render/ModelType';
|
||||||
|
import { TableStatusRenderer } from '../../renderers/StatusRenderer';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
export function ReturnOrderTable({ params }: { params?: any }) {
|
export function ReturnOrderTable({ params }: { params?: any }) {
|
||||||
@ -58,8 +60,8 @@ export function ReturnOrderTable({ params }: { params?: any }) {
|
|||||||
accessor: 'status',
|
accessor: 'status',
|
||||||
title: t`Status`,
|
title: t`Status`,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
switchable: true
|
switchable: true,
|
||||||
// TODO: Custom formatter
|
render: TableStatusRenderer(ModelType.returnorder)
|
||||||
}
|
}
|
||||||
// TODO: Creation date
|
// TODO: Creation date
|
||||||
// TODO: Target date
|
// TODO: Target date
|
||||||
|
@ -5,6 +5,8 @@ import { useMemo } from 'react';
|
|||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { ModelType } from '../../render/ModelType';
|
||||||
|
import { TableStatusRenderer } from '../../renderers/StatusRenderer';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
export function SalesOrderTable({ params }: { params?: any }) {
|
export function SalesOrderTable({ params }: { params?: any }) {
|
||||||
@ -59,8 +61,8 @@ export function SalesOrderTable({ params }: { params?: any }) {
|
|||||||
accessor: 'status',
|
accessor: 'status',
|
||||||
title: t`Status`,
|
title: t`Status`,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
switchable: true
|
switchable: true,
|
||||||
// TODO: Custom formatter
|
render: TableStatusRenderer(ModelType.salesorder)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Creation date
|
// TODO: Creation date
|
||||||
|
@ -7,6 +7,8 @@ import { notYetImplemented } from '../../../functions/notifications';
|
|||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { ModelType } from '../../render/ModelType';
|
||||||
|
import { TableStatusRenderer } from '../../renderers/StatusRenderer';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
import { RowAction } from '../RowActions';
|
import { RowAction } from '../RowActions';
|
||||||
@ -52,8 +54,8 @@ function stockItemTableColumns(): TableColumn[] {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
switchable: true,
|
switchable: true,
|
||||||
filter: true,
|
filter: true,
|
||||||
title: t`Status`
|
title: t`Status`,
|
||||||
// TODO: Custom renderer for stock status label
|
render: TableStatusRenderer(ModelType.stockitem)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'batch',
|
accessor: 'batch',
|
||||||
|
29
src/frontend/src/defaults/backendMappings.tsx
Normal file
29
src/frontend/src/defaults/backendMappings.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { ModelType } from '../components/render/ModelType';
|
||||||
|
|
||||||
|
/* Lookup tables for mapping backend responses to internal types */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of status codes which are used in the backend
|
||||||
|
* and the model type they are associated with
|
||||||
|
*/
|
||||||
|
export const statusCodeList: Record<string, ModelType> = {
|
||||||
|
BuildStatus: ModelType.build,
|
||||||
|
PurchaseOrderStatus: ModelType.purchaseorder,
|
||||||
|
ReturnOrderLineStatus: ModelType.purchaseorderline,
|
||||||
|
ReturnOrderStatus: ModelType.returnorder,
|
||||||
|
SalesOrderStatus: ModelType.salesorder,
|
||||||
|
StockHistoryCode: ModelType.stockhistory,
|
||||||
|
StockStatus: ModelType.stockitem
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map the colors used in the backend to the colors used in the frontend
|
||||||
|
*/
|
||||||
|
export const colorMap: { [key: string]: string } = {
|
||||||
|
dark: 'dark',
|
||||||
|
warning: 'yellow',
|
||||||
|
success: 'green',
|
||||||
|
info: 'cyan',
|
||||||
|
danger: 'red',
|
||||||
|
default: 'gray'
|
||||||
|
};
|
@ -1,13 +1,15 @@
|
|||||||
import { Trans } from '@lingui/macro';
|
import { Trans } from '@lingui/macro';
|
||||||
import { Button } from '@mantine/core';
|
import { Button, TextInput } from '@mantine/core';
|
||||||
import { Group, Text } from '@mantine/core';
|
import { Group, Text } from '@mantine/core';
|
||||||
import { Accordion } from '@mantine/core';
|
import { Accordion } from '@mantine/core';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode, useState } from 'react';
|
||||||
|
|
||||||
import { ApiFormProps } from '../../components/forms/ApiForm';
|
import { ApiFormProps } from '../../components/forms/ApiForm';
|
||||||
import { ApiFormChangeCallback } from '../../components/forms/fields/ApiFormField';
|
import { ApiFormChangeCallback } from '../../components/forms/fields/ApiFormField';
|
||||||
import { PlaceholderPill } from '../../components/items/Placeholder';
|
import { PlaceholderPill } from '../../components/items/Placeholder';
|
||||||
import { StylishText } from '../../components/items/StylishText';
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
|
import { ModelType } from '../../components/render/ModelType';
|
||||||
|
import { StatusRenderer } from '../../components/renderers/StatusRenderer';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../../functions/forms';
|
import { openCreateApiForm, openEditApiForm } from '../../functions/forms';
|
||||||
import {
|
import {
|
||||||
createPart,
|
createPart,
|
||||||
@ -60,6 +62,23 @@ function ApiFormsPlayground() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show some example status labels
|
||||||
|
function StatusLabelPlayground() {
|
||||||
|
const [status, setStatus] = useState<string>('10');
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Group>
|
||||||
|
<Text>Stock Status</Text>
|
||||||
|
<TextInput
|
||||||
|
value={status}
|
||||||
|
onChange={(event) => setStatus(event.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
<StatusRenderer type={ModelType.stockitem} status={status} />
|
||||||
|
</Group>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** Construct a simple accordion group with title and content */
|
/** Construct a simple accordion group with title and content */
|
||||||
function PlaygroundArea({
|
function PlaygroundArea({
|
||||||
title,
|
title,
|
||||||
@ -95,10 +114,11 @@ export default function Playground() {
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Accordion defaultValue="">
|
<Accordion defaultValue="">
|
||||||
|
<PlaygroundArea title="API Forms" content={<ApiFormsPlayground />} />
|
||||||
<PlaygroundArea
|
<PlaygroundArea
|
||||||
title="API Forms"
|
title="Status labels"
|
||||||
content={<ApiFormsPlayground />}
|
content={<StatusLabelPlayground />}
|
||||||
></PlaygroundArea>
|
/>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,28 +1,52 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
import { api } from '../App';
|
import { api } from '../App';
|
||||||
|
import { ModelType } from '../components/render/ModelType';
|
||||||
|
import { StatusCodeListInterface } from '../components/renderers/StatusRenderer';
|
||||||
|
import { statusCodeList } from '../defaults/backendMappings';
|
||||||
import { emptyServerAPI } from '../defaults/defaults';
|
import { emptyServerAPI } from '../defaults/defaults';
|
||||||
import { ServerAPIProps, UserProps } from './states';
|
import { ServerAPIProps, UserProps } from './states';
|
||||||
|
|
||||||
|
type StatusLookup = Record<ModelType, StatusCodeListInterface>;
|
||||||
|
|
||||||
interface ServerApiStateProps {
|
interface ServerApiStateProps {
|
||||||
server: ServerAPIProps;
|
server: ServerAPIProps;
|
||||||
setServer: (newServer: ServerAPIProps) => void;
|
setServer: (newServer: ServerAPIProps) => void;
|
||||||
fetchServerApiState: () => void;
|
fetchServerApiState: () => void;
|
||||||
|
status: StatusLookup | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useServerApiState = create<ServerApiStateProps>((set, get) => ({
|
export const useServerApiState = create<ServerApiStateProps>()(
|
||||||
server: emptyServerAPI,
|
persist(
|
||||||
setServer: (newServer: ServerAPIProps) => set({ server: newServer }),
|
(set) => ({
|
||||||
fetchServerApiState: async () => {
|
server: emptyServerAPI,
|
||||||
// Fetch server data
|
setServer: (newServer: ServerAPIProps) => set({ server: newServer }),
|
||||||
await api
|
fetchServerApiState: async () => {
|
||||||
.get(apiUrl(ApiPaths.api_server_info))
|
// Fetch server data
|
||||||
.then((response) => {
|
await api
|
||||||
set({ server: response.data });
|
.get(apiUrl(ApiPaths.api_server_info))
|
||||||
})
|
.then((response) => {
|
||||||
.catch(() => {});
|
set({ server: response.data });
|
||||||
}
|
})
|
||||||
}));
|
.catch(() => {});
|
||||||
|
// Fetch status data for rendering labels
|
||||||
|
await api.get(apiUrl(ApiPaths.global_status)).then((response) => {
|
||||||
|
const newStatusLookup: StatusLookup = {} as StatusLookup;
|
||||||
|
for (const key in response.data) {
|
||||||
|
newStatusLookup[statusCodeList[key]] = response.data[key].values;
|
||||||
|
}
|
||||||
|
set({ status: newStatusLookup });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
status: undefined
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: 'server-api-state',
|
||||||
|
getStorage: () => sessionStorage
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
export enum ApiPaths {
|
export enum ApiPaths {
|
||||||
api_server_info = 'api-server-info',
|
api_server_info = 'api-server-info',
|
||||||
@ -43,6 +67,7 @@ export enum ApiPaths {
|
|||||||
|
|
||||||
barcode = 'api-barcode',
|
barcode = 'api-barcode',
|
||||||
news = 'news',
|
news = 'news',
|
||||||
|
global_status = 'api-global-status',
|
||||||
|
|
||||||
// Build order URLs
|
// Build order URLs
|
||||||
build_order_list = 'api-build-list',
|
build_order_list = 'api-build-list',
|
||||||
@ -123,6 +148,8 @@ export function apiEndpoint(path: ApiPaths): string {
|
|||||||
return 'barcode/';
|
return 'barcode/';
|
||||||
case ApiPaths.news:
|
case ApiPaths.news:
|
||||||
return 'news/';
|
return 'news/';
|
||||||
|
case ApiPaths.global_status:
|
||||||
|
return 'generic/status/';
|
||||||
case ApiPaths.build_order_list:
|
case ApiPaths.build_order_list:
|
||||||
return 'build/';
|
return 'build/';
|
||||||
case ApiPaths.build_order_attachment_list:
|
case ApiPaths.build_order_attachment_list:
|
||||||
|
Loading…
Reference in New Issue
Block a user