mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 03:56:43 +00:00
[React] Use typed paths (#5686)
* Use typed paths for all tables * Refactor usage of useInstance hook * Refactor URLs for existing forms * More URL fixes * Further URL fixes
This commit is contained in:
parent
149e5c3696
commit
bf7c1b43bd
@ -17,6 +17,7 @@ import { useState } from 'react';
|
|||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
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 {
|
import {
|
||||||
ApiFormField,
|
ApiFormField,
|
||||||
ApiFormFieldSet,
|
ApiFormFieldSet,
|
||||||
@ -45,8 +46,8 @@ import {
|
|||||||
*/
|
*/
|
||||||
export interface ApiFormProps {
|
export interface ApiFormProps {
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: ApiPaths;
|
||||||
pk?: number;
|
pk?: number | string;
|
||||||
title: string;
|
title: string;
|
||||||
fields?: ApiFormFieldSet;
|
fields?: ApiFormFieldSet;
|
||||||
cancelText?: string;
|
cancelText?: string;
|
||||||
|
@ -41,9 +41,8 @@ export type ApiFormChangeCallback = {
|
|||||||
* @param value : The value of the field
|
* @param value : The value of the field
|
||||||
* @param default : The default value of the field
|
* @param default : The default value of the field
|
||||||
* @param icon : An icon to display next to the field
|
* @param icon : An icon to display next to the field
|
||||||
* @param fieldType : The type of field to render
|
* @param field_type : The type of field to render
|
||||||
* @param api_url : The API endpoint to fetch data from (for related fields)
|
* @param api_url : The API endpoint to fetch data from (for related fields)
|
||||||
* @param read_only : Whether the field is read-only
|
|
||||||
* @param model : The model to use for related fields
|
* @param model : The model to use for related fields
|
||||||
* @param filters : Optional API filters to apply to related fields
|
* @param filters : Optional API filters to apply to related fields
|
||||||
* @param required : Whether the field is required
|
* @param required : Whether the field is required
|
||||||
@ -61,9 +60,8 @@ export type ApiFormFieldType = {
|
|||||||
value?: any;
|
value?: any;
|
||||||
default?: any;
|
default?: any;
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
fieldType?: string;
|
field_type?: string;
|
||||||
api_url?: string;
|
api_url?: string;
|
||||||
read_only?: boolean;
|
|
||||||
model?: ModelType;
|
model?: ModelType;
|
||||||
filters?: any;
|
filters?: any;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
@ -99,8 +97,6 @@ export function constructField({
|
|||||||
...field
|
...field
|
||||||
};
|
};
|
||||||
|
|
||||||
def.disabled = def.disabled || def.read_only;
|
|
||||||
|
|
||||||
// Retrieve the latest value from the form
|
// Retrieve the latest value from the form
|
||||||
let value = form.values[fieldName];
|
let value = form.values[fieldName];
|
||||||
|
|
||||||
@ -109,7 +105,7 @@ export function constructField({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Change value to a date object if required
|
// Change value to a date object if required
|
||||||
switch (def.fieldType) {
|
switch (def.field_type) {
|
||||||
case 'date':
|
case 'date':
|
||||||
if (def.value) {
|
if (def.value) {
|
||||||
def.value = new Date(def.value);
|
def.value = new Date(def.value);
|
||||||
@ -192,9 +188,23 @@ export function ApiFormField({
|
|||||||
|
|
||||||
const value: any = useMemo(() => form.values[fieldName], [form.values]);
|
const value: any = useMemo(() => form.values[fieldName], [form.values]);
|
||||||
|
|
||||||
|
// Coerce the value to a numerical value
|
||||||
|
const numericalValue: number | undefined = useMemo(() => {
|
||||||
|
switch (definition.field_type) {
|
||||||
|
case 'integer':
|
||||||
|
return parseInt(value);
|
||||||
|
case 'decimal':
|
||||||
|
case 'float':
|
||||||
|
case 'number':
|
||||||
|
return parseFloat(value);
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
// Construct the individual field
|
// Construct the individual field
|
||||||
function buildField() {
|
function buildField() {
|
||||||
switch (definition.fieldType) {
|
switch (definition.field_type) {
|
||||||
case 'related field':
|
case 'related field':
|
||||||
return (
|
return (
|
||||||
<RelatedModelField
|
<RelatedModelField
|
||||||
@ -213,8 +223,8 @@ export function ApiFormField({
|
|||||||
<TextInput
|
<TextInput
|
||||||
{...definition}
|
{...definition}
|
||||||
id={fieldId}
|
id={fieldId}
|
||||||
type={definition.fieldType}
|
type={definition.field_type}
|
||||||
value={value}
|
value={value || ''}
|
||||||
error={error}
|
error={error}
|
||||||
radius="sm"
|
radius="sm"
|
||||||
onChange={(event) => onChange(event.currentTarget.value)}
|
onChange={(event) => onChange(event.currentTarget.value)}
|
||||||
@ -260,7 +270,7 @@ export function ApiFormField({
|
|||||||
{...definition}
|
{...definition}
|
||||||
radius="sm"
|
radius="sm"
|
||||||
id={fieldId}
|
id={fieldId}
|
||||||
value={value}
|
value={numericalValue}
|
||||||
error={error}
|
error={error}
|
||||||
onChange={(value: number) => onChange(value)}
|
onChange={(value: number) => onChange(value)}
|
||||||
/>
|
/>
|
||||||
@ -289,7 +299,8 @@ export function ApiFormField({
|
|||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Alert color="red" title={t`Error`}>
|
<Alert color="red" title={t`Error`}>
|
||||||
Invalid field type for field '{fieldName}': '{definition.fieldType}'
|
Invalid field type for field '{fieldName}': '{definition.field_type}
|
||||||
|
'
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,7 @@ export function ChoiceField({
|
|||||||
data={choices}
|
data={choices}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(value) => onChange(value)}
|
onChange={(value) => onChange(value)}
|
||||||
|
withinPortal={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ export const BuildOrderRenderer = ({ pk }: { pk: string }) => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<GeneralRenderer
|
<GeneralRenderer
|
||||||
api_key={ApiPaths.build_order_detail}
|
api_key={ApiPaths.build_order_list}
|
||||||
api_ref="build_order"
|
api_ref="build_order"
|
||||||
link={`/build/${pk}`}
|
link={`/build/${pk}`}
|
||||||
pk={pk}
|
pk={pk}
|
||||||
|
@ -12,7 +12,7 @@ export const PartRenderer = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<GeneralRenderer
|
<GeneralRenderer
|
||||||
api_key={ApiPaths.part_detail}
|
api_key={ApiPaths.part_list}
|
||||||
api_ref="part"
|
api_ref="part"
|
||||||
link={link ? `/part/${pk}` : ''}
|
link={link ? `/part/${pk}` : ''}
|
||||||
pk={pk}
|
pk={pk}
|
||||||
|
@ -16,7 +16,7 @@ export const PurchaseOrderRenderer = ({ pk }: { pk: string }) => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<GeneralRenderer
|
<GeneralRenderer
|
||||||
api_key={ApiPaths.purchase_order_detail}
|
api_key={ApiPaths.purchase_order_list}
|
||||||
api_ref="pruchaseorder"
|
api_ref="pruchaseorder"
|
||||||
link={`/order/purchase-order/${pk}`}
|
link={`/order/purchase-order/${pk}`}
|
||||||
pk={pk}
|
pk={pk}
|
||||||
|
@ -4,7 +4,7 @@ import { GeneralRenderer } from './GeneralRenderer';
|
|||||||
export const SalesOrderRenderer = ({ pk }: { pk: string }) => {
|
export const SalesOrderRenderer = ({ pk }: { pk: string }) => {
|
||||||
return (
|
return (
|
||||||
<GeneralRenderer
|
<GeneralRenderer
|
||||||
api_key={ApiPaths.sales_order_detail}
|
api_key={ApiPaths.sales_order_list}
|
||||||
api_ref="sales_order"
|
api_ref="sales_order"
|
||||||
link={`/order/so/${pk}`}
|
link={`/order/so/${pk}`}
|
||||||
pk={pk}
|
pk={pk}
|
||||||
|
@ -17,7 +17,7 @@ export const StockItemRenderer = ({ pk }: { pk: string }) => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<GeneralRenderer
|
<GeneralRenderer
|
||||||
api_key={ApiPaths.stock_item_detail}
|
api_key={ApiPaths.stock_item_list}
|
||||||
api_ref="stockitem"
|
api_ref="stockitem"
|
||||||
link={`/stock/item/${pk}`}
|
link={`/stock/item/${pk}`}
|
||||||
pk={pk}
|
pk={pk}
|
||||||
|
@ -4,7 +4,7 @@ import { GeneralRenderer } from './GeneralRenderer';
|
|||||||
export const StockLocationRenderer = ({ pk }: { pk: string }) => {
|
export const StockLocationRenderer = ({ pk }: { pk: string }) => {
|
||||||
return (
|
return (
|
||||||
<GeneralRenderer
|
<GeneralRenderer
|
||||||
api_key={ApiPaths.stock_location_detail}
|
api_key={ApiPaths.stock_location_list}
|
||||||
api_ref="stock_location"
|
api_ref="stock_location"
|
||||||
link={`/stock/location/${pk}`}
|
link={`/stock/location/${pk}`}
|
||||||
pk={pk}
|
pk={pk}
|
||||||
|
@ -23,7 +23,7 @@ export const SupplierPartRenderer = ({ pk }: { pk: string }) => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<GeneralRenderer
|
<GeneralRenderer
|
||||||
api_key={ApiPaths.supplier_part_detail}
|
api_key={ApiPaths.supplier_part_list}
|
||||||
api_ref="supplier_part"
|
api_ref="supplier_part"
|
||||||
link={`/supplier-part/${pk}`}
|
link={`/supplier-part/${pk}`}
|
||||||
pk={pk}
|
pk={pk}
|
||||||
|
@ -2,7 +2,6 @@ import { t } from '@lingui/macro';
|
|||||||
import { Badge, Group, Stack, Text, Tooltip } from '@mantine/core';
|
import { Badge, Group, Stack, Text, Tooltip } from '@mantine/core';
|
||||||
import { ActionIcon } from '@mantine/core';
|
import { ActionIcon } from '@mantine/core';
|
||||||
import { Dropzone } from '@mantine/dropzone';
|
import { Dropzone } from '@mantine/dropzone';
|
||||||
import { useId } from '@mantine/hooks';
|
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
import { IconExternalLink, IconFileUpload } from '@tabler/icons-react';
|
import { IconExternalLink, IconFileUpload } from '@tabler/icons-react';
|
||||||
import { ReactNode, useEffect, useMemo, useState } from 'react';
|
import { ReactNode, useEffect, useMemo, useState } from 'react';
|
||||||
@ -14,6 +13,7 @@ import {
|
|||||||
editAttachment
|
editAttachment
|
||||||
} from '../../functions/forms/AttachmentForms';
|
} from '../../functions/forms/AttachmentForms';
|
||||||
import { useTableRefresh } from '../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths } 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';
|
||||||
@ -77,7 +77,7 @@ export function AttachmentTable({
|
|||||||
model,
|
model,
|
||||||
pk
|
pk
|
||||||
}: {
|
}: {
|
||||||
url: string;
|
url: ApiPaths;
|
||||||
pk: number;
|
pk: number;
|
||||||
model: string;
|
model: string;
|
||||||
}): ReactNode {
|
}): ReactNode {
|
||||||
|
@ -24,7 +24,6 @@ const defaultPageSize: number = 25;
|
|||||||
/**
|
/**
|
||||||
* Set of optional properties which can be passed to an InvenTreeTable component
|
* Set of optional properties which can be passed to an InvenTreeTable component
|
||||||
*
|
*
|
||||||
* @param url : string - The API endpoint to query
|
|
||||||
* @param params : any - Base query parameters
|
* @param params : any - Base query parameters
|
||||||
* @param tableKey : string - Unique key for the table (used for local storage)
|
* @param tableKey : string - Unique key for the table (used for local storage)
|
||||||
* @param refreshId : string - Unique ID for the table (used to trigger a refresh)
|
* @param refreshId : string - Unique ID for the table (used to trigger a refresh)
|
||||||
|
@ -4,7 +4,7 @@ import { useMemo } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ThumbnailHoverCard } from '../../items/Thumbnail';
|
import { ApiPaths, url } from '../../../states/ApiState';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
@ -142,7 +142,7 @@ export function BuildOrderTable({ params = {} }: { params?: any }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<InvenTreeTable
|
||||||
url="build/"
|
url={url(ApiPaths.build_order_list)}
|
||||||
tableKey={tableKey}
|
tableKey={tableKey}
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
props={{
|
props={{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { ApiPaths, url } 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';
|
||||||
@ -39,7 +40,7 @@ export function NotificationTable({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<InvenTreeTable
|
||||||
url="/notifications/"
|
url={url(ApiPaths.notifications_list)}
|
||||||
tableKey={tableKey}
|
tableKey={tableKey}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
props={{
|
props={{
|
||||||
|
@ -3,6 +3,7 @@ import { useMemo } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths, url } from '../../../states/ApiState';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ export function PartCategoryTable({ params = {} }: { params?: any }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<InvenTreeTable
|
||||||
url="part/category/"
|
url={url(ApiPaths.category_list)}
|
||||||
tableKey={tableKey}
|
tableKey={tableKey}
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
props={{
|
props={{
|
||||||
|
@ -7,7 +7,7 @@ import { editPart } from '../../../functions/forms/PartForms';
|
|||||||
import { notYetImplemented } from '../../../functions/notifications';
|
import { notYetImplemented } from '../../../functions/notifications';
|
||||||
import { shortenString } from '../../../functions/tables';
|
import { shortenString } from '../../../functions/tables';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ThumbnailHoverCard } from '../../items/Thumbnail';
|
import { ApiPaths, url } from '../../../states/ApiState';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
import { InvenTreeTable, InvenTreeTableProps } from '../InvenTreeTable';
|
import { InvenTreeTable, InvenTreeTableProps } from '../InvenTreeTable';
|
||||||
@ -221,7 +221,7 @@ export function PartListTable({ props }: { props: InvenTreeTableProps }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<InvenTreeTable
|
||||||
url="part/"
|
url={url(ApiPaths.part_list)}
|
||||||
tableKey={tableKey}
|
tableKey={tableKey}
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
props={{
|
props={{
|
||||||
|
@ -6,6 +6,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
|
|
||||||
import { openCreateApiForm, openDeleteApiForm } from '../../../functions/forms';
|
import { openCreateApiForm, openDeleteApiForm } from '../../../functions/forms';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths, url } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../items/Thumbnail';
|
import { Thumbnail } from '../../items/Thumbnail';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
@ -59,7 +60,7 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
|||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'add-related-part',
|
name: 'add-related-part',
|
||||||
title: t`Add Related Part`,
|
title: t`Add Related Part`,
|
||||||
url: '/part/related/',
|
url: ApiPaths.related_part_list,
|
||||||
fields: {
|
fields: {
|
||||||
part_1: {
|
part_1: {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
@ -99,7 +100,7 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
|||||||
onClick: () => {
|
onClick: () => {
|
||||||
openDeleteApiForm({
|
openDeleteApiForm({
|
||||||
name: 'delete-related-part',
|
name: 'delete-related-part',
|
||||||
url: '/part/related/',
|
url: ApiPaths.related_part_list,
|
||||||
pk: record.pk,
|
pk: record.pk,
|
||||||
title: t`Delete Related Part`,
|
title: t`Delete Related Part`,
|
||||||
successMessage: t`Related part deleted`,
|
successMessage: t`Related part deleted`,
|
||||||
@ -115,13 +116,13 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<InvenTreeTable
|
||||||
url="/part/related/"
|
url={url(ApiPaths.related_part_list)}
|
||||||
tableKey={tableKey}
|
tableKey={tableKey}
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
props={{
|
props={{
|
||||||
params: {
|
params: {
|
||||||
part: partId,
|
part: partId,
|
||||||
catefory_detail: true
|
category_detail: true
|
||||||
},
|
},
|
||||||
rowActions: rowActions,
|
rowActions: rowActions,
|
||||||
customActionGroups: customActions
|
customActionGroups: customActions
|
||||||
|
@ -5,6 +5,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
|
|
||||||
import { notYetImplemented } from '../../../functions/notifications';
|
import { notYetImplemented } from '../../../functions/notifications';
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths, url } from '../../../states/ApiState';
|
||||||
import { ThumbnailHoverCard } from '../../items/Thumbnail';
|
import { ThumbnailHoverCard } from '../../items/Thumbnail';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
@ -125,7 +126,7 @@ export function StockItemTable({ params = {} }: { params?: any }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<InvenTreeTable
|
||||||
url="stock/"
|
url={url(ApiPaths.stock_item_list)}
|
||||||
tableKey={tableKey}
|
tableKey={tableKey}
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
props={{
|
props={{
|
||||||
|
@ -3,6 +3,7 @@ import { useMemo } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths, url } from '../../../states/ApiState';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ export function StockLocationTable({ params = {} }: { params?: any }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InvenTreeTable
|
<InvenTreeTable
|
||||||
url="stock/location/"
|
url={url(ApiPaths.stock_location_list)}
|
||||||
tableKey={tableKey}
|
tableKey={tableKey}
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
props={{
|
props={{
|
||||||
|
@ -6,6 +6,7 @@ import { AxiosResponse } from 'axios';
|
|||||||
import { api } from '../App';
|
import { api } from '../App';
|
||||||
import { ApiForm, ApiFormProps } from '../components/forms/ApiForm';
|
import { ApiForm, ApiFormProps } from '../components/forms/ApiForm';
|
||||||
import { ApiFormFieldType } from '../components/forms/fields/ApiFormField';
|
import { ApiFormFieldType } from '../components/forms/fields/ApiFormField';
|
||||||
|
import { url } from '../states/ApiState';
|
||||||
import { invalidResponse, permissionDenied } from './notifications';
|
import { invalidResponse, permissionDenied } from './notifications';
|
||||||
import { generateUniqueId } from './uid';
|
import { generateUniqueId } from './uid';
|
||||||
|
|
||||||
@ -13,17 +14,7 @@ import { generateUniqueId } from './uid';
|
|||||||
* Construct an API url from the provided ApiFormProps object
|
* Construct an API url from the provided ApiFormProps object
|
||||||
*/
|
*/
|
||||||
export function constructFormUrl(props: ApiFormProps): string {
|
export function constructFormUrl(props: ApiFormProps): string {
|
||||||
let url = props.url;
|
return url(props.url, props.pk);
|
||||||
|
|
||||||
if (!url.endsWith('/')) {
|
|
||||||
url += '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.pk && props.pk > 0) {
|
|
||||||
url += `${props.pk}/`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,7 +67,7 @@ export function extractAvailableFields(
|
|||||||
fields[fieldName] = {
|
fields[fieldName] = {
|
||||||
...field,
|
...field,
|
||||||
name: fieldName,
|
name: fieldName,
|
||||||
fieldType: field.type,
|
field_type: field.type,
|
||||||
description: field.help_text,
|
description: field.help_text,
|
||||||
value: field.value ?? field.default
|
value: field.value ?? field.default
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ 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 '../../states/ApiState';
|
||||||
import {
|
import {
|
||||||
openCreateApiForm,
|
openCreateApiForm,
|
||||||
openDeleteApiForm,
|
openDeleteApiForm,
|
||||||
@ -31,7 +32,7 @@ export function addAttachment({
|
|||||||
attachmentType,
|
attachmentType,
|
||||||
callback
|
callback
|
||||||
}: {
|
}: {
|
||||||
url: string;
|
url: ApiPaths;
|
||||||
model: string;
|
model: string;
|
||||||
pk: number;
|
pk: number;
|
||||||
attachmentType: 'file' | 'link';
|
attachmentType: 'file' | 'link';
|
||||||
@ -77,7 +78,7 @@ export function editAttachment({
|
|||||||
attachmentType,
|
attachmentType,
|
||||||
callback
|
callback
|
||||||
}: {
|
}: {
|
||||||
url: string;
|
url: ApiPaths;
|
||||||
model: string;
|
model: string;
|
||||||
pk: number;
|
pk: number;
|
||||||
attachmentType: 'file' | 'link';
|
attachmentType: 'file' | 'link';
|
||||||
@ -116,7 +117,7 @@ export function deleteAttachment({
|
|||||||
pk,
|
pk,
|
||||||
callback
|
callback
|
||||||
}: {
|
}: {
|
||||||
url: string;
|
url: ApiPaths;
|
||||||
pk: number;
|
pk: number;
|
||||||
callback: () => void;
|
callback: () => void;
|
||||||
}) {
|
}) {
|
||||||
|
@ -4,6 +4,7 @@ import {
|
|||||||
ApiFormFieldSet,
|
ApiFormFieldSet,
|
||||||
ApiFormFieldType
|
ApiFormFieldType
|
||||||
} from '../../components/forms/fields/ApiFormField';
|
} from '../../components/forms/fields/ApiFormField';
|
||||||
|
import { ApiPaths } from '../../states/ApiState';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../forms';
|
import { openCreateApiForm, openEditApiForm } from '../forms';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +75,7 @@ export function createPart() {
|
|||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'part-create',
|
name: 'part-create',
|
||||||
title: t`Create Part`,
|
title: t`Create Part`,
|
||||||
url: '/part/',
|
url: ApiPaths.part_list,
|
||||||
successMessage: t`Part created`,
|
successMessage: t`Part created`,
|
||||||
fields: partFields({})
|
fields: partFields({})
|
||||||
});
|
});
|
||||||
@ -94,7 +95,7 @@ export function editPart({
|
|||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
name: 'part-edit',
|
name: 'part-edit',
|
||||||
title: t`Edit Part`,
|
title: t`Edit Part`,
|
||||||
url: '/part/',
|
url: ApiPaths.part_list,
|
||||||
pk: part_id,
|
pk: part_id,
|
||||||
successMessage: t`Part updated`,
|
successMessage: t`Part updated`,
|
||||||
fields: partFields({ editing: true }),
|
fields: partFields({ editing: true }),
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
ApiFormFieldSet,
|
ApiFormFieldSet,
|
||||||
ApiFormFieldType
|
ApiFormFieldType
|
||||||
} from '../../components/forms/fields/ApiFormField';
|
} from '../../components/forms/fields/ApiFormField';
|
||||||
|
import { ApiPaths } from '../../states/ApiState';
|
||||||
import { openCreateApiForm, openEditApiForm } from '../forms';
|
import { openCreateApiForm, openEditApiForm } from '../forms';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +55,7 @@ export function stockFields({}: {}): ApiFormFieldSet {
|
|||||||
},
|
},
|
||||||
serial_numbers: {
|
serial_numbers: {
|
||||||
// TODO: icon
|
// TODO: icon
|
||||||
fieldType: 'string',
|
field_type: 'string',
|
||||||
label: t`Serial Numbers`,
|
label: t`Serial Numbers`,
|
||||||
description: t`Enter serial numbers for new stock (or leave blank)`,
|
description: t`Enter serial numbers for new stock (or leave blank)`,
|
||||||
required: false
|
required: false
|
||||||
@ -99,7 +100,7 @@ export function stockFields({}: {}): ApiFormFieldSet {
|
|||||||
export function createStockItem() {
|
export function createStockItem() {
|
||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'stockitem-create',
|
name: 'stockitem-create',
|
||||||
url: '/stock/',
|
url: ApiPaths.stock_item_list,
|
||||||
fields: stockFields({}),
|
fields: stockFields({}),
|
||||||
title: t`Create Stock Item`
|
title: t`Create Stock Item`
|
||||||
});
|
});
|
||||||
@ -112,7 +113,7 @@ export function createStockItem() {
|
|||||||
export function editStockItem(item: number) {
|
export function editStockItem(item: number) {
|
||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
name: 'stockitem-edit',
|
name: 'stockitem-edit',
|
||||||
url: '/stock/',
|
url: ApiPaths.stock_item_list,
|
||||||
pk: item,
|
pk: item,
|
||||||
fields: stockFields({}),
|
fields: stockFields({}),
|
||||||
title: t`Edit Stock Item`
|
title: t`Edit Stock Item`
|
||||||
|
@ -2,6 +2,7 @@ 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, url } 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
|
||||||
@ -12,15 +13,19 @@ import { api } from '../App';
|
|||||||
* To use this hook:
|
* To use this hook:
|
||||||
* const { instance, refreshInstance } = useInstance(url: string, pk: number)
|
* const { instance, refreshInstance } = useInstance(url: string, pk: number)
|
||||||
*/
|
*/
|
||||||
export function useInstance(
|
export function useInstance({
|
||||||
url: string,
|
endpoint,
|
||||||
pk: string | undefined,
|
pk,
|
||||||
params: any = {}
|
params = {}
|
||||||
) {
|
}: {
|
||||||
|
endpoint: ApiPaths;
|
||||||
|
pk: string | undefined;
|
||||||
|
params?: any;
|
||||||
|
}) {
|
||||||
const [instance, setInstance] = useState<any>({});
|
const [instance, setInstance] = useState<any>({});
|
||||||
|
|
||||||
const instanceQuery = useQuery({
|
const instanceQuery = useQuery({
|
||||||
queryKey: ['instance', url, pk, params],
|
queryKey: ['instance', endpoint, pk, params],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (pk == null || pk == undefined || pk.length == 0) {
|
if (pk == null || pk == undefined || pk.length == 0) {
|
||||||
setInstance({});
|
setInstance({});
|
||||||
@ -28,7 +33,7 @@ export function useInstance(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return api
|
return api
|
||||||
.get(url + pk + '/', {
|
.get(url(endpoint, pk), {
|
||||||
params: params
|
params: params
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
partCategoryFields
|
partCategoryFields
|
||||||
} from '../../functions/forms/PartForms';
|
} from '../../functions/forms/PartForms';
|
||||||
import { createStockItem } from '../../functions/forms/StockForms';
|
import { createStockItem } from '../../functions/forms/StockForms';
|
||||||
|
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() {
|
||||||
@ -22,7 +23,7 @@ function ApiFormsPlayground() {
|
|||||||
|
|
||||||
const editCategoryForm: ApiFormProps = {
|
const editCategoryForm: ApiFormProps = {
|
||||||
name: 'partcategory',
|
name: 'partcategory',
|
||||||
url: '/part/category/',
|
url: ApiPaths.category_list,
|
||||||
pk: 2,
|
pk: 2,
|
||||||
title: 'Edit Category',
|
title: 'Edit Category',
|
||||||
fields: fields
|
fields: fields
|
||||||
@ -30,7 +31,7 @@ function ApiFormsPlayground() {
|
|||||||
|
|
||||||
const createAttachmentForm: ApiFormProps = {
|
const createAttachmentForm: ApiFormProps = {
|
||||||
name: 'createattachment',
|
name: 'createattachment',
|
||||||
url: '/part/attachment/',
|
url: ApiPaths.part_attachment_list,
|
||||||
title: 'Create Attachment',
|
title: 'Create Attachment',
|
||||||
successMessage: 'Attachment uploaded',
|
successMessage: 'Attachment uploaded',
|
||||||
fields: {
|
fields: {
|
||||||
|
@ -6,16 +6,13 @@ import {
|
|||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
IconList,
|
IconList,
|
||||||
IconListCheck,
|
IconListCheck,
|
||||||
IconListTree,
|
|
||||||
IconNotes,
|
IconNotes,
|
||||||
IconPaperclip,
|
IconPaperclip,
|
||||||
IconSitemap
|
IconSitemap
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useMemo } from 'react';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { api } from '../../App';
|
|
||||||
import {
|
import {
|
||||||
PlaceholderPanel,
|
PlaceholderPanel,
|
||||||
PlaceholderPill
|
PlaceholderPill
|
||||||
@ -27,6 +24,7 @@ import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
|
|||||||
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 { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
|
import { ApiPaths, url } from '../../states/ApiState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detail page for a single Build Order
|
* Detail page for a single Build Order
|
||||||
@ -38,8 +36,12 @@ export default function BuildDetail() {
|
|||||||
instance: build,
|
instance: build,
|
||||||
refreshInstance,
|
refreshInstance,
|
||||||
instanceQuery
|
instanceQuery
|
||||||
} = useInstance('/build/', id, {
|
} = useInstance({
|
||||||
part_detail: true
|
endpoint: ApiPaths.build_order_list,
|
||||||
|
pk: id,
|
||||||
|
params: {
|
||||||
|
part_detail: true
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const buildPanels: PanelType[] = useMemo(() => {
|
const buildPanels: PanelType[] = useMemo(() => {
|
||||||
@ -107,7 +109,7 @@ export default function BuildDetail() {
|
|||||||
icon: <IconPaperclip size="18" />,
|
icon: <IconPaperclip size="18" />,
|
||||||
content: (
|
content: (
|
||||||
<AttachmentTable
|
<AttachmentTable
|
||||||
url="/build/attachment/"
|
url={ApiPaths.build_order_attachment_list}
|
||||||
model="build"
|
model="build"
|
||||||
pk={build.pk ?? -1}
|
pk={build.pk ?? -1}
|
||||||
/>
|
/>
|
||||||
@ -119,7 +121,7 @@ export default function BuildDetail() {
|
|||||||
icon: <IconNotes size="18" />,
|
icon: <IconNotes size="18" />,
|
||||||
content: (
|
content: (
|
||||||
<NotesEditor
|
<NotesEditor
|
||||||
url={`/build/${build.pk}/`}
|
url={url(ApiPaths.build_order_list, build.pk)}
|
||||||
data={build.notes ?? ''}
|
data={build.notes ?? ''}
|
||||||
allowEdit={true}
|
allowEdit={true}
|
||||||
/>
|
/>
|
||||||
|
@ -16,6 +16,7 @@ import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
|||||||
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 { 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.
|
||||||
@ -29,7 +30,13 @@ export default function CategoryDetail({}: {}) {
|
|||||||
instance: category,
|
instance: category,
|
||||||
refreshInstance,
|
refreshInstance,
|
||||||
instanceQuery
|
instanceQuery
|
||||||
} = useInstance('/part/category/', id, { path_detail: true });
|
} = useInstance({
|
||||||
|
endpoint: ApiPaths.category_list,
|
||||||
|
pk: id,
|
||||||
|
params: {
|
||||||
|
path_detail: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const categoryPanels: PanelType[] = useMemo(
|
const categoryPanels: PanelType[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
@ -29,6 +29,7 @@ import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
|||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { editPart } from '../../functions/forms/PartForms';
|
import { editPart } from '../../functions/forms/PartForms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
|
import { ApiPaths, url } from '../../states/ApiState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detail view for a single Part instance
|
* Detail view for a single Part instance
|
||||||
@ -40,7 +41,13 @@ export default function PartDetail() {
|
|||||||
instance: part,
|
instance: part,
|
||||||
refreshInstance,
|
refreshInstance,
|
||||||
instanceQuery
|
instanceQuery
|
||||||
} = useInstance('/part/', id, { path_detail: true });
|
} = useInstance({
|
||||||
|
endpoint: ApiPaths.part_list,
|
||||||
|
pk: id,
|
||||||
|
params: {
|
||||||
|
path_detail: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Part data panels (recalculate when part data changes)
|
// Part data panels (recalculate when part data changes)
|
||||||
const partPanels: PanelType[] = useMemo(() => {
|
const partPanels: PanelType[] = useMemo(() => {
|
||||||
@ -123,7 +130,7 @@ export default function PartDetail() {
|
|||||||
name: 'related_parts',
|
name: 'related_parts',
|
||||||
label: t`Related Parts`,
|
label: t`Related Parts`,
|
||||||
icon: <IconLayersLinked size="18" />,
|
icon: <IconLayersLinked size="18" />,
|
||||||
content: partRelatedTab()
|
content: <RelatedPartTable partId={part.pk ?? -1} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'attachments',
|
name: 'attachments',
|
||||||
@ -131,7 +138,7 @@ export default function PartDetail() {
|
|||||||
icon: <IconPaperclip size="18" />,
|
icon: <IconPaperclip size="18" />,
|
||||||
content: (
|
content: (
|
||||||
<AttachmentTable
|
<AttachmentTable
|
||||||
url="/part/attachment/"
|
url={ApiPaths.part_attachment_list}
|
||||||
model="part"
|
model="part"
|
||||||
pk={part.pk ?? -1}
|
pk={part.pk ?? -1}
|
||||||
/>
|
/>
|
||||||
@ -146,14 +153,11 @@ export default function PartDetail() {
|
|||||||
];
|
];
|
||||||
}, [part]);
|
}, [part]);
|
||||||
|
|
||||||
function partRelatedTab(): React.ReactNode {
|
|
||||||
return <RelatedPartTable partId={part.pk ?? -1} />;
|
|
||||||
}
|
|
||||||
function partNotesTab(): React.ReactNode {
|
function partNotesTab(): React.ReactNode {
|
||||||
// TODO: Set edit permission based on user permissions
|
// TODO: Set edit permission based on user permissions
|
||||||
return (
|
return (
|
||||||
<NotesEditor
|
<NotesEditor
|
||||||
url={`/part/${part.pk}/`}
|
url={url(ApiPaths.part_list, part.pk)}
|
||||||
data={part.notes ?? ''}
|
data={part.notes ?? ''}
|
||||||
allowEdit={true}
|
allowEdit={true}
|
||||||
/>
|
/>
|
||||||
|
@ -9,6 +9,7 @@ import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
|||||||
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 { 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();
|
||||||
@ -17,7 +18,13 @@ export default function Stock() {
|
|||||||
instance: location,
|
instance: location,
|
||||||
refreshInstance,
|
refreshInstance,
|
||||||
instanceQuery
|
instanceQuery
|
||||||
} = useInstance('/stock/location/', id, { path_detail: true });
|
} = useInstance({
|
||||||
|
endpoint: ApiPaths.stock_location_list,
|
||||||
|
pk: id,
|
||||||
|
params: {
|
||||||
|
path_detail: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const locationPanels: PanelType[] = useMemo(() => {
|
const locationPanels: PanelType[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
@ -7,10 +7,9 @@ import {
|
|||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
IconNotes,
|
IconNotes,
|
||||||
IconPaperclip,
|
IconPaperclip,
|
||||||
IconSitemap,
|
IconSitemap
|
||||||
IconTransferIn
|
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
||||||
@ -19,6 +18,7 @@ import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
|||||||
import { AttachmentTable } from '../../components/tables/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/AttachmentTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
|
import { ApiPaths, url } from '../../states/ApiState';
|
||||||
|
|
||||||
export default function StockDetail() {
|
export default function StockDetail() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
@ -27,10 +27,14 @@ export default function StockDetail() {
|
|||||||
instance: stockitem,
|
instance: stockitem,
|
||||||
refreshInstance,
|
refreshInstance,
|
||||||
instanceQuery
|
instanceQuery
|
||||||
} = useInstance('/stock/', id, {
|
} = useInstance({
|
||||||
part_detail: true,
|
endpoint: ApiPaths.stock_item_list,
|
||||||
location_detail: true,
|
pk: id,
|
||||||
path_detail: true
|
params: {
|
||||||
|
part_detail: true,
|
||||||
|
location_detail: true,
|
||||||
|
path_detail: true
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const stockPanels: PanelType[] = useMemo(() => {
|
const stockPanels: PanelType[] = useMemo(() => {
|
||||||
@ -71,7 +75,7 @@ export default function StockDetail() {
|
|||||||
icon: <IconPaperclip size="18" />,
|
icon: <IconPaperclip size="18" />,
|
||||||
content: (
|
content: (
|
||||||
<AttachmentTable
|
<AttachmentTable
|
||||||
url="/stock/attachment/"
|
url={ApiPaths.stock_attachment_list}
|
||||||
model="stock_item"
|
model="stock_item"
|
||||||
pk={stockitem.pk ?? -1}
|
pk={stockitem.pk ?? -1}
|
||||||
/>
|
/>
|
||||||
@ -83,7 +87,7 @@ export default function StockDetail() {
|
|||||||
icon: <IconNotes size="18" />,
|
icon: <IconNotes size="18" />,
|
||||||
content: (
|
content: (
|
||||||
<NotesEditor
|
<NotesEditor
|
||||||
url={`/stock/${stockitem.pk}/`}
|
url={url(ApiPaths.stock_item_list, stockitem.pk)}
|
||||||
data={stockitem.notes ?? ''}
|
data={stockitem.notes ?? ''}
|
||||||
allowEdit={true}
|
allowEdit={true}
|
||||||
/>
|
/>
|
||||||
|
@ -47,6 +47,7 @@ export const useServerApiState = create<ServerApiStateProps>((set, get) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export enum ApiPaths {
|
export enum ApiPaths {
|
||||||
|
// User information
|
||||||
user_me = 'api-user-me',
|
user_me = 'api-user-me',
|
||||||
user_roles = 'api-user-roles',
|
user_roles = 'api-user-roles',
|
||||||
user_token = 'api-user-token',
|
user_token = 'api-user-token',
|
||||||
@ -54,17 +55,40 @@ export enum ApiPaths {
|
|||||||
user_reset = 'api-user-reset',
|
user_reset = 'api-user-reset',
|
||||||
user_reset_set = 'api-user-reset-set',
|
user_reset_set = 'api-user-reset-set',
|
||||||
|
|
||||||
|
notifications_list = 'api-notifications-list',
|
||||||
|
|
||||||
barcode = 'api-barcode',
|
barcode = 'api-barcode',
|
||||||
part_detail = 'api-part-detail',
|
|
||||||
supplier_part_detail = 'api-supplier-part-detail',
|
// Build order URLs
|
||||||
stock_item_detail = 'api-stock-item-detail',
|
build_order_list = 'api-build-list',
|
||||||
stock_location_detail = 'api-stock-location-detail',
|
build_order_attachment_list = 'api-build-attachment-list',
|
||||||
purchase_order_detail = 'api-purchase-order-detail',
|
|
||||||
sales_order_detail = 'api-sales-order-detail',
|
// Part URLs
|
||||||
build_order_detail = 'api-build-order-detail'
|
part_list = 'api-part-list',
|
||||||
|
category_list = 'api-category-list',
|
||||||
|
related_part_list = 'api-related-part-list',
|
||||||
|
part_attachment_list = 'api-part-attachment-list',
|
||||||
|
|
||||||
|
// Company URLs
|
||||||
|
company_list = 'api-company-list',
|
||||||
|
supplier_part_list = 'api-supplier-part-list',
|
||||||
|
|
||||||
|
// Stock Item URLs
|
||||||
|
stock_item_list = 'api-stock-item-list',
|
||||||
|
stock_location_list = 'api-stock-location-list',
|
||||||
|
stock_attachment_list = 'api-stock-attachment-list',
|
||||||
|
|
||||||
|
// Purchase Order URLs
|
||||||
|
purchase_order_list = 'api-purchase-order-list',
|
||||||
|
|
||||||
|
// Sales Order URLs
|
||||||
|
sales_order_list = 'api-sales-order-list'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function url(path: ApiPaths, pk?: any): string {
|
/**
|
||||||
|
* Return the endpoint associated with a given API path
|
||||||
|
*/
|
||||||
|
export function endpoint(path: ApiPaths): string {
|
||||||
switch (path) {
|
switch (path) {
|
||||||
case ApiPaths.user_me:
|
case ApiPaths.user_me:
|
||||||
return 'user/me/';
|
return 'user/me/';
|
||||||
@ -78,25 +102,51 @@ export function url(path: ApiPaths, pk?: any): string {
|
|||||||
return '/auth/password/reset/';
|
return '/auth/password/reset/';
|
||||||
case ApiPaths.user_reset_set:
|
case ApiPaths.user_reset_set:
|
||||||
return '/auth/password/reset/confirm/';
|
return '/auth/password/reset/confirm/';
|
||||||
|
case ApiPaths.notifications_list:
|
||||||
|
return 'notifications/';
|
||||||
case ApiPaths.barcode:
|
case ApiPaths.barcode:
|
||||||
return 'barcode/';
|
return 'barcode/';
|
||||||
case ApiPaths.part_detail:
|
case ApiPaths.build_order_list:
|
||||||
return `part/${pk}/`;
|
return 'build/';
|
||||||
case ApiPaths.supplier_part_detail:
|
case ApiPaths.build_order_attachment_list:
|
||||||
return `company/part/${pk}/`;
|
return 'build/attachment/';
|
||||||
case ApiPaths.stock_item_detail:
|
case ApiPaths.part_list:
|
||||||
return `stock/${pk}/`;
|
return 'part/';
|
||||||
case ApiPaths.stock_location_detail:
|
case ApiPaths.category_list:
|
||||||
return `stock/location/${pk}/`;
|
return 'part/category/';
|
||||||
case ApiPaths.purchase_order_detail:
|
case ApiPaths.related_part_list:
|
||||||
return `order/po/${pk}/`;
|
return 'part/related/';
|
||||||
case ApiPaths.sales_order_detail:
|
case ApiPaths.part_attachment_list:
|
||||||
return `order/so/${pk}/`;
|
return 'part/attachment/';
|
||||||
case ApiPaths.build_order_detail:
|
case ApiPaths.company_list:
|
||||||
return `build/${pk}/`;
|
return 'company/';
|
||||||
|
case ApiPaths.supplier_part_list:
|
||||||
|
return 'company/part/';
|
||||||
|
case ApiPaths.stock_item_list:
|
||||||
|
return 'stock/';
|
||||||
|
case ApiPaths.stock_location_list:
|
||||||
|
return 'stock/location/';
|
||||||
|
case ApiPaths.stock_attachment_list:
|
||||||
|
return 'stock/attachment/';
|
||||||
|
case ApiPaths.purchase_order_list:
|
||||||
|
return 'order/po/';
|
||||||
|
case ApiPaths.sales_order_list:
|
||||||
|
return 'order/so/';
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an API URL with an endpoint and (optional) pk value
|
||||||
|
*/
|
||||||
|
export function url(path: ApiPaths, pk?: any): string {
|
||||||
|
let _url = endpoint(path);
|
||||||
|
|
||||||
|
if (_url && pk) {
|
||||||
|
_url += `${pk}/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _url;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user