mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Model render refactor (#5894)
* Embiggen search drawer * Refactor search drawer queries: - Use proper permission checks * Actually check user settings * Move StatusRenderer * Update renderers - Improve in-line render for different order types * Update stockitem renderere * Remove old renderer functions * Better data handling in UserState * Tweaks for settings pages * "Fix" scanning page - Rendering is a bit broken currently, as the barcode scan does not send back the model data * "Fix" scanning page - Rendering is a bit broken currently, as the barcode scan does not send back the model data - Required refactoring enumerations out into separate files - Some strange race condition / import loop was happening * Fix incorrect imports * Fixing hover card - Use unique key * fixes * Fix urls.md * More udpates * Fix unused import
This commit is contained in:
parent
9e2da947a9
commit
e6db817c8f
@ -18,6 +18,7 @@ class MyUrlsPlugin(UrlsMixin, InvenTreePlugin):
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
The URLs get exposed under `/plugin/{plugin.slug}/*` and get exposed to the template engine with the prefix `plugin:{plugin.slug}:` (for usage with the [url tag](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#url)).
|
The URLs get exposed under `/plugin/{plugin.slug}/*` and get exposed to the template engine with the prefix `plugin:{plugin.slug}:` (for usage with the [url tag](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#url)).
|
||||||
|
|
||||||
!!! info "Note"
|
!!! info "Note"
|
||||||
|
@ -3,7 +3,8 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { api } from '../App';
|
import { api } from '../App';
|
||||||
import { ApiPaths, apiUrl } from '../states/ApiState';
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../states/ApiState';
|
||||||
import { StatisticItem } from './items/DashboardItem';
|
import { StatisticItem } from './items/DashboardItem';
|
||||||
import { ErrorItem } from './items/ErrorItem';
|
import { ErrorItem } from './items/ErrorItem';
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import { ReactNode } from 'react';
|
|||||||
import { notYetImplemented } from '../../functions/notifications';
|
import { notYetImplemented } from '../../functions/notifications';
|
||||||
|
|
||||||
export type ActionButtonProps = {
|
export type ActionButtonProps = {
|
||||||
|
key?: string;
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
text?: string;
|
text?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
@ -22,12 +23,13 @@ export function ActionButton(props: ActionButtonProps) {
|
|||||||
return (
|
return (
|
||||||
!props.hidden && (
|
!props.hidden && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
key={props.text ?? props.tooltip}
|
key={`tooltip-${props.key}`}
|
||||||
disabled={!props.tooltip && !props.text}
|
disabled={!props.tooltip && !props.text}
|
||||||
label={props.tooltip ?? props.text}
|
label={props.tooltip ?? props.text}
|
||||||
position="left"
|
position="left"
|
||||||
>
|
>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
|
key={`action-icon-${props.key}`}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
radius="xs"
|
radius="xs"
|
||||||
color={props.color}
|
color={props.color}
|
||||||
|
@ -9,9 +9,9 @@ import { useEffect, useMemo } from 'react';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { api, queryClient } from '../../App';
|
import { api, queryClient } from '../../App';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { constructFormUrl } from '../../functions/forms';
|
import { constructFormUrl } from '../../functions/forms';
|
||||||
import { invalidResponse } from '../../functions/notifications';
|
import { invalidResponse } from '../../functions/notifications';
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import { ApiFormField, ApiFormFieldSet } from './fields/ApiFormField';
|
import { ApiFormField, ApiFormFieldSet } from './fields/ApiFormField';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,7 +14,7 @@ import { IconX } from '@tabler/icons-react';
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { ModelType } from '../../render/ModelType';
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { ApiFormProps } from '../ApiForm';
|
import { ApiFormProps } from '../ApiForm';
|
||||||
import { ChoiceField } from './ChoiceField';
|
import { ChoiceField } from './ChoiceField';
|
||||||
import { RelatedModelField } from './RelatedModelField';
|
import { RelatedModelField } from './RelatedModelField';
|
||||||
|
@ -81,7 +81,7 @@ export function BarcodeActionDropdown({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
key="barcode"
|
key="barcode-actions"
|
||||||
tooltip={t`Barcode Actions`}
|
tooltip={t`Barcode Actions`}
|
||||||
icon={<IconQrcode />}
|
icon={<IconQrcode />}
|
||||||
actions={actions}
|
actions={actions}
|
||||||
|
@ -4,7 +4,8 @@ import { ContextModalProps } from '@mantine/modals';
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { ApiPaths, apiUrl, useServerApiState } from '../../states/ApiState';
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { apiUrl, useServerApiState } from '../../states/ApiState';
|
||||||
import { useLocalState } from '../../states/LocalState';
|
import { useLocalState } from '../../states/LocalState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
import { CopyButton } from '../items/CopyButton';
|
import { CopyButton } from '../items/CopyButton';
|
||||||
|
@ -23,7 +23,8 @@ import { Html5QrcodeResult } from 'html5-qrcode/core';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
|
||||||
export function QrCodeModal({
|
export function QrCodeModal({
|
||||||
context,
|
context,
|
||||||
|
@ -7,8 +7,9 @@ import { useNavigate, useParams } from 'react-router-dom';
|
|||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { navTabs as mainNavTabs } from '../../defaults/links';
|
import { navTabs as mainNavTabs } from '../../defaults/links';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { InvenTreeStyle } from '../../globalStyle';
|
import { InvenTreeStyle } from '../../globalStyle';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { ScanButton } from '../items/ScanButton';
|
import { ScanButton } from '../items/ScanButton';
|
||||||
import { MainMenu } from './MainMenu';
|
import { MainMenu } from './MainMenu';
|
||||||
import { NavHoverMenu } from './NavHoverMenu';
|
import { NavHoverMenu } from './NavHoverMenu';
|
||||||
|
@ -14,7 +14,8 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { StylishText } from '../items/StylishText';
|
import { StylishText } from '../items/StylishText';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,7 +58,7 @@ export function PageDetail({
|
|||||||
{detail}
|
{detail}
|
||||||
<Space />
|
<Space />
|
||||||
{actions && (
|
{actions && (
|
||||||
<Group spacing={5} position="right">
|
<Group key="page-actions" spacing={5} position="right">
|
||||||
{actions}
|
{actions}
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
|
@ -6,7 +6,8 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { StylishText } from '../items/StylishText';
|
import { StylishText } from '../items/StylishText';
|
||||||
|
|
||||||
export function PartCategoryTree({
|
export function PartCategoryTree({
|
||||||
|
@ -26,139 +26,27 @@ import {
|
|||||||
IconX
|
IconX
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
|
import { UserRoles } from '../../enums/Roles';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
import { useUserSettingsState } from '../../states/SettingsState';
|
||||||
|
import { useUserState } from '../../states/UserState';
|
||||||
import { RenderInstance } from '../render/Instance';
|
import { RenderInstance } from '../render/Instance';
|
||||||
import { ModelInformationDict, ModelType } from '../render/ModelType';
|
import { ModelInformationDict } from '../render/ModelType';
|
||||||
|
|
||||||
// Define type for handling individual search queries
|
// Define type for handling individual search queries
|
||||||
type SearchQuery = {
|
type SearchQuery = {
|
||||||
name: ModelType;
|
model: ModelType;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
parameters: any;
|
parameters: any;
|
||||||
results?: any;
|
results?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Placeholder function for permissions checks (will be replaced with a proper implementation)
|
|
||||||
function permissionCheck(permission: string) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Placeholder function for settings checks (will be replaced with a proper implementation)
|
|
||||||
function settingsCheck(setting: string) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Build a list of search queries based on user permissions
|
|
||||||
*/
|
|
||||||
function buildSearchQueries(): SearchQuery[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: ModelType.part,
|
|
||||||
parameters: {},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('part.view') &&
|
|
||||||
settingsCheck('SEARCH_PREVIEW_SHOW_PARTS')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.supplierpart,
|
|
||||||
parameters: {
|
|
||||||
part_detail: true,
|
|
||||||
supplier_detail: true,
|
|
||||||
manufacturer_detail: true
|
|
||||||
},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('part.view') &&
|
|
||||||
permissionCheck('purchase_order.view') &&
|
|
||||||
settingsCheck('SEARCH_PREVIEW_SHOW_SUPPLIER_PARTS')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.manufacturerpart,
|
|
||||||
parameters: {
|
|
||||||
part_detail: true,
|
|
||||||
supplier_detail: true,
|
|
||||||
manufacturer_detail: true
|
|
||||||
},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('part.view') &&
|
|
||||||
permissionCheck('purchase_order.view') &&
|
|
||||||
settingsCheck('SEARCH_PREVIEW_SHOW_MANUFACTURER_PARTS')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.partcategory,
|
|
||||||
parameters: {},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('part_category.view') &&
|
|
||||||
settingsCheck('SEARCH_PREVIEW_SHOW_CATEGORIES')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.stockitem,
|
|
||||||
parameters: {
|
|
||||||
part_detail: true,
|
|
||||||
location_detail: true
|
|
||||||
},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('stock.view') &&
|
|
||||||
settingsCheck('SEARCH_PREVIEW_SHOW_STOCK')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.stocklocation,
|
|
||||||
parameters: {},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('stock_location.view') &&
|
|
||||||
settingsCheck('SEARCH_PREVIEW_SHOW_LOCATIONS')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.build,
|
|
||||||
parameters: {
|
|
||||||
part_detail: true
|
|
||||||
},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('build.view') &&
|
|
||||||
settingsCheck('SEARCH_PREVIEW_SHOW_BUILD_ORDERS')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.company,
|
|
||||||
parameters: {},
|
|
||||||
enabled:
|
|
||||||
(permissionCheck('sales_order.view') ||
|
|
||||||
permissionCheck('purchase_order.view')) &&
|
|
||||||
settingsCheck('SEARCH_PREVIEW_SHOW_COMPANIES')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.purchaseorder,
|
|
||||||
parameters: {
|
|
||||||
supplier_detail: true
|
|
||||||
},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('purchase_order.view') &&
|
|
||||||
settingsCheck(`SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS`)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.salesorder,
|
|
||||||
parameters: {
|
|
||||||
customer_detail: true
|
|
||||||
},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('sales_order.view') &&
|
|
||||||
settingsCheck(`SEARCH_PREVIEW_SHOW_SALES_ORDERS`)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ModelType.returnorder,
|
|
||||||
parameters: {
|
|
||||||
customer_detail: true
|
|
||||||
},
|
|
||||||
enabled:
|
|
||||||
permissionCheck('return_order.view') &&
|
|
||||||
settingsCheck(`SEARCH_PREVIEW_SHOW_RETURN_ORDERS`)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Render the results for a single search query
|
* Render the results for a single search query
|
||||||
*/
|
*/
|
||||||
@ -175,10 +63,11 @@ function QueryResultGroup({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const model = ModelInformationDict[query.name];
|
const model = ModelInformationDict[query.model];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper shadow="sm" radius="xs" p="md">
|
<Paper shadow="sm" radius="xs" p="md" key={`paper-${query.model}`}>
|
||||||
<Stack key={query.name}>
|
<Stack key={`stack-${query.model}`}>
|
||||||
<Group position="apart" noWrap={true}>
|
<Group position="apart" noWrap={true}>
|
||||||
<Group position="left" spacing={5} noWrap={true}>
|
<Group position="left" spacing={5} noWrap={true}>
|
||||||
<Text size="lg">{model.label_multiple}</Text>
|
<Text size="lg">{model.label_multiple}</Text>
|
||||||
@ -193,7 +82,7 @@ function QueryResultGroup({
|
|||||||
color="red"
|
color="red"
|
||||||
variant="transparent"
|
variant="transparent"
|
||||||
radius="xs"
|
radius="xs"
|
||||||
onClick={() => onRemove(query.name)}
|
onClick={() => onRemove(query.model)}
|
||||||
>
|
>
|
||||||
<IconX />
|
<IconX />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
@ -201,11 +90,11 @@ function QueryResultGroup({
|
|||||||
<Divider />
|
<Divider />
|
||||||
<Stack>
|
<Stack>
|
||||||
{query.results.results.map((result: any) => (
|
{query.results.results.map((result: any) => (
|
||||||
<Anchor onClick={() => onResultClick(query.name, result.pk)}>
|
<Anchor onClick={() => onResultClick(query.model, result.pk)}>
|
||||||
<RenderInstance
|
<RenderInstance
|
||||||
key={`${query.name}-${result.pk}`}
|
key={`${query.model}-${result.pk}`}
|
||||||
instance={result}
|
instance={result}
|
||||||
model={query.name}
|
model={query.model}
|
||||||
/>
|
/>
|
||||||
</Anchor>
|
</Anchor>
|
||||||
))}
|
))}
|
||||||
@ -233,10 +122,116 @@ export function SearchDrawer({
|
|||||||
const [searchRegex, setSearchRegex] = useState<boolean>(false);
|
const [searchRegex, setSearchRegex] = useState<boolean>(false);
|
||||||
const [searchWhole, setSearchWhole] = useState<boolean>(false);
|
const [searchWhole, setSearchWhole] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const user = useUserState();
|
||||||
|
const userSettings = useUserSettingsState();
|
||||||
|
|
||||||
|
// Build out search queries based on user permissions and preferences
|
||||||
|
const searchQueryList: SearchQuery[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
model: ModelType.part,
|
||||||
|
parameters: {},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.part) &&
|
||||||
|
userSettings.isSet('SEARCH_PREVIEW_SHOW_PARTS')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.supplierpart,
|
||||||
|
parameters: {
|
||||||
|
part_detail: true,
|
||||||
|
supplier_detail: true,
|
||||||
|
manufacturer_detail: true
|
||||||
|
},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.part) &&
|
||||||
|
user.hasViewRole(UserRoles.purchase_order) &&
|
||||||
|
userSettings.isSet('SEARCH_PREVIEW_SHOW_SUPPLIER_PARTS')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.manufacturerpart,
|
||||||
|
parameters: {
|
||||||
|
part_detail: true,
|
||||||
|
supplier_detail: true,
|
||||||
|
manufacturer_detail: true
|
||||||
|
},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.part) &&
|
||||||
|
user.hasViewRole(UserRoles.purchase_order) &&
|
||||||
|
userSettings.isSet('SEARCH_PREVIEW_SHOW_MANUFACTURER_PARTS')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.partcategory,
|
||||||
|
parameters: {},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.part_category) &&
|
||||||
|
userSettings.isSet('SEARCH_PREVIEW_SHOW_CATEGORIES')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.stockitem,
|
||||||
|
parameters: {
|
||||||
|
part_detail: true,
|
||||||
|
location_detail: true
|
||||||
|
},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.stock) &&
|
||||||
|
userSettings.isSet('SEARCH_PREVIEW_SHOW_STOCK')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.stocklocation,
|
||||||
|
parameters: {},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.stock_location) &&
|
||||||
|
userSettings.isSet('SEARCH_PREVIEW_SHOW_LOCATIONS')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.build,
|
||||||
|
parameters: {
|
||||||
|
part_detail: true
|
||||||
|
},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.build) &&
|
||||||
|
userSettings.isSet('SEARCH_PREVIEW_SHOW_BUILD_ORDERS')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.company,
|
||||||
|
parameters: {},
|
||||||
|
enabled:
|
||||||
|
(user.hasViewRole(UserRoles.sales_order) ||
|
||||||
|
user.hasViewRole(UserRoles.purchase_order)) &&
|
||||||
|
userSettings.isSet('SEARCH_PREVIEW_SHOW_COMPANIES')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.purchaseorder,
|
||||||
|
parameters: {
|
||||||
|
supplier_detail: true
|
||||||
|
},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.purchase_order) &&
|
||||||
|
userSettings.isSet(`SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS`)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.salesorder,
|
||||||
|
parameters: {
|
||||||
|
customer_detail: true
|
||||||
|
},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.sales_order) &&
|
||||||
|
userSettings.isSet(`SEARCH_PREVIEW_SHOW_SALES_ORDERS`)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: ModelType.returnorder,
|
||||||
|
parameters: {
|
||||||
|
customer_detail: true
|
||||||
|
},
|
||||||
|
enabled:
|
||||||
|
user.hasViewRole(UserRoles.return_order) &&
|
||||||
|
userSettings.isSet(`SEARCH_PREVIEW_SHOW_RETURN_ORDERS`)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, [user, userSettings]);
|
||||||
|
|
||||||
// Construct a list of search queries based on user permissions
|
// Construct a list of search queries based on user permissions
|
||||||
const searchQueries: SearchQuery[] = buildSearchQueries().filter(
|
const searchQueries: SearchQuery[] = searchQueryList.filter((q) => q.enabled);
|
||||||
(q) => q.enabled
|
|
||||||
);
|
|
||||||
|
|
||||||
// Re-fetch data whenever the search term is updated
|
// Re-fetch data whenever the search term is updated
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -261,7 +256,7 @@ export function SearchDrawer({
|
|||||||
|
|
||||||
// Add in custom query parameters
|
// Add in custom query parameters
|
||||||
searchQueries.forEach((query) => {
|
searchQueries.forEach((query) => {
|
||||||
params[query.name] = query.parameters;
|
params[query.model] = query.parameters;
|
||||||
});
|
});
|
||||||
|
|
||||||
return api
|
return api
|
||||||
@ -289,11 +284,11 @@ export function SearchDrawer({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (searchQuery.data) {
|
if (searchQuery.data) {
|
||||||
let queries = searchQueries.filter(
|
let queries = searchQueries.filter(
|
||||||
(query) => query.name in searchQuery.data
|
(query) => query.model in searchQuery.data
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let key in searchQuery.data) {
|
for (let key in searchQuery.data) {
|
||||||
let query = queries.find((q) => q.name == key);
|
let query = queries.find((q) => q.model == key);
|
||||||
if (query) {
|
if (query) {
|
||||||
query.results = searchQuery.data[key];
|
query.results = searchQuery.data[key];
|
||||||
}
|
}
|
||||||
@ -310,7 +305,7 @@ export function SearchDrawer({
|
|||||||
|
|
||||||
// Callback to remove a set of results from the list
|
// Callback to remove a set of results from the list
|
||||||
function removeResults(query: ModelType) {
|
function removeResults(query: ModelType) {
|
||||||
setQueryResults(queryResults.filter((q) => q.name != query));
|
setQueryResults(queryResults.filter((q) => q.model != query));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback when the drawer is closed
|
// Callback when the drawer is closed
|
||||||
@ -332,7 +327,7 @@ export function SearchDrawer({
|
|||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
opened={opened}
|
opened={opened}
|
||||||
size="md"
|
size="xl"
|
||||||
onClose={closeDrawer}
|
onClose={closeDrawer}
|
||||||
position="right"
|
position="right"
|
||||||
withCloseButton={false}
|
withCloseButton={false}
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
import { Anchor, Group, Stack, Text, Title } from '@mantine/core';
|
import {
|
||||||
|
Anchor,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Space,
|
||||||
|
Stack,
|
||||||
|
Text
|
||||||
|
} from '@mantine/core';
|
||||||
import { IconSwitch } from '@tabler/icons-react';
|
import { IconSwitch } from '@tabler/icons-react';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { StylishText } from '../items/StylishText';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a settings page header with interlinks to one other settings page
|
* Construct a settings page header with interlinks to one other settings page
|
||||||
*/
|
*/
|
||||||
@ -14,28 +24,35 @@ export function SettingsHeader({
|
|||||||
switch_text,
|
switch_text,
|
||||||
switch_link
|
switch_link
|
||||||
}: {
|
}: {
|
||||||
title: string | ReactNode;
|
title: string;
|
||||||
shorthand?: string;
|
shorthand?: string;
|
||||||
subtitle: string | ReactNode;
|
subtitle?: string;
|
||||||
switch_condition?: boolean;
|
switch_condition?: boolean;
|
||||||
switch_text?: string | ReactNode;
|
switch_text?: string | ReactNode;
|
||||||
switch_link?: string;
|
switch_link?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Stack spacing="0" ml={'sm'}>
|
<Paper shadow="xs" radius="xs" p="xs">
|
||||||
<Group>
|
<Group position="apart">
|
||||||
<Title order={3}>{title}</Title>
|
<Stack spacing="xs">
|
||||||
{shorthand && <Text c="dimmed">({shorthand})</Text>}
|
<Group position="left" spacing="xs">
|
||||||
|
<StylishText size="xl">{title}</StylishText>
|
||||||
|
<Text size="sm">{shorthand}</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Group>
|
<Text italic>{subtitle}</Text>
|
||||||
<Text c="dimmed">{subtitle}</Text>
|
</Stack>
|
||||||
|
<Space />
|
||||||
{switch_text && switch_link && switch_condition && (
|
{switch_text && switch_link && switch_condition && (
|
||||||
<Anchor component={Link} to={switch_link}>
|
<Anchor component={Link} to={switch_link}>
|
||||||
<IconSwitch size={14} />
|
<Button variant="outline">
|
||||||
{switch_text}
|
<Group spacing="sm">
|
||||||
|
<IconSwitch size={18} />
|
||||||
|
<Text>{switch_text}</Text>
|
||||||
|
</Group>
|
||||||
|
</Button>
|
||||||
</Anchor>
|
</Anchor>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { StylishText } from '../items/StylishText';
|
import { StylishText } from '../items/StylishText';
|
||||||
|
|
||||||
export function StockLocationTree({
|
export function StockLocationTree({
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { RenderInlineModel } from './Instance';
|
import { RenderInlineModel } from './Instance';
|
||||||
|
import { StatusRenderer } from './StatusRenderer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline rendering of a single BuildOrder instance
|
* Inline rendering of a single BuildOrder instance
|
||||||
@ -10,6 +12,10 @@ export function RenderBuildOrder({ instance }: { instance: any }): ReactNode {
|
|||||||
<RenderInlineModel
|
<RenderInlineModel
|
||||||
primary={instance.reference}
|
primary={instance.reference}
|
||||||
secondary={instance.title}
|
secondary={instance.title}
|
||||||
|
suffix={StatusRenderer({
|
||||||
|
status: instance.status,
|
||||||
|
type: ModelType.build
|
||||||
|
})}
|
||||||
image={instance.part_detail?.thumbnail || instance.part_detail?.image}
|
image={instance.part_detail?.thumbnail || instance.part_detail?.image}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,7 @@ import { Alert, Space } from '@mantine/core';
|
|||||||
import { Group, Text } from '@mantine/core';
|
import { Group, Text } from '@mantine/core';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { Thumbnail } from '../images/Thumbnail';
|
import { Thumbnail } from '../images/Thumbnail';
|
||||||
import { RenderBuildOrder } from './Build';
|
import { RenderBuildOrder } from './Build';
|
||||||
import {
|
import {
|
||||||
@ -13,7 +14,6 @@ import {
|
|||||||
RenderSupplierPart
|
RenderSupplierPart
|
||||||
} from './Company';
|
} from './Company';
|
||||||
import { RenderProjectCode } from './Generic';
|
import { RenderProjectCode } from './Generic';
|
||||||
import { ModelType } from './ModelType';
|
|
||||||
import {
|
import {
|
||||||
RenderPurchaseOrder,
|
RenderPurchaseOrder,
|
||||||
RenderReturnOrder,
|
RenderReturnOrder,
|
||||||
@ -101,7 +101,7 @@ export function RenderInlineModel({
|
|||||||
}: {
|
}: {
|
||||||
primary: string;
|
primary: string;
|
||||||
secondary?: string;
|
secondary?: string;
|
||||||
suffix?: string;
|
suffix?: ReactNode;
|
||||||
image?: string;
|
image?: string;
|
||||||
labels?: string[];
|
labels?: string[];
|
||||||
url?: string;
|
url?: string;
|
||||||
|
@ -1,37 +1,18 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
export enum ModelType {
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
part = 'part',
|
import { ModelType } from '../../enums/ModelType';
|
||||||
supplierpart = 'supplierpart',
|
|
||||||
manufacturerpart = 'manufacturerpart',
|
|
||||||
partcategory = 'partcategory',
|
|
||||||
partparametertemplate = 'partparametertemplate',
|
|
||||||
projectcode = 'projectcode',
|
|
||||||
stockitem = 'stockitem',
|
|
||||||
stocklocation = 'stocklocation',
|
|
||||||
stockhistory = 'stockhistory',
|
|
||||||
build = 'build',
|
|
||||||
company = 'company',
|
|
||||||
purchaseorder = 'purchaseorder',
|
|
||||||
purchaseorderline = 'purchaseorderline',
|
|
||||||
salesorder = 'salesorder',
|
|
||||||
salesordershipment = 'salesordershipment',
|
|
||||||
returnorder = 'returnorder',
|
|
||||||
address = 'address',
|
|
||||||
contact = 'contact',
|
|
||||||
owner = 'owner',
|
|
||||||
user = 'user'
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ModelInformatonInterface {
|
interface ModelInformationInterface {
|
||||||
label: string;
|
label: string;
|
||||||
label_multiple: string;
|
label_multiple: string;
|
||||||
url_overview?: string;
|
url_overview?: string;
|
||||||
url_detail?: string;
|
url_detail?: string;
|
||||||
|
api_endpoint?: ApiPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ModelDictory = {
|
type ModelDictory = {
|
||||||
[key in keyof typeof ModelType]: ModelInformatonInterface;
|
[key in keyof typeof ModelType]: ModelInformationInterface;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ModelInformationDict: ModelDictory = {
|
export const ModelInformationDict: ModelDictory = {
|
||||||
@ -39,116 +20,136 @@ export const ModelInformationDict: ModelDictory = {
|
|||||||
label: t`Part`,
|
label: t`Part`,
|
||||||
label_multiple: t`Parts`,
|
label_multiple: t`Parts`,
|
||||||
url_overview: '/part',
|
url_overview: '/part',
|
||||||
url_detail: '/part/:pk/'
|
url_detail: '/part/:pk/',
|
||||||
|
api_endpoint: ApiPaths.part_list
|
||||||
},
|
},
|
||||||
partparametertemplate: {
|
partparametertemplate: {
|
||||||
label: t`Part Parameter Template`,
|
label: t`Part Parameter Template`,
|
||||||
label_multiple: t`Part Parameter Templates`,
|
label_multiple: t`Part Parameter Templates`,
|
||||||
url_overview: '/partparametertemplate',
|
url_overview: '/partparametertemplate',
|
||||||
url_detail: '/partparametertemplate/:pk/'
|
url_detail: '/partparametertemplate/:pk/',
|
||||||
|
api_endpoint: ApiPaths.part_parameter_template_list
|
||||||
},
|
},
|
||||||
supplierpart: {
|
supplierpart: {
|
||||||
label: t`Supplier Part`,
|
label: t`Supplier Part`,
|
||||||
label_multiple: t`Supplier Parts`,
|
label_multiple: t`Supplier Parts`,
|
||||||
url_overview: '/supplierpart',
|
url_overview: '/supplierpart',
|
||||||
url_detail: '/supplierpart/:pk/'
|
url_detail: '/supplierpart/:pk/',
|
||||||
|
api_endpoint: ApiPaths.supplier_part_list
|
||||||
},
|
},
|
||||||
manufacturerpart: {
|
manufacturerpart: {
|
||||||
label: t`Manufacturer Part`,
|
label: t`Manufacturer Part`,
|
||||||
label_multiple: t`Manufacturer Parts`,
|
label_multiple: t`Manufacturer Parts`,
|
||||||
url_overview: '/manufacturerpart',
|
url_overview: '/manufacturerpart',
|
||||||
url_detail: '/manufacturerpart/:pk/'
|
url_detail: '/manufacturerpart/:pk/',
|
||||||
|
api_endpoint: ApiPaths.manufacturer_part_list
|
||||||
},
|
},
|
||||||
partcategory: {
|
partcategory: {
|
||||||
label: t`Part Category`,
|
label: t`Part Category`,
|
||||||
label_multiple: t`Part Categories`,
|
label_multiple: t`Part Categories`,
|
||||||
url_overview: '/partcategory',
|
url_overview: '/partcategory',
|
||||||
url_detail: '/partcategory/:pk/'
|
url_detail: '/partcategory/:pk/',
|
||||||
|
api_endpoint: ApiPaths.category_list
|
||||||
},
|
},
|
||||||
stockitem: {
|
stockitem: {
|
||||||
label: t`Stock Item`,
|
label: t`Stock Item`,
|
||||||
label_multiple: t`Stock Items`,
|
label_multiple: t`Stock Items`,
|
||||||
url_overview: '/stockitem',
|
url_overview: '/stockitem',
|
||||||
url_detail: '/stockitem/:pk/'
|
url_detail: '/stockitem/:pk/',
|
||||||
|
api_endpoint: ApiPaths.stock_item_list
|
||||||
},
|
},
|
||||||
stocklocation: {
|
stocklocation: {
|
||||||
label: t`Stock Location`,
|
label: t`Stock Location`,
|
||||||
label_multiple: t`Stock Locations`,
|
label_multiple: t`Stock Locations`,
|
||||||
url_overview: '/stocklocation',
|
url_overview: '/stocklocation',
|
||||||
url_detail: '/stocklocation/:pk/'
|
url_detail: '/stocklocation/:pk/',
|
||||||
|
api_endpoint: ApiPaths.stock_location_list
|
||||||
},
|
},
|
||||||
stockhistory: {
|
stockhistory: {
|
||||||
label: t`Stock History`,
|
label: t`Stock History`,
|
||||||
label_multiple: t`Stock Histories`
|
label_multiple: t`Stock Histories`,
|
||||||
|
api_endpoint: ApiPaths.stock_tracking_list
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
label: t`Build`,
|
label: t`Build`,
|
||||||
label_multiple: t`Builds`,
|
label_multiple: t`Builds`,
|
||||||
url_overview: '/build',
|
url_overview: '/build',
|
||||||
url_detail: '/build/:pk/'
|
url_detail: '/build/:pk/',
|
||||||
|
api_endpoint: ApiPaths.build_order_list
|
||||||
},
|
},
|
||||||
company: {
|
company: {
|
||||||
label: t`Company`,
|
label: t`Company`,
|
||||||
label_multiple: t`Companies`,
|
label_multiple: t`Companies`,
|
||||||
url_overview: '/company',
|
url_overview: '/company',
|
||||||
url_detail: '/company/:pk/'
|
url_detail: '/company/:pk/',
|
||||||
|
api_endpoint: ApiPaths.company_list
|
||||||
},
|
},
|
||||||
projectcode: {
|
projectcode: {
|
||||||
label: t`Project Code`,
|
label: t`Project Code`,
|
||||||
label_multiple: t`Project Codes`,
|
label_multiple: t`Project Codes`,
|
||||||
url_overview: '/project-code',
|
url_overview: '/project-code',
|
||||||
url_detail: '/project-code/:pk/'
|
url_detail: '/project-code/:pk/',
|
||||||
|
api_endpoint: ApiPaths.project_code_list
|
||||||
},
|
},
|
||||||
purchaseorder: {
|
purchaseorder: {
|
||||||
label: t`Purchase Order`,
|
label: t`Purchase Order`,
|
||||||
label_multiple: t`Purchase Orders`,
|
label_multiple: t`Purchase Orders`,
|
||||||
url_overview: '/purchaseorder',
|
url_overview: '/purchaseorder',
|
||||||
url_detail: '/purchaseorder/:pk/'
|
url_detail: '/purchaseorder/:pk/',
|
||||||
|
api_endpoint: ApiPaths.purchase_order_list
|
||||||
},
|
},
|
||||||
purchaseorderline: {
|
purchaseorderline: {
|
||||||
label: t`Purchase Order Line`,
|
label: t`Purchase Order Line`,
|
||||||
label_multiple: t`Purchase Order Lines`
|
label_multiple: t`Purchase Order Lines`,
|
||||||
|
api_endpoint: ApiPaths.purchase_order_line_list
|
||||||
},
|
},
|
||||||
salesorder: {
|
salesorder: {
|
||||||
label: t`Sales Order`,
|
label: t`Sales Order`,
|
||||||
label_multiple: t`Sales Orders`,
|
label_multiple: t`Sales Orders`,
|
||||||
url_overview: '/salesorder',
|
url_overview: '/salesorder',
|
||||||
url_detail: '/salesorder/:pk/'
|
url_detail: '/salesorder/:pk/',
|
||||||
|
api_endpoint: ApiPaths.sales_order_list
|
||||||
},
|
},
|
||||||
salesordershipment: {
|
salesordershipment: {
|
||||||
label: t`Sales Order Shipment`,
|
label: t`Sales Order Shipment`,
|
||||||
label_multiple: t`Sales Order Shipments`,
|
label_multiple: t`Sales Order Shipments`,
|
||||||
url_overview: '/salesordershipment',
|
url_overview: '/salesordershipment',
|
||||||
url_detail: '/salesordershipment/:pk/'
|
url_detail: '/salesordershipment/:pk/',
|
||||||
|
api_endpoint: ApiPaths.sales_order_shipment_list
|
||||||
},
|
},
|
||||||
returnorder: {
|
returnorder: {
|
||||||
label: t`Return Order`,
|
label: t`Return Order`,
|
||||||
label_multiple: t`Return Orders`,
|
label_multiple: t`Return Orders`,
|
||||||
url_overview: '/returnorder',
|
url_overview: '/returnorder',
|
||||||
url_detail: '/returnorder/:pk/'
|
url_detail: '/returnorder/:pk/',
|
||||||
|
api_endpoint: ApiPaths.return_order_list
|
||||||
},
|
},
|
||||||
address: {
|
address: {
|
||||||
label: t`Address`,
|
label: t`Address`,
|
||||||
label_multiple: t`Addresses`,
|
label_multiple: t`Addresses`,
|
||||||
url_overview: '/address',
|
url_overview: '/address',
|
||||||
url_detail: '/address/:pk/'
|
url_detail: '/address/:pk/',
|
||||||
|
api_endpoint: ApiPaths.address_list
|
||||||
},
|
},
|
||||||
contact: {
|
contact: {
|
||||||
label: t`Contact`,
|
label: t`Contact`,
|
||||||
label_multiple: t`Contacts`,
|
label_multiple: t`Contacts`,
|
||||||
url_overview: '/contact',
|
url_overview: '/contact',
|
||||||
url_detail: '/contact/:pk/'
|
url_detail: '/contact/:pk/',
|
||||||
|
api_endpoint: ApiPaths.contact_list
|
||||||
},
|
},
|
||||||
owner: {
|
owner: {
|
||||||
label: t`Owner`,
|
label: t`Owner`,
|
||||||
label_multiple: t`Owners`,
|
label_multiple: t`Owners`,
|
||||||
url_overview: '/owner',
|
url_overview: '/owner',
|
||||||
url_detail: '/owner/:pk/'
|
url_detail: '/owner/:pk/',
|
||||||
|
api_endpoint: ApiPaths.owner_list
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
label: t`User`,
|
label: t`User`,
|
||||||
label_multiple: t`Users`,
|
label_multiple: t`Users`,
|
||||||
url_overview: '/user',
|
url_overview: '/user',
|
||||||
url_detail: '/user/:pk/'
|
url_detail: '/user/:pk/',
|
||||||
|
api_endpoint: ApiPaths.user_list
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { RenderInlineModel } from './Instance';
|
import { RenderInlineModel } from './Instance';
|
||||||
|
import { StatusRenderer } from './StatusRenderer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline rendering of a single PurchaseOrder instance
|
* Inline rendering of a single PurchaseOrder instance
|
||||||
@ -18,6 +20,10 @@ export function RenderPurchaseOrder({
|
|||||||
<RenderInlineModel
|
<RenderInlineModel
|
||||||
primary={instance.reference}
|
primary={instance.reference}
|
||||||
secondary={instance.description}
|
secondary={instance.description}
|
||||||
|
suffix={StatusRenderer({
|
||||||
|
status: instance.status,
|
||||||
|
type: ModelType.purchaseorder
|
||||||
|
})}
|
||||||
image={supplier.thumnbnail || supplier.image}
|
image={supplier.thumnbnail || supplier.image}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -33,6 +39,10 @@ export function RenderReturnOrder({ instance }: { instance: any }): ReactNode {
|
|||||||
<RenderInlineModel
|
<RenderInlineModel
|
||||||
primary={instance.reference}
|
primary={instance.reference}
|
||||||
secondary={instance.description}
|
secondary={instance.description}
|
||||||
|
suffix={StatusRenderer({
|
||||||
|
status: instance.status,
|
||||||
|
type: ModelType.returnorder
|
||||||
|
})}
|
||||||
image={customer.thumnbnail || customer.image}
|
image={customer.thumnbnail || customer.image}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -50,6 +60,10 @@ export function RenderSalesOrder({ instance }: { instance: any }): ReactNode {
|
|||||||
<RenderInlineModel
|
<RenderInlineModel
|
||||||
primary={instance.reference}
|
primary={instance.reference}
|
||||||
secondary={instance.description}
|
secondary={instance.description}
|
||||||
|
suffix={StatusRenderer({
|
||||||
|
status: instance.status,
|
||||||
|
type: ModelType.salesorder
|
||||||
|
})}
|
||||||
image={customer.thumnbnail || customer.image}
|
image={customer.thumnbnail || customer.image}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Badge, Center, MantineSize } from '@mantine/core';
|
import { Badge, Center, MantineSize } from '@mantine/core';
|
||||||
|
|
||||||
import { colorMap } from '../../defaults/backendMappings';
|
import { colorMap } from '../../defaults/backendMappings';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { useServerApiState } from '../../states/ApiState';
|
import { useServerApiState } from '../../states/ApiState';
|
||||||
import { ModelType } from '../render/ModelType';
|
|
||||||
|
|
||||||
interface StatusCodeInterface {
|
interface StatusCodeInterface {
|
||||||
key: string;
|
key: string;
|
@ -1,3 +1,4 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
import { RenderInlineModel } from './Instance';
|
import { RenderInlineModel } from './Instance';
|
||||||
@ -19,10 +20,18 @@ export function RenderStockLocation({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function RenderStockItem({ instance }: { instance: any }): ReactNode {
|
export function RenderStockItem({ instance }: { instance: any }): ReactNode {
|
||||||
|
let quantity_string = '';
|
||||||
|
|
||||||
|
if (instance?.serial !== null && instance?.serial !== undefined) {
|
||||||
|
quantity_string += t`Serial Number` + `: ${instance.serial}`;
|
||||||
|
} else if (instance?.quantity) {
|
||||||
|
quantity_string = t`Quantity` + `: ${instance.quantity}`;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RenderInlineModel
|
<RenderInlineModel
|
||||||
primary={instance.part_detail?.full_name}
|
primary={instance.part_detail?.full_name}
|
||||||
secondary={instance.quantity}
|
suffix={quantity_string}
|
||||||
image={instance.part_detail?.thumbnail || instance.part_detail?.image}
|
image={instance.part_detail?.thumbnail || instance.part_detail?.image}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
import { Group } from '@mantine/core';
|
|
||||||
|
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import { GeneralRenderer } from './GeneralRenderer';
|
|
||||||
import { PartRenderer } from './PartRenderer';
|
|
||||||
|
|
||||||
export const BuildOrderRenderer = ({ pk }: { pk: string }) => {
|
|
||||||
const DetailRenderer = (data: any) => {
|
|
||||||
return (
|
|
||||||
<Group position="apart">
|
|
||||||
{data?.reference}
|
|
||||||
<small>
|
|
||||||
<PartRenderer
|
|
||||||
pk={data?.part_detail?.pk}
|
|
||||||
data={data?.part_detail}
|
|
||||||
link={true}
|
|
||||||
/>
|
|
||||||
</small>
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<GeneralRenderer
|
|
||||||
api_key={ApiPaths.build_order_list}
|
|
||||||
api_ref="build_order"
|
|
||||||
link={`/build/${pk}`}
|
|
||||||
pk={pk}
|
|
||||||
renderer={DetailRenderer}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,83 +0,0 @@
|
|||||||
import { Anchor, Loader } from '@mantine/core';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
|
|
||||||
import { api } from '../../App';
|
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
|
||||||
import { ThumbnailHoverCard } from '../images/Thumbnail';
|
|
||||||
|
|
||||||
export function GeneralRenderer({
|
|
||||||
api_key,
|
|
||||||
api_ref: ref,
|
|
||||||
link,
|
|
||||||
pk,
|
|
||||||
image = true,
|
|
||||||
data = undefined,
|
|
||||||
renderer
|
|
||||||
}: {
|
|
||||||
api_key: ApiPaths;
|
|
||||||
api_ref: string;
|
|
||||||
link: string;
|
|
||||||
pk: string;
|
|
||||||
image?: boolean;
|
|
||||||
data?: any;
|
|
||||||
renderer?: (data: any) => JSX.Element;
|
|
||||||
}) {
|
|
||||||
// check if data was passed - or fetch it
|
|
||||||
if (!data) {
|
|
||||||
const {
|
|
||||||
data: fetched_data,
|
|
||||||
isError,
|
|
||||||
isFetching,
|
|
||||||
isLoading
|
|
||||||
} = useQuery({
|
|
||||||
queryKey: [ref, pk],
|
|
||||||
queryFn: () => {
|
|
||||||
return api
|
|
||||||
.get(apiUrl(api_key, pk))
|
|
||||||
.then((res) => res.data)
|
|
||||||
.catch(() => {
|
|
||||||
{
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Loading section
|
|
||||||
if (isError) {
|
|
||||||
return <div>Something went wrong...</div>;
|
|
||||||
}
|
|
||||||
if (isFetching || isLoading) {
|
|
||||||
return <Loader />;
|
|
||||||
}
|
|
||||||
data = fetched_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Renderers
|
|
||||||
let content = undefined;
|
|
||||||
// Specific renderer was passed
|
|
||||||
if (renderer) content = renderer(data);
|
|
||||||
|
|
||||||
// No image and no content no default renderer
|
|
||||||
if (image === false && !content) content = data.name;
|
|
||||||
|
|
||||||
// Wrap in link if link was passed
|
|
||||||
if (content && link) {
|
|
||||||
content = (
|
|
||||||
<Anchor href={link} style={{ textDecoration: 'none' }}>
|
|
||||||
{content}
|
|
||||||
</Anchor>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return content if it exists, else default
|
|
||||||
if (content !== undefined) {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<ThumbnailHoverCard
|
|
||||||
src={data.thumbnail || data.image}
|
|
||||||
text={data.name}
|
|
||||||
link={link}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import { GeneralRenderer } from './GeneralRenderer';
|
|
||||||
|
|
||||||
export const PartRenderer = ({
|
|
||||||
pk,
|
|
||||||
data = undefined,
|
|
||||||
link = true
|
|
||||||
}: {
|
|
||||||
pk: string;
|
|
||||||
data?: any;
|
|
||||||
link?: boolean;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<GeneralRenderer
|
|
||||||
api_key={ApiPaths.part_list}
|
|
||||||
api_ref="part"
|
|
||||||
link={link ? `/part/${pk}` : ''}
|
|
||||||
pk={pk}
|
|
||||||
data={data}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
import { Group } from '@mantine/core';
|
|
||||||
|
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import { GeneralRenderer } from './GeneralRenderer';
|
|
||||||
|
|
||||||
export const PurchaseOrderRenderer = ({ pk }: { pk: string }) => {
|
|
||||||
const DetailRenderer = (data: any) => {
|
|
||||||
const code = data?.project_code_detail?.code;
|
|
||||||
return (
|
|
||||||
<Group position="apart">
|
|
||||||
<div>{data?.reference}</div>
|
|
||||||
{code && <div>({code})</div>}
|
|
||||||
{data?.supplier_reference && <div>{data?.supplier_reference}</div>}
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<GeneralRenderer
|
|
||||||
api_key={ApiPaths.purchase_order_list}
|
|
||||||
api_ref="purchaseorder"
|
|
||||||
link={`/order/purchase-order/${pk}`}
|
|
||||||
pk={pk}
|
|
||||||
renderer={DetailRenderer}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,16 +0,0 @@
|
|||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import { GeneralRenderer } from './GeneralRenderer';
|
|
||||||
|
|
||||||
export const SalesOrderRenderer = ({ pk }: { pk: string }) => {
|
|
||||||
return (
|
|
||||||
<GeneralRenderer
|
|
||||||
api_key={ApiPaths.sales_order_list}
|
|
||||||
api_ref="sales_order"
|
|
||||||
link={`/order/so/${pk}`}
|
|
||||||
pk={pk}
|
|
||||||
renderer={(data: any) => {
|
|
||||||
return data.reference;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,27 +0,0 @@
|
|||||||
import { Group } from '@mantine/core';
|
|
||||||
|
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import { GeneralRenderer } from './GeneralRenderer';
|
|
||||||
import { PartRenderer } from './PartRenderer';
|
|
||||||
|
|
||||||
export const StockItemRenderer = ({ pk }: { pk: string }) => {
|
|
||||||
const DetailRenderer = (data: any) => {
|
|
||||||
return (
|
|
||||||
<Group position="apart">
|
|
||||||
{data?.quantity}
|
|
||||||
<small>
|
|
||||||
<PartRenderer pk={data?.part_detail.pk} data={data?.part_detail} />
|
|
||||||
</small>
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<GeneralRenderer
|
|
||||||
api_key={ApiPaths.stock_item_list}
|
|
||||||
api_ref="stockitem"
|
|
||||||
link={`/stock/item/${pk}`}
|
|
||||||
pk={pk}
|
|
||||||
renderer={DetailRenderer}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,14 +0,0 @@
|
|||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import { GeneralRenderer } from './GeneralRenderer';
|
|
||||||
|
|
||||||
export const StockLocationRenderer = ({ pk }: { pk: string }) => {
|
|
||||||
return (
|
|
||||||
<GeneralRenderer
|
|
||||||
api_key={ApiPaths.stock_location_list}
|
|
||||||
api_ref="stock_location"
|
|
||||||
link={`/stock/location/${pk}`}
|
|
||||||
pk={pk}
|
|
||||||
image={false}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,33 +0,0 @@
|
|||||||
import { Group } from '@mantine/core';
|
|
||||||
|
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
import { GeneralRenderer } from './GeneralRenderer';
|
|
||||||
import { PartRenderer } from './PartRenderer';
|
|
||||||
|
|
||||||
export const SupplierPartRenderer = ({ pk }: { pk: string }) => {
|
|
||||||
const DetailRenderer = (data: any) => {
|
|
||||||
return (
|
|
||||||
<Group position="apart">
|
|
||||||
{data?.SKU}
|
|
||||||
<small>
|
|
||||||
<span style={{ color: 'white' }}>
|
|
||||||
<PartRenderer
|
|
||||||
pk={data?.part_detail?.pk}
|
|
||||||
data={data?.part_detail}
|
|
||||||
link={false}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</small>
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<GeneralRenderer
|
|
||||||
api_key={ApiPaths.supplier_part_list}
|
|
||||||
api_ref="supplier_part"
|
|
||||||
link={`/supplier-part/${pk}`}
|
|
||||||
pk={pk}
|
|
||||||
renderer={DetailRenderer}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,39 +0,0 @@
|
|||||||
import { BuildOrderRenderer } from './BuildOrderRenderer';
|
|
||||||
import { PartRenderer } from './PartRenderer';
|
|
||||||
import { PurchaseOrderRenderer } from './PurchaseOrderRenderer';
|
|
||||||
import { SalesOrderRenderer } from './SalesOrderRenderer';
|
|
||||||
import { StockItemRenderer } from './StockItemRenderer';
|
|
||||||
import { StockLocationRenderer } from './StockLocationRenderer';
|
|
||||||
import { SupplierPartRenderer } from './SupplierPartRenderer';
|
|
||||||
|
|
||||||
export enum RenderTypes {
|
|
||||||
part = 'part',
|
|
||||||
stock_item = 'stockitem',
|
|
||||||
stock_location = 'stocklocation',
|
|
||||||
supplier_part = 'supplierpart',
|
|
||||||
purchase_order = 'purchase_order',
|
|
||||||
sales_order = 'sales_order',
|
|
||||||
build_order = 'build_order'
|
|
||||||
}
|
|
||||||
|
|
||||||
// dict of renderers
|
|
||||||
const renderers = {
|
|
||||||
[RenderTypes.part]: PartRenderer,
|
|
||||||
[RenderTypes.stock_item]: StockItemRenderer,
|
|
||||||
[RenderTypes.stock_location]: StockLocationRenderer,
|
|
||||||
[RenderTypes.supplier_part]: SupplierPartRenderer,
|
|
||||||
[RenderTypes.purchase_order]: PurchaseOrderRenderer,
|
|
||||||
[RenderTypes.sales_order]: SalesOrderRenderer,
|
|
||||||
[RenderTypes.build_order]: BuildOrderRenderer
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface RenderProps {
|
|
||||||
type: RenderTypes;
|
|
||||||
pk: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Render(props: RenderProps) {
|
|
||||||
const { type, ...rest } = props;
|
|
||||||
const RendererComponent = renderers[type];
|
|
||||||
return <RendererComponent {...rest} />;
|
|
||||||
}
|
|
@ -4,11 +4,11 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
import { formatCurrency, renderDate } from '../../defaults/formatters';
|
import { formatCurrency, renderDate } from '../../defaults/formatters';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { ProgressBar } from '../items/ProgressBar';
|
import { ProgressBar } from '../items/ProgressBar';
|
||||||
import { YesNoButton } from '../items/YesNoButton';
|
import { YesNoButton } from '../items/YesNoButton';
|
||||||
import { ModelType } from '../render/ModelType';
|
import { TableStatusRenderer } from '../render/StatusRenderer';
|
||||||
import { RenderOwner } from '../render/User';
|
import { RenderOwner } from '../render/User';
|
||||||
import { TableStatusRenderer } from '../renderers/StatusRenderer';
|
|
||||||
import { TableColumn } from './Column';
|
import { TableColumn } from './Column';
|
||||||
import { ProjectCodeHoverCard } from './TableHoverCard';
|
import { ProjectCodeHoverCard } from './TableHoverCard';
|
||||||
|
|
||||||
|
@ -56,7 +56,11 @@ export function ProjectCodeHoverCard({ projectCode }: { projectCode: any }) {
|
|||||||
<TableHoverCard
|
<TableHoverCard
|
||||||
value={projectCode?.code}
|
value={projectCode?.code}
|
||||||
title={t`Project Code`}
|
title={t`Project Code`}
|
||||||
extra={projectCode?.description}
|
extra={
|
||||||
|
projectCode && (
|
||||||
|
<Text key="project-code">{projectCode?.description}</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
'-'
|
'-'
|
||||||
|
@ -8,11 +8,13 @@ import {
|
|||||||
import { ReactNode, useCallback, useMemo } from 'react';
|
import { ReactNode, useCallback, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { bomItemFields } from '../../../forms/BomForms';
|
import { bomItemFields } from '../../../forms/BomForms';
|
||||||
import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms';
|
import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { UserRoles, useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { YesNoButton } from '../../items/YesNoButton';
|
import { YesNoButton } from '../../items/YesNoButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
@ -64,7 +66,9 @@ export function BomTable({
|
|||||||
let extra = [];
|
let extra = [];
|
||||||
|
|
||||||
if (record.part != partId) {
|
if (record.part != partId) {
|
||||||
extra.push(t`This BOM item is defined for a different parent`);
|
extra.push(
|
||||||
|
<Text key="different-parent">{t`This BOM item is defined for a different parent`}</Text>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -2,8 +2,9 @@ import { t } from '@lingui/macro';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { ThumbnailHoverCard } from '../../images/Thumbnail';
|
import { ThumbnailHoverCard } from '../../images/Thumbnail';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
|
@ -3,11 +3,12 @@ import { useMemo } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { renderDate } from '../../../defaults/formatters';
|
import { renderDate } from '../../../defaults/formatters';
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { ThumbnailHoverCard } from '../../images/Thumbnail';
|
import { ThumbnailHoverCard } from '../../images/Thumbnail';
|
||||||
import { ProgressBar } from '../../items/ProgressBar';
|
import { ProgressBar } from '../../items/ProgressBar';
|
||||||
import { ModelType } from '../../render/ModelType';
|
|
||||||
import { RenderUser } from '../../render/User';
|
import { RenderUser } from '../../render/User';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import {
|
import {
|
||||||
|
@ -7,13 +7,14 @@ import { IconExternalLink, IconFileUpload } from '@tabler/icons-react';
|
|||||||
import { ReactNode, useEffect, useMemo, useState } from 'react';
|
import { ReactNode, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { api } from '../../../App';
|
import { api } from '../../../App';
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
import {
|
import {
|
||||||
addAttachment,
|
addAttachment,
|
||||||
deleteAttachment,
|
deleteAttachment,
|
||||||
editAttachment
|
editAttachment
|
||||||
} from '../../../forms/AttachmentForms';
|
} from '../../../forms/AttachmentForms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { AttachmentLink } from '../../items/AttachmentLink';
|
import { AttachmentLink } from '../../items/AttachmentLink';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
@ -3,8 +3,9 @@ import { Group, Text } from '@mantine/core';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { DescriptionColumn } from '../ColumnRenderers';
|
import { DescriptionColumn } from '../ColumnRenderers';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowAction } from '../RowActions';
|
import { RowAction } from '../RowActions';
|
||||||
|
@ -2,8 +2,9 @@ import { t } from '@lingui/macro';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { DescriptionColumn } from '../ColumnRenderers';
|
import { DescriptionColumn } from '../ColumnRenderers';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
@ -2,14 +2,16 @@ import { t } from '@lingui/macro';
|
|||||||
import { Group, Text } from '@mantine/core';
|
import { Group, Text } from '@mantine/core';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
openCreateApiForm,
|
||||||
openDeleteApiForm,
|
openDeleteApiForm,
|
||||||
openEditApiForm
|
openEditApiForm
|
||||||
} from '../../../functions/forms';
|
} from '../../../functions/forms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { UserRoles, useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { YesNoButton } from '../../items/YesNoButton';
|
import { YesNoButton } from '../../items/YesNoButton';
|
||||||
@ -178,7 +180,7 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
|||||||
|
|
||||||
// TODO: Hide if user does not have permission to edit parts
|
// TODO: Hide if user does not have permission to edit parts
|
||||||
actions.push(
|
actions.push(
|
||||||
<AddItemButton tooltip="Add parameter" onClick={addParameter} />
|
<AddItemButton tooltip={t`Add parameter`} onClick={addParameter} />
|
||||||
);
|
);
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
|
@ -2,6 +2,8 @@ import { t } from '@lingui/macro';
|
|||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { partParameterTemplateFields } from '../../../forms/PartForms';
|
import { partParameterTemplateFields } from '../../../forms/PartForms';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
openCreateApiForm,
|
||||||
@ -9,8 +11,8 @@ import {
|
|||||||
openEditApiForm
|
openEditApiForm
|
||||||
} from '../../../functions/forms';
|
} from '../../../functions/forms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { UserRoles, useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
@ -3,9 +3,10 @@ import { Group, Text } from '@mantine/core';
|
|||||||
import { ReactNode, useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
import { shortenString } from '../../../functions/tables';
|
import { shortenString } from '../../../functions/tables';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { DescriptionColumn, LinkColumn } from '../ColumnRenderers';
|
import { DescriptionColumn, LinkColumn } from '../ColumnRenderers';
|
||||||
@ -77,23 +78,29 @@ function partTableColumns(): TableColumn[] {
|
|||||||
|
|
||||||
if (min_stock > stock) {
|
if (min_stock > stock) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text color="orange">{t`Minimum stock` + `: ${min_stock}`}</Text>
|
<Text key="min-stock" color="orange">
|
||||||
|
{t`Minimum stock` + `: ${min_stock}`}
|
||||||
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
||||||
color = 'orange';
|
color = 'orange';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.ordering > 0) {
|
if (record.ordering > 0) {
|
||||||
extra.push(<Text>{t`On Order` + `: ${record.ordering}`}</Text>);
|
extra.push(
|
||||||
|
<Text key="on-order">{t`On Order` + `: ${record.ordering}`}</Text>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.building) {
|
if (record.building) {
|
||||||
extra.push(<Text>{t`Building` + `: ${record.building}`}</Text>);
|
extra.push(
|
||||||
|
<Text key="building">{t`Building` + `: ${record.building}`}</Text>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.allocated_to_build_orders > 0) {
|
if (record.allocated_to_build_orders > 0) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text>
|
<Text key="bo-allocations">
|
||||||
{t`Build Order Allocations` +
|
{t`Build Order Allocations` +
|
||||||
`: ${record.allocated_to_build_orders}`}
|
`: ${record.allocated_to_build_orders}`}
|
||||||
</Text>
|
</Text>
|
||||||
@ -102,7 +109,7 @@ function partTableColumns(): TableColumn[] {
|
|||||||
|
|
||||||
if (record.allocated_to_sales_orders > 0) {
|
if (record.allocated_to_sales_orders > 0) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text>
|
<Text key="so-allocations">
|
||||||
{t`Sales Order Allocations` +
|
{t`Sales Order Allocations` +
|
||||||
`: ${record.allocated_to_sales_orders}`}
|
`: ${record.allocated_to_sales_orders}`}
|
||||||
</Text>
|
</Text>
|
||||||
@ -110,7 +117,9 @@ function partTableColumns(): TableColumn[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (available != stock) {
|
if (available != stock) {
|
||||||
extra.push(<Text>{t`Available` + `: ${available}`}</Text>);
|
extra.push(
|
||||||
|
<Text key="available">{t`Available` + `: ${available}`}</Text>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add extra information on stock "demand"
|
// TODO: Add extra information on stock "demand"
|
||||||
|
@ -4,10 +4,12 @@ import { IconLayersLinked } from '@tabler/icons-react';
|
|||||||
import { ReactNode, useCallback, useMemo } from 'react';
|
import { ReactNode, useCallback, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { openCreateApiForm, openDeleteApiForm } from '../../../functions/forms';
|
import { openCreateApiForm, openDeleteApiForm } from '../../../functions/forms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { UserRoles, useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
@ -10,8 +10,9 @@ import {
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { api } from '../../../App';
|
import { api } from '../../../App';
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { StylishText } from '../../items/StylishText';
|
import { StylishText } from '../../items/StylishText';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable, InvenTreeTableProps } from '../InvenTreeTable';
|
import { InvenTreeTable, InvenTreeTableProps } from '../InvenTreeTable';
|
||||||
|
@ -4,11 +4,13 @@ import { IconSquareArrowRight } from '@tabler/icons-react';
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { ProgressBar } from '../../../components/items/ProgressBar';
|
import { ProgressBar } from '../../../components/items/ProgressBar';
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { purchaseOrderLineItemFields } from '../../../forms/PurchaseOrderForms';
|
import { purchaseOrderLineItemFields } from '../../../forms/PurchaseOrderForms';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
|
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { UserRoles, useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { ActionButton } from '../../buttons/ActionButton';
|
import { ActionButton } from '../../buttons/ActionButton';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
@ -64,7 +66,8 @@ export function PurchaseOrderLineItemTable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let fields = purchaseOrderLineItemFields({
|
let fields = purchaseOrderLineItemFields({
|
||||||
supplierId: supplier
|
supplierId: supplier,
|
||||||
|
create: false
|
||||||
});
|
});
|
||||||
|
|
||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
@ -219,21 +222,29 @@ export function PurchaseOrderLineItemTable({
|
|||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
url: ApiPaths.purchase_order_line_list,
|
url: ApiPaths.purchase_order_line_list,
|
||||||
title: t`Add Line Item`,
|
title: t`Add Line Item`,
|
||||||
fields: purchaseOrderLineItemFields({}),
|
fields: purchaseOrderLineItemFields({
|
||||||
|
create: true,
|
||||||
|
orderId: orderId
|
||||||
|
}),
|
||||||
onFormSuccess: refreshTable,
|
onFormSuccess: refreshTable,
|
||||||
successMessage: t`Line item added`
|
successMessage: t`Line item added`
|
||||||
});
|
});
|
||||||
}, []);
|
}, [orderId]);
|
||||||
|
|
||||||
// Custom table actions
|
// Custom table actions
|
||||||
const tableActions = useMemo(() => {
|
const tableActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
<AddItemButton
|
<AddItemButton
|
||||||
|
key="add-line-item"
|
||||||
tooltip={t`Add line item`}
|
tooltip={t`Add line item`}
|
||||||
onClick={addLine}
|
onClick={addLine}
|
||||||
hidden={!user?.hasAddRole(UserRoles.purchase_order)}
|
hidden={!user?.hasAddRole(UserRoles.purchase_order)}
|
||||||
/>,
|
/>,
|
||||||
<ActionButton text={t`Receive items`} icon={<IconSquareArrowRight />} />
|
<ActionButton
|
||||||
|
key="receive-items"
|
||||||
|
text={t`Receive items`}
|
||||||
|
icon={<IconSquareArrowRight />}
|
||||||
|
/>
|
||||||
];
|
];
|
||||||
}, [orderId, user]);
|
}, [orderId, user]);
|
||||||
|
|
||||||
|
@ -2,10 +2,11 @@ import { t } from '@lingui/macro';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { ModelType } from '../../render/ModelType';
|
|
||||||
import {
|
import {
|
||||||
CreationDateColumn,
|
CreationDateColumn,
|
||||||
DescriptionColumn,
|
DescriptionColumn,
|
||||||
|
@ -2,6 +2,8 @@ import { t } from '@lingui/macro';
|
|||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import { ReactNode, useCallback, useMemo } from 'react';
|
import { ReactNode, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import { supplierPartFields } from '../../../forms/CompanyForms';
|
import { supplierPartFields } from '../../../forms/CompanyForms';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
openCreateApiForm,
|
||||||
@ -9,8 +11,8 @@ import {
|
|||||||
openEditApiForm
|
openEditApiForm
|
||||||
} from '../../../functions/forms';
|
} from '../../../functions/forms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { UserRoles, useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
@ -110,7 +112,7 @@ export function SupplierPartTable({ params }: { params: any }): ReactNode {
|
|||||||
|
|
||||||
if (part.units) {
|
if (part.units) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text>
|
<Text key="base">
|
||||||
{t`Base units`} : {part.units}
|
{t`Base units`} : {part.units}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
@ -2,10 +2,11 @@ import { t } from '@lingui/macro';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { ModelType } from '../../render/ModelType';
|
|
||||||
import {
|
import {
|
||||||
CreationDateColumn,
|
CreationDateColumn,
|
||||||
DescriptionColumn,
|
DescriptionColumn,
|
||||||
|
@ -2,10 +2,11 @@ import { t } from '@lingui/macro';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { ModelType } from '../../render/ModelType';
|
|
||||||
import {
|
import {
|
||||||
CreationDateColumn,
|
CreationDateColumn,
|
||||||
DescriptionColumn,
|
DescriptionColumn,
|
||||||
|
@ -4,8 +4,9 @@ import { IconReload } from '@tabler/icons-react';
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { api } from '../../../App';
|
import { api } from '../../../App';
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { ActionButton } from '../../buttons/ActionButton';
|
import { ActionButton } from '../../buttons/ActionButton';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
|
@ -2,14 +2,16 @@ import { t } from '@lingui/macro';
|
|||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
openCreateApiForm,
|
||||||
openDeleteApiForm,
|
openDeleteApiForm,
|
||||||
openEditApiForm
|
openEditApiForm
|
||||||
} from '../../../functions/forms';
|
} from '../../../functions/forms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { UserRoles, useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
@ -2,14 +2,16 @@ import { t } from '@lingui/macro';
|
|||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
openCreateApiForm,
|
||||||
openDeleteApiForm,
|
openDeleteApiForm,
|
||||||
openEditApiForm
|
openEditApiForm
|
||||||
} from '../../../functions/forms';
|
} from '../../../functions/forms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { UserRoles, useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { DescriptionColumn } from '../ColumnRenderers';
|
import { DescriptionColumn } from '../ColumnRenderers';
|
||||||
|
@ -4,10 +4,11 @@ import { ReactNode, useMemo } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { formatCurrency, renderDate } from '../../../defaults/formatters';
|
import { formatCurrency, renderDate } from '../../../defaults/formatters';
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { ModelType } from '../../render/ModelType';
|
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { StatusColumn } from '../ColumnRenderers';
|
import { StatusColumn } from '../ColumnRenderers';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
@ -65,49 +66,77 @@ function stockItemTableColumns(): TableColumn[] {
|
|||||||
if (record.is_building) {
|
if (record.is_building) {
|
||||||
color = 'blue';
|
color = 'blue';
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm">{t`This stock item is in production`}</Text>
|
<Text
|
||||||
|
key="production"
|
||||||
|
size="sm"
|
||||||
|
>{t`This stock item is in production`}</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.sales_order) {
|
if (record.sales_order) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm">{t`This stock item has been assigned to a sales order`}</Text>
|
<Text
|
||||||
|
key="sales-order"
|
||||||
|
size="sm"
|
||||||
|
>{t`This stock item has been assigned to a sales order`}</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.customer) {
|
if (record.customer) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm">{t`This stock item has been assigned to a customer`}</Text>
|
<Text
|
||||||
|
key="customer"
|
||||||
|
size="sm"
|
||||||
|
>{t`This stock item has been assigned to a customer`}</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.belongs_to) {
|
if (record.belongs_to) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm">{t`This stock item is installed in another stock item`}</Text>
|
<Text
|
||||||
|
key="belongs-to"
|
||||||
|
size="sm"
|
||||||
|
>{t`This stock item is installed in another stock item`}</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.consumed_by) {
|
if (record.consumed_by) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm">{t`This stock item has been consumed by a build order`}</Text>
|
<Text
|
||||||
|
key="consumed-by"
|
||||||
|
size="sm"
|
||||||
|
>{t`This stock item has been consumed by a build order`}</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.expired) {
|
if (record.expired) {
|
||||||
extra.push(<Text size="sm">{t`This stock item has expired`}</Text>);
|
extra.push(
|
||||||
|
<Text
|
||||||
|
key="expired"
|
||||||
|
size="sm"
|
||||||
|
>{t`This stock item has expired`}</Text>
|
||||||
|
);
|
||||||
} else if (record.stale) {
|
} else if (record.stale) {
|
||||||
extra.push(<Text size="sm">{t`This stock item is stale`}</Text>);
|
extra.push(
|
||||||
|
<Text key="stale" size="sm">{t`This stock item is stale`}</Text>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allocated > 0) {
|
if (allocated > 0) {
|
||||||
if (allocated >= quantity) {
|
if (allocated >= quantity) {
|
||||||
color = 'orange';
|
color = 'orange';
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm">{t`This stock item is fully allocated`}</Text>
|
<Text
|
||||||
|
key="fully-allocated"
|
||||||
|
size="sm"
|
||||||
|
>{t`This stock item is fully allocated`}</Text>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm">{t`This stock item is partially allocated`}</Text>
|
<Text
|
||||||
|
key="partially-allocated"
|
||||||
|
size="sm"
|
||||||
|
>{t`This stock item is partially allocated`}</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,13 +144,17 @@ function stockItemTableColumns(): TableColumn[] {
|
|||||||
if (available != quantity) {
|
if (available != quantity) {
|
||||||
if (available > 0) {
|
if (available > 0) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm" color="orange">
|
<Text key="available" size="sm" color="orange">
|
||||||
{t`Available` + `: ${available}`}
|
{t`Available` + `: ${available}`}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm" color="red">{t`No stock available`}</Text>
|
<Text
|
||||||
|
key="no-stock"
|
||||||
|
size="sm"
|
||||||
|
color="red"
|
||||||
|
>{t`No stock available`}</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,7 +162,10 @@ function stockItemTableColumns(): TableColumn[] {
|
|||||||
if (quantity <= 0) {
|
if (quantity <= 0) {
|
||||||
color = 'red';
|
color = 'red';
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text size="sm">{t`This stock item has been depleted`}</Text>
|
<Text
|
||||||
|
key="depleted"
|
||||||
|
size="sm"
|
||||||
|
>{t`This stock item has been depleted`}</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,9 @@ import { t } from '@lingui/macro';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { YesNoButton } from '../../items/YesNoButton';
|
import { YesNoButton } from '../../items/YesNoButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { DescriptionColumn } from '../ColumnRenderers';
|
import { DescriptionColumn } from '../ColumnRenderers';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ModelType } from '../components/render/ModelType';
|
import { ModelType } from '../enums/ModelType';
|
||||||
|
|
||||||
/* Lookup tables for mapping backend responses to internal types */
|
/* Lookup tables for mapping backend responses to internal types */
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
import { ApiPaths } from '../states/ApiState';
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
|
|
||||||
interface DashboardItems {
|
interface DashboardItems {
|
||||||
id: string;
|
id: string;
|
||||||
|
89
src/frontend/src/enums/ApiEndpoints.tsx
Normal file
89
src/frontend/src/enums/ApiEndpoints.tsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Enumeration of available API endpoints.
|
||||||
|
*/
|
||||||
|
export enum ApiPaths {
|
||||||
|
api_server_info = 'api-server-info',
|
||||||
|
|
||||||
|
api_search = 'api-search',
|
||||||
|
|
||||||
|
// User information
|
||||||
|
user_me = 'api-user-me',
|
||||||
|
user_roles = 'api-user-roles',
|
||||||
|
user_token = 'api-user-token',
|
||||||
|
user_simple_login = 'api-user-simple-login',
|
||||||
|
user_reset = 'api-user-reset',
|
||||||
|
user_reset_set = 'api-user-reset-set',
|
||||||
|
user_sso = 'api-user-sso',
|
||||||
|
user_sso_remove = 'api-user-sso-remove',
|
||||||
|
user_emails = 'api-user-emails',
|
||||||
|
user_email_verify = 'api-user-email-verify',
|
||||||
|
user_email_primary = 'api-user-email-primary',
|
||||||
|
user_email_remove = 'api-user-email-remove',
|
||||||
|
|
||||||
|
user_list = 'api-user-list',
|
||||||
|
owner_list = 'api-owner-list',
|
||||||
|
|
||||||
|
settings_global_list = 'api-settings-global-list',
|
||||||
|
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',
|
||||||
|
version = 'api-version',
|
||||||
|
sso_providers = 'api-sso-providers',
|
||||||
|
|
||||||
|
// Build order URLs
|
||||||
|
build_order_list = 'api-build-list',
|
||||||
|
build_order_attachment_list = 'api-build-attachment-list',
|
||||||
|
|
||||||
|
// BOM URLs
|
||||||
|
bom_list = 'api-bom-list',
|
||||||
|
|
||||||
|
// Part URLs
|
||||||
|
part_list = 'api-part-list',
|
||||||
|
category_list = 'api-category-list',
|
||||||
|
category_tree = 'api-category-tree',
|
||||||
|
related_part_list = 'api-related-part-list',
|
||||||
|
part_attachment_list = 'api-part-attachment-list',
|
||||||
|
part_parameter_list = 'api-part-parameter-list',
|
||||||
|
part_parameter_template_list = 'api-part-parameter-template-list',
|
||||||
|
|
||||||
|
// Company URLs
|
||||||
|
company_list = 'api-company-list',
|
||||||
|
company_attachment_list = 'api-company-attachment-list',
|
||||||
|
supplier_part_list = 'api-supplier-part-list',
|
||||||
|
manufacturer_part_list = 'api-manufacturer-part-list',
|
||||||
|
address_list = 'api-address-list',
|
||||||
|
contact_list = 'api-contact-list',
|
||||||
|
|
||||||
|
// Stock Item URLs
|
||||||
|
stock_item_list = 'api-stock-item-list',
|
||||||
|
stock_tracking_list = 'api-stock-tracking-list',
|
||||||
|
stock_location_list = 'api-stock-location-list',
|
||||||
|
stock_location_tree = 'api-stock-location-tree',
|
||||||
|
stock_attachment_list = 'api-stock-attachment-list',
|
||||||
|
|
||||||
|
// Purchase Order URLs
|
||||||
|
purchase_order_list = 'api-purchase-order-list',
|
||||||
|
purchase_order_line_list = 'api-purchase-order-line-list',
|
||||||
|
purchase_order_attachment_list = 'api-purchase-order-attachment-list',
|
||||||
|
|
||||||
|
// Sales Order URLs
|
||||||
|
sales_order_list = 'api-sales-order-list',
|
||||||
|
sales_order_attachment_list = 'api-sales-order-attachment-list',
|
||||||
|
sales_order_shipment_list = 'api_sales_order_shipment_list',
|
||||||
|
|
||||||
|
// Return Order URLs
|
||||||
|
return_order_list = 'api-return-order-list',
|
||||||
|
return_order_attachment_list = 'api-return-order-attachment-list',
|
||||||
|
|
||||||
|
// Plugin URLs
|
||||||
|
plugin_list = 'api-plugin-list',
|
||||||
|
|
||||||
|
project_code_list = 'api-project-code-list',
|
||||||
|
custom_unit_list = 'api-custom-unit-list'
|
||||||
|
}
|
25
src/frontend/src/enums/ModelType.tsx
Normal file
25
src/frontend/src/enums/ModelType.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Enumeration of available API model types
|
||||||
|
*/
|
||||||
|
export enum ModelType {
|
||||||
|
part = 'part',
|
||||||
|
supplierpart = 'supplierpart',
|
||||||
|
manufacturerpart = 'manufacturerpart',
|
||||||
|
partcategory = 'partcategory',
|
||||||
|
partparametertemplate = 'partparametertemplate',
|
||||||
|
projectcode = 'projectcode',
|
||||||
|
stockitem = 'stockitem',
|
||||||
|
stocklocation = 'stocklocation',
|
||||||
|
stockhistory = 'stockhistory',
|
||||||
|
build = 'build',
|
||||||
|
company = 'company',
|
||||||
|
purchaseorder = 'purchaseorder',
|
||||||
|
purchaseorderline = 'purchaseorderline',
|
||||||
|
salesorder = 'salesorder',
|
||||||
|
salesordershipment = 'salesordershipment',
|
||||||
|
returnorder = 'returnorder',
|
||||||
|
address = 'address',
|
||||||
|
contact = 'contact',
|
||||||
|
owner = 'owner',
|
||||||
|
user = 'user'
|
||||||
|
}
|
25
src/frontend/src/enums/Roles.tsx
Normal file
25
src/frontend/src/enums/Roles.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Enumeration of available user role groups
|
||||||
|
*/
|
||||||
|
export enum UserRoles {
|
||||||
|
admin = 'admin',
|
||||||
|
build = 'build',
|
||||||
|
part = 'part',
|
||||||
|
part_category = 'part_category',
|
||||||
|
purchase_order = 'purchase_order',
|
||||||
|
return_order = 'return_order',
|
||||||
|
sales_order = 'sales_order',
|
||||||
|
stock = 'stock',
|
||||||
|
stock_location = 'stocklocation',
|
||||||
|
stocktake = 'stocktake'
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enumeration of available user permissions within each role group
|
||||||
|
*/
|
||||||
|
export enum UserPermissions {
|
||||||
|
view = 'view',
|
||||||
|
add = 'add',
|
||||||
|
change = 'change',
|
||||||
|
delete = 'delete'
|
||||||
|
}
|
@ -2,12 +2,12 @@ import { t } from '@lingui/macro';
|
|||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
|
|
||||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||||
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
openCreateApiForm,
|
||||||
openDeleteApiForm,
|
openDeleteApiForm,
|
||||||
openEditApiForm
|
openEditApiForm
|
||||||
} from '../functions/forms';
|
} from '../functions/forms';
|
||||||
import { ApiPaths } from '../states/ApiState';
|
|
||||||
|
|
||||||
export function attachmentFields(editing: boolean): ApiFormFieldSet {
|
export function attachmentFields(editing: boolean): ApiFormFieldSet {
|
||||||
let fields: ApiFormFieldSet = {
|
let fields: ApiFormFieldSet = {
|
||||||
|
@ -14,8 +14,8 @@ import {
|
|||||||
ApiFormData,
|
ApiFormData,
|
||||||
ApiFormFieldSet
|
ApiFormFieldSet
|
||||||
} from '../components/forms/fields/ApiFormField';
|
} from '../components/forms/fields/ApiFormField';
|
||||||
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
import { openEditApiForm } from '../functions/forms';
|
import { openEditApiForm } from '../functions/forms';
|
||||||
import { ApiPaths } from '../states/ApiState';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field set for SupplierPart instance
|
* Field set for SupplierPart instance
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||||
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../functions/forms';
|
import { openCreateApiForm, openEditApiForm } from '../functions/forms';
|
||||||
import { ApiPaths } from '../states/ApiState';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a set of fields for creating / editing a Part instance
|
* Construct a set of fields for creating / editing a Part instance
|
||||||
|
@ -16,15 +16,21 @@ import {
|
|||||||
* Construct a set of fields for creating / editing a PurchaseOrderLineItem instance
|
* Construct a set of fields for creating / editing a PurchaseOrderLineItem instance
|
||||||
*/
|
*/
|
||||||
export function purchaseOrderLineItemFields({
|
export function purchaseOrderLineItemFields({
|
||||||
supplierId
|
supplierId,
|
||||||
|
orderId,
|
||||||
|
create = false
|
||||||
}: {
|
}: {
|
||||||
supplierId?: number;
|
supplierId?: number;
|
||||||
|
orderId?: number;
|
||||||
|
create?: boolean;
|
||||||
}) {
|
}) {
|
||||||
let fields: ApiFormFieldSet = {
|
let fields: ApiFormFieldSet = {
|
||||||
order: {
|
order: {
|
||||||
filters: {
|
filters: {
|
||||||
supplier_detail: true
|
supplier_detail: true
|
||||||
}
|
},
|
||||||
|
value: orderId,
|
||||||
|
hidden: create != true || orderId != undefined
|
||||||
},
|
},
|
||||||
part: {
|
part: {
|
||||||
filters: {
|
filters: {
|
||||||
|
@ -5,8 +5,8 @@ import {
|
|||||||
ApiFormData,
|
ApiFormData,
|
||||||
ApiFormFieldSet
|
ApiFormFieldSet
|
||||||
} from '../components/forms/fields/ApiFormField';
|
} from '../components/forms/fields/ApiFormField';
|
||||||
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../functions/forms';
|
import { openCreateApiForm, openEditApiForm } from '../functions/forms';
|
||||||
import { ApiPaths } from '../states/ApiState';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a set of fields for creating / editing a StockItem instance
|
* Construct a set of fields for creating / editing a StockItem instance
|
||||||
|
@ -4,7 +4,8 @@ import { IconCheck } from '@tabler/icons-react';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import { api } from '../App';
|
import { api } from '../App';
|
||||||
import { ApiPaths, apiUrl, useServerApiState } from '../states/ApiState';
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
|
import { apiUrl, useServerApiState } from '../states/ApiState';
|
||||||
import { useLocalState } from '../states/LocalState';
|
import { useLocalState } from '../states/LocalState';
|
||||||
import { useSessionState } from '../states/SessionState';
|
import { useSessionState } from '../states/SessionState';
|
||||||
import {
|
import {
|
||||||
|
21
src/frontend/src/functions/conversion.tsx
Normal file
21
src/frontend/src/functions/conversion.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Determine if the provided value is "true":
|
||||||
|
*
|
||||||
|
* Many settings stored on the server are true/false,
|
||||||
|
* but stored as string values, "true" / "false".
|
||||||
|
*
|
||||||
|
* This function provides a wrapper to ensure that the return type is boolean
|
||||||
|
*/
|
||||||
|
export function isTrue(value: any): boolean {
|
||||||
|
if (value === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = String(value).trim().toLowerCase();
|
||||||
|
|
||||||
|
return ['true', 'yes', '1', 'on', 't', 'y'].includes(s);
|
||||||
|
}
|
@ -2,7 +2,8 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { api } from '../App';
|
import { api } from '../App';
|
||||||
import { ApiPaths, apiUrl } from '../states/ApiState';
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../states/ApiState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hook for loading a single instance of an instance from the API
|
* Custom hook for loading a single instance of an instance from the API
|
||||||
|
@ -14,7 +14,8 @@ import { useNavigate, useSearchParams } from 'react-router-dom';
|
|||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { LanguageContext } from '../../contexts/LanguageContext';
|
import { LanguageContext } from '../../contexts/LanguageContext';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
|
||||||
export default function Set_Password() {
|
export default function Set_Password() {
|
||||||
const simpleForm = useForm({ initialValues: { password: '' } });
|
const simpleForm = useForm({ initialValues: { password: '' } });
|
||||||
|
@ -7,8 +7,9 @@ import { ReactNode, useState } from 'react';
|
|||||||
import { ApiFormProps } from '../../components/forms/ApiForm';
|
import { ApiFormProps } from '../../components/forms/ApiForm';
|
||||||
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/render/StatusRenderer';
|
||||||
import { StatusRenderer } from '../../components/renderers/StatusRenderer';
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import {
|
import {
|
||||||
createPart,
|
createPart,
|
||||||
editPart,
|
editPart,
|
||||||
@ -16,7 +17,6 @@ import {
|
|||||||
} from '../../forms/PartForms';
|
} from '../../forms/PartForms';
|
||||||
import { createStockItem } from '../../forms/StockForms';
|
import { createStockItem } from '../../forms/StockForms';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../../functions/forms';
|
import { openCreateApiForm, openEditApiForm } from '../../functions/forms';
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
|
|
||||||
// Generate some example forms using the modal API forms interface
|
// Generate some example forms using the modal API forms interface
|
||||||
function ApiFormsPlayground() {
|
function ApiFormsPlayground() {
|
||||||
|
@ -47,37 +47,44 @@ import { api } from '../../App';
|
|||||||
import { DocInfo } from '../../components/items/DocInfo';
|
import { DocInfo } from '../../components/items/DocInfo';
|
||||||
import { StylishText } from '../../components/items/StylishText';
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
import { TitleWithDoc } from '../../components/items/TitleWithDoc';
|
import { TitleWithDoc } from '../../components/items/TitleWithDoc';
|
||||||
import { Render, RenderTypes } from '../../components/renderers';
|
import { RenderInstance } from '../../components/render/Instance';
|
||||||
|
import { ModelInformationDict } from '../../components/render/ModelType';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
import { notYetImplemented } from '../../functions/notifications';
|
||||||
import { IS_DEV_OR_DEMO } from '../../main';
|
import { IS_DEV_OR_DEMO } from '../../main';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
|
||||||
interface ScanItem {
|
interface ScanItem {
|
||||||
id: string;
|
id: string;
|
||||||
ref: string;
|
ref: string;
|
||||||
data: any;
|
data: any;
|
||||||
|
instance?: any;
|
||||||
timestamp: Date;
|
timestamp: Date;
|
||||||
source: string;
|
source: string;
|
||||||
link?: string;
|
link?: string;
|
||||||
objectType?: RenderTypes;
|
model?: ModelType;
|
||||||
objectPk?: string;
|
pk?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchObject(rd: any): [RenderTypes | undefined, string | undefined] {
|
/*
|
||||||
|
* Match the scanned object to a known internal model type
|
||||||
|
*/
|
||||||
|
function matchObject(rd: any): [ModelType | undefined, string | undefined] {
|
||||||
if (rd?.part) {
|
if (rd?.part) {
|
||||||
return [RenderTypes.part, rd?.part.pk];
|
return [ModelType.part, rd?.part.pk];
|
||||||
} else if (rd?.stockitem) {
|
} else if (rd?.stockitem) {
|
||||||
return [RenderTypes.stock_item, rd?.stockitem.pk];
|
return [ModelType.stockitem, rd?.stockitem.pk];
|
||||||
} else if (rd?.stocklocation) {
|
} else if (rd?.stocklocation) {
|
||||||
return [RenderTypes.stock_location, rd?.stocklocation.pk];
|
return [ModelType.stocklocation, rd?.stocklocation.pk];
|
||||||
} else if (rd?.supplierpart) {
|
} else if (rd?.supplierpart) {
|
||||||
return [RenderTypes.supplier_part, rd?.supplierpart.pk];
|
return [ModelType.supplierpart, rd?.supplierpart.pk];
|
||||||
} else if (rd?.purchaseorder) {
|
} else if (rd?.purchaseorder) {
|
||||||
return [RenderTypes.purchase_order, rd?.purchaseorder.pk];
|
return [ModelType.purchaseorder, rd?.purchaseorder.pk];
|
||||||
} else if (rd?.salesorder) {
|
} else if (rd?.salesorder) {
|
||||||
return [RenderTypes.sales_order, rd?.salesorder.pk];
|
return [ModelType.salesorder, rd?.salesorder.pk];
|
||||||
} else if (rd?.build) {
|
} else if (rd?.build) {
|
||||||
return [RenderTypes.build_order, rd?.build.pk];
|
return [ModelType.build, rd?.build.pk];
|
||||||
} else {
|
} else {
|
||||||
return [undefined, undefined];
|
return [undefined, undefined];
|
||||||
}
|
}
|
||||||
@ -147,8 +154,27 @@ export default function Scan() {
|
|||||||
item.link = response.data?.url;
|
item.link = response.data?.url;
|
||||||
|
|
||||||
const rsp = matchObject(response.data);
|
const rsp = matchObject(response.data);
|
||||||
item.objectType = rsp[0];
|
item.model = rsp[0];
|
||||||
item.objectPk = rsp[1];
|
item.pk = rsp[1];
|
||||||
|
|
||||||
|
// Fetch instance data
|
||||||
|
if (item.model && item.pk) {
|
||||||
|
let model_info = ModelInformationDict[item.model];
|
||||||
|
|
||||||
|
if (model_info && model_info.api_endpoint) {
|
||||||
|
let url = apiUrl(model_info.api_endpoint, item.pk);
|
||||||
|
|
||||||
|
api
|
||||||
|
.get(url)
|
||||||
|
.then((response) => {
|
||||||
|
item.instance = response.data;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('error while fetching instance data at', url);
|
||||||
|
console.info(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
historyHandlers.setState(history);
|
historyHandlers.setState(history);
|
||||||
})
|
})
|
||||||
@ -206,7 +232,7 @@ export default function Scan() {
|
|||||||
...new Set(
|
...new Set(
|
||||||
selection
|
selection
|
||||||
.map((id) => {
|
.map((id) => {
|
||||||
return history.find((item) => item.id === id)?.objectType;
|
return history.find((item) => item.id === id)?.model;
|
||||||
})
|
})
|
||||||
.filter((item) => item != undefined)
|
.filter((item) => item != undefined)
|
||||||
)
|
)
|
||||||
@ -384,13 +410,13 @@ function HistoryTable({
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{item.objectPk && item.objectType ? (
|
{item.pk && item.model && item.instance ? (
|
||||||
<Render type={item.objectType} pk={item.objectPk} />
|
<RenderInstance model={item.model} instance={item.instance} />
|
||||||
) : (
|
) : (
|
||||||
item.ref
|
item.ref
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>{item.objectType}</td>
|
<td>{item.model}</td>
|
||||||
<td>{item.source}</td>
|
<td>{item.source}</td>
|
||||||
<td>{item.timestamp?.toString()}</td>
|
<td>{item.timestamp?.toString()}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -5,7 +5,8 @@ import { useToggle } from '@mantine/hooks';
|
|||||||
|
|
||||||
import { api } from '../../../../App';
|
import { api } from '../../../../App';
|
||||||
import { EditButton } from '../../../../components/items/EditButton';
|
import { EditButton } from '../../../../components/items/EditButton';
|
||||||
import { ApiPaths, apiUrl } from '../../../../states/ApiState';
|
import { ApiPaths } from '../../../../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../../../../states/ApiState';
|
||||||
import { useUserState } from '../../../../states/UserState';
|
import { useUserState } from '../../../../states/UserState';
|
||||||
|
|
||||||
export function AccountDetailPanel() {
|
export function AccountDetailPanel() {
|
||||||
|
@ -19,7 +19,8 @@ import { useEffect, useState } from 'react';
|
|||||||
|
|
||||||
import { api, queryClient } from '../../../../App';
|
import { api, queryClient } from '../../../../App';
|
||||||
import { PlaceholderPill } from '../../../../components/items/Placeholder';
|
import { PlaceholderPill } from '../../../../components/items/Placeholder';
|
||||||
import { ApiPaths, apiUrl } from '../../../../states/ApiState';
|
import { ApiPaths } from '../../../../enums/ApiEndpoints';
|
||||||
|
import { apiUrl } from '../../../../states/ApiState';
|
||||||
|
|
||||||
export function SecurityContent() {
|
export function SecurityContent() {
|
||||||
const [isSsoEnabled, setIsSsoEnabled] = useState<boolean>(false);
|
const [isSsoEnabled, setIsSsoEnabled] = useState<boolean>(false);
|
||||||
|
@ -86,10 +86,8 @@ export default function AdminCenter() {
|
|||||||
<>
|
<>
|
||||||
<Stack spacing="xs">
|
<Stack spacing="xs">
|
||||||
<SettingsHeader
|
<SettingsHeader
|
||||||
title={<Trans>Admin Center</Trans>}
|
title={t`Admin Center`}
|
||||||
subtitle={
|
subtitle={t`Advanced Amininistrative Options for InvenTree`}
|
||||||
<Trans>Advanced Amininistrative Options for InvenTree</Trans>
|
|
||||||
}
|
|
||||||
switch_link="/settings/system"
|
switch_link="/settings/system"
|
||||||
switch_text="System Settings"
|
switch_text="System Settings"
|
||||||
/>
|
/>
|
||||||
|
@ -3,11 +3,11 @@ import { LoadingOverlay, Stack } from '@mantine/core';
|
|||||||
import { IconPlugConnected } from '@tabler/icons-react';
|
import { IconPlugConnected } from '@tabler/icons-react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { PageDetail } from '../../../components/nav/PageDetail';
|
|
||||||
import { PanelGroup, PanelType } from '../../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../../components/nav/PanelGroup';
|
||||||
|
import { SettingsHeader } from '../../../components/nav/SettingsHeader';
|
||||||
import { PluginListTable } from '../../../components/tables/plugin/PluginListTable';
|
import { PluginListTable } from '../../../components/tables/plugin/PluginListTable';
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
import { useInstance } from '../../../hooks/UseInstance';
|
import { useInstance } from '../../../hooks/UseInstance';
|
||||||
import { ApiPaths } from '../../../states/ApiState';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugins settings page
|
* Plugins settings page
|
||||||
@ -44,7 +44,7 @@ export default function PluginSettings() {
|
|||||||
<>
|
<>
|
||||||
<Stack spacing="xs">
|
<Stack spacing="xs">
|
||||||
<LoadingOverlay visible={settingsQuery.isFetching} />
|
<LoadingOverlay visible={settingsQuery.isFetching} />
|
||||||
<PageDetail title={t`Plugin Settings`} />
|
<SettingsHeader title={t`Plugin Settings`} switch_condition={false} />
|
||||||
<PanelGroup pageKey="plugin-settings" panels={pluginPanels} />
|
<PanelGroup pageKey="plugin-settings" panels={pluginPanels} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
|
@ -292,8 +292,8 @@ export default function SystemSettings() {
|
|||||||
<>
|
<>
|
||||||
<Stack spacing="xs">
|
<Stack spacing="xs">
|
||||||
<SettingsHeader
|
<SettingsHeader
|
||||||
title={server.instance || ''}
|
title={t`System Settings`}
|
||||||
subtitle={<Trans>System Settings</Trans>}
|
subtitle={server.instance || ''}
|
||||||
switch_link="/settings/user"
|
switch_link="/settings/user"
|
||||||
switch_text={<Trans>Switch to User Setting</Trans>}
|
switch_text={<Trans>Switch to User Setting</Trans>}
|
||||||
/>
|
/>
|
||||||
|
@ -112,9 +112,9 @@ export default function UserSettings() {
|
|||||||
<>
|
<>
|
||||||
<Stack spacing="xs">
|
<Stack spacing="xs">
|
||||||
<SettingsHeader
|
<SettingsHeader
|
||||||
title={`${user?.first_name} ${user?.last_name}`}
|
title={t`Account Settings`}
|
||||||
|
subtitle={`${user?.first_name} ${user?.last_name}`}
|
||||||
shorthand={user?.username || ''}
|
shorthand={user?.username || ''}
|
||||||
subtitle={<Trans>Account Settings</Trans>}
|
|
||||||
switch_link="/settings/system"
|
switch_link="/settings/system"
|
||||||
switch_text={<Trans>Switch to System Setting</Trans>}
|
switch_text={<Trans>Switch to System Setting</Trans>}
|
||||||
switch_condition={user?.is_staff || false}
|
switch_condition={user?.is_staff || false}
|
||||||
|
@ -13,8 +13,9 @@ import { api } from '../App';
|
|||||||
import { PageDetail } from '../components/nav/PageDetail';
|
import { PageDetail } from '../components/nav/PageDetail';
|
||||||
import { PanelGroup } from '../components/nav/PanelGroup';
|
import { PanelGroup } from '../components/nav/PanelGroup';
|
||||||
import { NotificationTable } from '../components/tables/notifications/NotificationsTable';
|
import { NotificationTable } from '../components/tables/notifications/NotificationsTable';
|
||||||
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
import { useTableRefresh } from '../hooks/TableRefresh';
|
import { useTableRefresh } from '../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../states/ApiState';
|
import { apiUrl } from '../states/ApiState';
|
||||||
|
|
||||||
export default function NotificationsPage() {
|
export default function NotificationsPage() {
|
||||||
const unreadRefresh = useTableRefresh('unreadnotifications');
|
const unreadRefresh = useTableRefresh('unreadnotifications');
|
||||||
|
@ -14,8 +14,7 @@ import {
|
|||||||
IconPaperclip,
|
IconPaperclip,
|
||||||
IconPrinter,
|
IconPrinter,
|
||||||
IconQrcode,
|
IconQrcode,
|
||||||
IconSitemap,
|
IconSitemap
|
||||||
IconTrash
|
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
@ -31,16 +30,17 @@ import {
|
|||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { ModelType } from '../../components/render/ModelType';
|
import { StatusRenderer } from '../../components/render/StatusRenderer';
|
||||||
import { StatusRenderer } from '../../components/renderers/StatusRenderer';
|
|
||||||
import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
|
import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
|
||||||
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
||||||
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { buildOrderFields } from '../../forms/BuildForms';
|
import { buildOrderFields } from '../../forms/BuildForms';
|
||||||
import { openEditApiForm } from '../../functions/forms';
|
import { openEditApiForm } from '../../functions/forms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,9 +5,9 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
|
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
|
import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { buildOrderFields } from '../../forms/BuildForms';
|
import { buildOrderFields } from '../../forms/BuildForms';
|
||||||
import { openCreateApiForm } from '../../functions/forms';
|
import { openCreateApiForm } from '../../functions/forms';
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build Order index page
|
* Build Order index page
|
||||||
|
@ -33,10 +33,12 @@ import { ReturnOrderTable } from '../../components/tables/sales/ReturnOrderTable
|
|||||||
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
||||||
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { editCompany } from '../../forms/CompanyForms';
|
import { editCompany } from '../../forms/CompanyForms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { UserRoles, useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
|
|
||||||
export type CompanyDetailProps = {
|
export type CompanyDetailProps = {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -14,8 +14,8 @@ import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
|||||||
import { PartCategoryTree } from '../../components/nav/PartCategoryTree';
|
import { PartCategoryTree } from '../../components/nav/PartCategoryTree';
|
||||||
import { PartCategoryTable } from '../../components/tables/part/PartCategoryTable';
|
import { PartCategoryTable } from '../../components/tables/part/PartCategoryTable';
|
||||||
import { PartListTable } from '../../components/tables/part/PartTable';
|
import { PartListTable } from '../../components/tables/part/PartTable';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detail view for a single PartCategory instance.
|
* Detail view for a single PartCategory instance.
|
||||||
|
@ -50,9 +50,10 @@ import { SupplierPartTable } from '../../components/tables/purchasing/SupplierPa
|
|||||||
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
||||||
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { editPart } from '../../forms/PartForms';
|
import { editPart } from '../../forms/PartForms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,8 +26,9 @@ import { AttachmentTable } from '../../components/tables/general/AttachmentTable
|
|||||||
import { PurchaseOrderLineItemTable } from '../../components/tables/purchasing/PurchaseOrderLineItemTable';
|
import { PurchaseOrderLineItemTable } from '../../components/tables/purchasing/PurchaseOrderLineItemTable';
|
||||||
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,7 +115,7 @@ export default function PurchaseOrderDetail() {
|
|||||||
]}
|
]}
|
||||||
/>,
|
/>,
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
key="order"
|
key="order-actions"
|
||||||
tooltip={t`Order Actions`}
|
tooltip={t`Order Actions`}
|
||||||
icon={<IconDots />}
|
icon={<IconDots />}
|
||||||
actions={[EditItemAction({}), DeleteItemAction({})]}
|
actions={[EditItemAction({}), DeleteItemAction({})]}
|
||||||
|
@ -8,8 +8,9 @@ import { PageDetail } from '../../components/nav/PageDetail';
|
|||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detail page for a single ReturnOrder
|
* Detail page for a single ReturnOrder
|
||||||
|
@ -16,8 +16,9 @@ import { PageDetail } from '../../components/nav/PageDetail';
|
|||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detail page for a single SalesOrder
|
* Detail page for a single SalesOrder
|
||||||
|
@ -9,8 +9,8 @@ import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
|||||||
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
||||||
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||||
import { StockLocationTable } from '../../components/tables/stock/StockLocationTable';
|
import { StockLocationTable } from '../../components/tables/stock/StockLocationTable';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths } from '../../states/ApiState';
|
|
||||||
|
|
||||||
export default function Stock() {
|
export default function Stock() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
@ -35,9 +35,10 @@ import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
|||||||
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
||||||
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { editStockItem } from '../../forms/StockForms';
|
import { editStockItem } from '../../forms/StockForms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
|
|
||||||
export default function StockDetail() {
|
export default function StockDetail() {
|
||||||
|
@ -2,10 +2,11 @@ import { create } from 'zustand';
|
|||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
import { api } from '../App';
|
import { api } from '../App';
|
||||||
import { ModelType } from '../components/render/ModelType';
|
import { StatusCodeListInterface } from '../components/render/StatusRenderer';
|
||||||
import { StatusCodeListInterface } from '../components/renderers/StatusRenderer';
|
|
||||||
import { statusCodeList } from '../defaults/backendMappings';
|
import { statusCodeList } from '../defaults/backendMappings';
|
||||||
import { emptyServerAPI } from '../defaults/defaults';
|
import { emptyServerAPI } from '../defaults/defaults';
|
||||||
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../enums/ModelType';
|
||||||
import { ServerAPIProps } from './states';
|
import { ServerAPIProps } from './states';
|
||||||
|
|
||||||
type StatusLookup = Record<ModelType, StatusCodeListInterface>;
|
type StatusLookup = Record<ModelType, StatusCodeListInterface>;
|
||||||
@ -48,87 +49,6 @@ export const useServerApiState = create<ServerApiStateProps>()(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export enum ApiPaths {
|
|
||||||
api_server_info = 'api-server-info',
|
|
||||||
|
|
||||||
api_search = 'api-search',
|
|
||||||
|
|
||||||
// User information
|
|
||||||
user_me = 'api-user-me',
|
|
||||||
user_roles = 'api-user-roles',
|
|
||||||
user_token = 'api-user-token',
|
|
||||||
user_simple_login = 'api-user-simple-login',
|
|
||||||
user_reset = 'api-user-reset',
|
|
||||||
user_reset_set = 'api-user-reset-set',
|
|
||||||
user_sso = 'api-user-sso',
|
|
||||||
user_sso_remove = 'api-user-sso-remove',
|
|
||||||
user_emails = 'api-user-emails',
|
|
||||||
user_email_verify = 'api-user-email-verify',
|
|
||||||
user_email_primary = 'api-user-email-primary',
|
|
||||||
user_email_remove = 'api-user-email-remove',
|
|
||||||
|
|
||||||
settings_global_list = 'api-settings-global-list',
|
|
||||||
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',
|
|
||||||
version = 'api-version',
|
|
||||||
sso_providers = 'api-sso-providers',
|
|
||||||
|
|
||||||
// Build order URLs
|
|
||||||
build_order_list = 'api-build-list',
|
|
||||||
build_order_attachment_list = 'api-build-attachment-list',
|
|
||||||
|
|
||||||
// BOM URLs
|
|
||||||
bom_list = 'api-bom-list',
|
|
||||||
|
|
||||||
// Part URLs
|
|
||||||
part_list = 'api-part-list',
|
|
||||||
category_list = 'api-category-list',
|
|
||||||
category_tree = 'api-category-tree',
|
|
||||||
related_part_list = 'api-related-part-list',
|
|
||||||
part_attachment_list = 'api-part-attachment-list',
|
|
||||||
part_parameter_list = 'api-part-parameter-list',
|
|
||||||
part_parameter_template_list = 'api-part-parameter-template-list',
|
|
||||||
|
|
||||||
// Company URLs
|
|
||||||
company_list = 'api-company-list',
|
|
||||||
company_attachment_list = 'api-company-attachment-list',
|
|
||||||
supplier_part_list = 'api-supplier-part-list',
|
|
||||||
manufacturer_part_list = 'api-manufacturer-part-list',
|
|
||||||
|
|
||||||
// Stock Item URLs
|
|
||||||
stock_item_list = 'api-stock-item-list',
|
|
||||||
stock_tracking_list = 'api-stock-tracking-list',
|
|
||||||
stock_location_list = 'api-stock-location-list',
|
|
||||||
stock_location_tree = 'api-stock-location-tree',
|
|
||||||
stock_attachment_list = 'api-stock-attachment-list',
|
|
||||||
|
|
||||||
// Purchase Order URLs
|
|
||||||
purchase_order_list = 'api-purchase-order-list',
|
|
||||||
purchase_order_line_list = 'api-purchase-order-line-list',
|
|
||||||
purchase_order_attachment_list = 'api-purchase-order-attachment-list',
|
|
||||||
|
|
||||||
// Sales Order URLs
|
|
||||||
sales_order_list = 'api-sales-order-list',
|
|
||||||
sales_order_attachment_list = 'api-sales-order-attachment-list',
|
|
||||||
|
|
||||||
// Return Order URLs
|
|
||||||
return_order_list = 'api-return-order-list',
|
|
||||||
return_order_attachment_list = 'api-return-order-attachment-list',
|
|
||||||
|
|
||||||
// Plugin URLs
|
|
||||||
plugin_list = 'api-plugin-list',
|
|
||||||
|
|
||||||
project_code_list = 'api-project-code-list',
|
|
||||||
custom_unit_list = 'api-custom-unit-list'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to return the API prefix.
|
* Function to return the API prefix.
|
||||||
* For now it is fixed, but may be configurable in the future.
|
* For now it is fixed, but may be configurable in the future.
|
||||||
@ -144,6 +64,10 @@ export function apiEndpoint(path: ApiPaths): string {
|
|||||||
switch (path) {
|
switch (path) {
|
||||||
case ApiPaths.api_server_info:
|
case ApiPaths.api_server_info:
|
||||||
return '';
|
return '';
|
||||||
|
case ApiPaths.user_list:
|
||||||
|
return 'user/';
|
||||||
|
case ApiPaths.owner_list:
|
||||||
|
return 'user/owner/';
|
||||||
case ApiPaths.user_me:
|
case ApiPaths.user_me:
|
||||||
return 'user/me/';
|
return 'user/me/';
|
||||||
case ApiPaths.user_roles:
|
case ApiPaths.user_roles:
|
||||||
@ -214,6 +138,10 @@ export function apiEndpoint(path: ApiPaths): string {
|
|||||||
return 'part/attachment/';
|
return 'part/attachment/';
|
||||||
case ApiPaths.company_list:
|
case ApiPaths.company_list:
|
||||||
return 'company/';
|
return 'company/';
|
||||||
|
case ApiPaths.contact_list:
|
||||||
|
return 'company/contact/';
|
||||||
|
case ApiPaths.address_list:
|
||||||
|
return 'company/address/';
|
||||||
case ApiPaths.company_attachment_list:
|
case ApiPaths.company_attachment_list:
|
||||||
return 'company/attachment/';
|
return 'company/attachment/';
|
||||||
case ApiPaths.supplier_part_list:
|
case ApiPaths.supplier_part_list:
|
||||||
@ -240,6 +168,8 @@ export function apiEndpoint(path: ApiPaths): string {
|
|||||||
return 'order/so/';
|
return 'order/so/';
|
||||||
case ApiPaths.sales_order_attachment_list:
|
case ApiPaths.sales_order_attachment_list:
|
||||||
return 'order/so/attachment/';
|
return 'order/so/attachment/';
|
||||||
|
case ApiPaths.sales_order_shipment_list:
|
||||||
|
return 'order/so/shipment/';
|
||||||
case ApiPaths.return_order_list:
|
case ApiPaths.return_order_list:
|
||||||
return 'order/ro/';
|
return 'order/ro/';
|
||||||
case ApiPaths.return_order_attachment_list:
|
case ApiPaths.return_order_attachment_list:
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
|
||||||
import { api } from '../App';
|
import { api } from '../App';
|
||||||
import { ApiPaths, apiUrl } from './ApiState';
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
|
import { isTrue } from '../functions/conversion';
|
||||||
|
import { apiUrl } from './ApiState';
|
||||||
import { Setting, SettingsLookup } from './states';
|
import { Setting, SettingsLookup } from './states';
|
||||||
|
|
||||||
export interface SettingsStateProps {
|
export interface SettingsStateProps {
|
||||||
@ -12,6 +14,8 @@ export interface SettingsStateProps {
|
|||||||
lookup: SettingsLookup;
|
lookup: SettingsLookup;
|
||||||
fetchSettings: () => void;
|
fetchSettings: () => void;
|
||||||
endpoint: ApiPaths;
|
endpoint: ApiPaths;
|
||||||
|
getSetting: (key: string, default_value?: string) => string; // Return a raw setting value
|
||||||
|
isSet: (key: string, default_value?: boolean) => boolean; // Check a "boolean" setting
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,6 +38,13 @@ export const useGlobalSettingsState = create<SettingsStateProps>(
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Error fetching global settings:', error);
|
console.error('Error fetching global settings:', error);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
getSetting: (key: string, default_value?: string) => {
|
||||||
|
return get().lookup[key] ?? default_value ?? '';
|
||||||
|
},
|
||||||
|
isSet: (key: string, default_value?: boolean) => {
|
||||||
|
let value = get().lookup[key] ?? default_value ?? 'false';
|
||||||
|
return isTrue(value);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -57,6 +68,13 @@ export const useUserSettingsState = create<SettingsStateProps>((set, get) => ({
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Error fetching user settings:', error);
|
console.error('Error fetching user settings:', error);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
getSetting: (key: string, default_value?: string) => {
|
||||||
|
return get().lookup[key] ?? default_value ?? '';
|
||||||
|
},
|
||||||
|
isSet: (key: string, default_value?: boolean) => {
|
||||||
|
let value = get().lookup[key] ?? default_value ?? 'false';
|
||||||
|
return isTrue(value);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -1,36 +1,12 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
|
||||||
import { api } from '../App';
|
import { api } from '../App';
|
||||||
|
import { ApiPaths } from '../enums/ApiEndpoints';
|
||||||
|
import { UserPermissions, UserRoles } from '../enums/Roles';
|
||||||
import { doClassicLogout } from '../functions/auth';
|
import { doClassicLogout } from '../functions/auth';
|
||||||
import { ApiPaths, apiUrl } from './ApiState';
|
import { apiUrl } from './ApiState';
|
||||||
import { UserProps } from './states';
|
import { UserProps } from './states';
|
||||||
|
|
||||||
/*
|
|
||||||
* Enumeration of available user role groups
|
|
||||||
*/
|
|
||||||
export enum UserRoles {
|
|
||||||
admin = 'admin',
|
|
||||||
build = 'build',
|
|
||||||
part = 'part',
|
|
||||||
part_category = 'part_category',
|
|
||||||
purchase_order = 'purchase_order',
|
|
||||||
return_order = 'return_order',
|
|
||||||
sales_order = 'sales_order',
|
|
||||||
stock = 'stock',
|
|
||||||
stock_location = 'stocklocation',
|
|
||||||
stocktake = 'stocktake'
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enumeration of available user permissions within each role group
|
|
||||||
*/
|
|
||||||
export enum UserPermissions {
|
|
||||||
view = 'view',
|
|
||||||
add = 'add',
|
|
||||||
change = 'change',
|
|
||||||
delete = 'delete'
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserStateProps {
|
interface UserStateProps {
|
||||||
user: UserProps | undefined;
|
user: UserProps | undefined;
|
||||||
username: () => string;
|
username: () => string;
|
||||||
@ -86,9 +62,9 @@ export const useUserState = create<UserStateProps>((set, get) => ({
|
|||||||
const user: UserProps = get().user as UserProps;
|
const user: UserProps = get().user as UserProps;
|
||||||
|
|
||||||
// Update user with role data
|
// Update user with role data
|
||||||
user.roles = response.data.roles;
|
user.roles = response.data?.roles ?? {};
|
||||||
user.is_staff = response.data.is_staff ?? false;
|
user.is_staff = response.data?.is_staff ?? false;
|
||||||
user.is_superuser = response.data.is_superuser ?? false;
|
user.is_superuser = response.data?.is_superuser ?? false;
|
||||||
set({ user: user });
|
set({ user: user });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@ -99,11 +75,15 @@ export const useUserState = create<UserStateProps>((set, get) => ({
|
|||||||
// Check if the user has the specified permission for the specified role
|
// Check if the user has the specified permission for the specified role
|
||||||
const user: UserProps = get().user as UserProps;
|
const user: UserProps = get().user as UserProps;
|
||||||
|
|
||||||
if (user.is_superuser) return true;
|
if (!user) {
|
||||||
if (user.roles === undefined) return false;
|
return false;
|
||||||
if (user.roles[role] === undefined) return false;
|
}
|
||||||
|
|
||||||
return user.roles[role].includes(permission);
|
if (user?.is_superuser) return true;
|
||||||
|
if (user?.roles === undefined) return false;
|
||||||
|
if (user?.roles[role] === undefined) return false;
|
||||||
|
|
||||||
|
return user?.roles[role].includes(permission);
|
||||||
},
|
},
|
||||||
hasDeleteRole: (role: UserRoles) => {
|
hasDeleteRole: (role: UserRoles) => {
|
||||||
return get().checkUserRole(role, UserPermissions.delete);
|
return get().checkUserRole(role, UserPermissions.delete);
|
||||||
|
Loading…
Reference in New Issue
Block a user