mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 20:16:44 +00:00
[React] Purchasing and Sales (#5743)
* Skeleton layout for purchasing and sales - Empty pages - Nav links - Tab panels * Add <CompanyTable> component - Customers - Suppliers - Manufacturers * Table fxies * Initial implementation of PurchaseOrderTable * More tables - Basic SalesOrderTable - Basic ReturnOrderTable * Improvements to InvenTreeTable component - Handle incorrect return type from server - Use state for recordCount * Formatting fixes
This commit is contained in:
parent
543d50149b
commit
566fef5309
@ -347,7 +347,18 @@ export function InvenTreeTable({
|
|||||||
setMissingRecordsText(
|
setMissingRecordsText(
|
||||||
tableProps.noRecordsText ?? t`No records found`
|
tableProps.noRecordsText ?? t`No records found`
|
||||||
);
|
);
|
||||||
return response.data;
|
|
||||||
|
// Extract returned data (accounting for pagination) and ensure it is a list
|
||||||
|
let results = response.data?.results ?? response.data ?? [];
|
||||||
|
|
||||||
|
if (!Array.isArray(results)) {
|
||||||
|
setMissingRecordsText(t`Server returned incorrect data type`);
|
||||||
|
results = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
setRecordCount(response.data?.count ?? results.length);
|
||||||
|
|
||||||
|
return results;
|
||||||
case 400:
|
case 400:
|
||||||
setMissingRecordsText(t`Bad request`);
|
setMissingRecordsText(t`Bad request`);
|
||||||
break;
|
break;
|
||||||
@ -389,6 +400,8 @@ export function InvenTreeTable({
|
|||||||
refetchOnMount: true
|
refetchOnMount: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [recordCount, setRecordCount] = useState<number>(0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reload the table whenever the refetch changes
|
* Reload the table whenever the refetch changes
|
||||||
* this allows us to programmatically refresh the table
|
* this allows us to programmatically refresh the table
|
||||||
@ -487,7 +500,7 @@ export function InvenTreeTable({
|
|||||||
loaderVariant="dots"
|
loaderVariant="dots"
|
||||||
idAccessor={tableProps.idAccessor}
|
idAccessor={tableProps.idAccessor}
|
||||||
minHeight={200}
|
minHeight={200}
|
||||||
totalRecords={data?.count ?? data?.length ?? 0}
|
totalRecords={recordCount}
|
||||||
recordsPerPage={tableProps.pageSize ?? defaultPageSize}
|
recordsPerPage={tableProps.pageSize ?? defaultPageSize}
|
||||||
page={page}
|
page={page}
|
||||||
onPageChange={setPage}
|
onPageChange={setPage}
|
||||||
@ -501,7 +514,7 @@ export function InvenTreeTable({
|
|||||||
}
|
}
|
||||||
fetching={isFetching}
|
fetching={isFetching}
|
||||||
noRecordsText={missingRecordsText}
|
noRecordsText={missingRecordsText}
|
||||||
records={data?.results ?? data ?? []}
|
records={data}
|
||||||
columns={dataColumns}
|
columns={dataColumns}
|
||||||
onRowClick={tableProps.onRowClick}
|
onRowClick={tableProps.onRowClick}
|
||||||
/>
|
/>
|
||||||
|
@ -6,18 +6,18 @@ 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';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../../App';
|
||||||
import {
|
import {
|
||||||
addAttachment,
|
addAttachment,
|
||||||
deleteAttachment,
|
deleteAttachment,
|
||||||
editAttachment
|
editAttachment
|
||||||
} from '../../functions/forms/AttachmentForms';
|
} from '../../../functions/forms/AttachmentForms';
|
||||||
import { useTableRefresh } from '../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths, 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';
|
||||||
import { RowAction } from './RowActions';
|
import { RowAction } from '../RowActions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define set of columns to display for the attachment table
|
* Define set of columns to display for the attachment table
|
63
src/frontend/src/components/tables/general/CompanyTable.tsx
Normal file
63
src/frontend/src/components/tables/general/CompanyTable.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Group, Text } from '@mantine/core';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A table which displays a list of company records,
|
||||||
|
* based on the provided filter parameters
|
||||||
|
*/
|
||||||
|
export function CompanyTable({ params }: { params?: any }) {
|
||||||
|
const { tableKey } = useTableRefresh('company');
|
||||||
|
|
||||||
|
const columns = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessor: 'name',
|
||||||
|
title: t`Company Name`,
|
||||||
|
sortable: true,
|
||||||
|
render: (record: any) => {
|
||||||
|
return (
|
||||||
|
<Group spacing="xs" noWrap={true}>
|
||||||
|
<Thumbnail
|
||||||
|
src={record.thumbnail ?? record.image}
|
||||||
|
alt={record.name}
|
||||||
|
size={24}
|
||||||
|
/>
|
||||||
|
<Text>{record.name}</Text>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'description',
|
||||||
|
title: t`Description`,
|
||||||
|
sortable: false,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'website',
|
||||||
|
title: t`Website`,
|
||||||
|
sortable: false,
|
||||||
|
switchable: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InvenTreeTable
|
||||||
|
url={apiUrl(ApiPaths.company_list)}
|
||||||
|
tableKey={tableKey}
|
||||||
|
columns={columns}
|
||||||
|
props={{
|
||||||
|
params: {
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Group, Text } from '@mantine/core';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
|
export function PurchaseOrderTable({ params }: { params?: any }) {
|
||||||
|
const { tableKey } = useTableRefresh('purchase-order');
|
||||||
|
|
||||||
|
// TODO: Custom filters
|
||||||
|
|
||||||
|
// TODO: Row actions
|
||||||
|
|
||||||
|
// TODO: Table actions (e.g. create new purchase order)
|
||||||
|
|
||||||
|
const tableColumns = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessor: 'reference',
|
||||||
|
title: t`Reference`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'description',
|
||||||
|
title: t`Description`,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'supplier__name',
|
||||||
|
title: t`Supplier`,
|
||||||
|
sortable: true,
|
||||||
|
render: function (record: any) {
|
||||||
|
let supplier = record.supplier_detail ?? {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group spacing="xs" noWrap={true}>
|
||||||
|
<Thumbnail src={supplier?.image} alt={supplier.name} />
|
||||||
|
<Text>{supplier?.name}</Text>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'supplier_reference',
|
||||||
|
title: t`Supplier Reference`,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'project_code',
|
||||||
|
title: t`Project Code`,
|
||||||
|
switchable: true
|
||||||
|
// TODO: Custom formatter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'status',
|
||||||
|
title: t`Status`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true
|
||||||
|
// TODO: Custom formatter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'creation_date',
|
||||||
|
title: t`Created`,
|
||||||
|
switchable: true
|
||||||
|
// TODO: Custom formatter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'target_date',
|
||||||
|
title: t`Target Date`,
|
||||||
|
switchable: true
|
||||||
|
// TODO: Custom formatter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'line_items',
|
||||||
|
title: t`Line Items`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true
|
||||||
|
}
|
||||||
|
// TODO: total_price
|
||||||
|
// TODO: responsible
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InvenTreeTable
|
||||||
|
url={apiUrl(ApiPaths.purchase_order_list)}
|
||||||
|
tableKey={tableKey}
|
||||||
|
columns={tableColumns}
|
||||||
|
props={{
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
supplier_detail: true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Group, Text } from '@mantine/core';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
|
export function ReturnOrderTable({ params }: { params?: any }) {
|
||||||
|
const { tableKey } = useTableRefresh('return-orders');
|
||||||
|
|
||||||
|
// TODO: Custom filters
|
||||||
|
|
||||||
|
// TODO: Row actions
|
||||||
|
|
||||||
|
// TODO: Table actions (e.g. create new return order)
|
||||||
|
|
||||||
|
const tableColumns = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessor: 'reference',
|
||||||
|
title: t`Return Order`,
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'description',
|
||||||
|
title: t`Description`,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'customer__name',
|
||||||
|
title: t`Customer`,
|
||||||
|
sortable: true,
|
||||||
|
render: function (record: any) {
|
||||||
|
let customer = record.customer_detail ?? {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group spacing="xs" noWrap={true}>
|
||||||
|
<Thumbnail src={customer?.image} alt={customer.name} />
|
||||||
|
<Text>{customer?.name}</Text>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'customer_reference',
|
||||||
|
title: t`Customer Reference`,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'project_code',
|
||||||
|
title: t`Project Code`,
|
||||||
|
switchable: true
|
||||||
|
// TODO: Custom formatter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'status',
|
||||||
|
title: t`Status`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true
|
||||||
|
// TODO: Custom formatter
|
||||||
|
}
|
||||||
|
// TODO: Creation date
|
||||||
|
// TODO: Target date
|
||||||
|
// TODO: Line items
|
||||||
|
// TODO: Responsible
|
||||||
|
// TODO: Total cost
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InvenTreeTable
|
||||||
|
url={apiUrl(ApiPaths.return_order_list)}
|
||||||
|
tableKey={tableKey}
|
||||||
|
columns={tableColumns}
|
||||||
|
props={{
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
customer_detail: true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
87
src/frontend/src/components/tables/sales/SalesOrderTable.tsx
Normal file
87
src/frontend/src/components/tables/sales/SalesOrderTable.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Group, Text } from '@mantine/core';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
|
export function SalesOrderTable({ params }: { params?: any }) {
|
||||||
|
const { tableKey } = useTableRefresh('sales-order');
|
||||||
|
|
||||||
|
// TODO: Custom filters
|
||||||
|
|
||||||
|
// TODO: Row actions
|
||||||
|
|
||||||
|
// TODO: Table actions (e.g. create new sales order)
|
||||||
|
|
||||||
|
const tableColumns = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessor: 'reference',
|
||||||
|
title: t`Sales Order`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'description',
|
||||||
|
title: t`Description`,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'customer__name',
|
||||||
|
title: t`Customer`,
|
||||||
|
sortable: true,
|
||||||
|
render: function (record: any) {
|
||||||
|
let customer = record.customer_detail ?? {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group spacing="xs" noWrap={true}>
|
||||||
|
<Thumbnail src={customer?.image} alt={customer.name} />
|
||||||
|
<Text>{customer?.name}</Text>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'customer_reference',
|
||||||
|
title: t`Customer Reference`,
|
||||||
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'project_code',
|
||||||
|
title: t`Project Code`,
|
||||||
|
switchable: true
|
||||||
|
// TODO: Custom formatter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'status',
|
||||||
|
title: t`Status`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true
|
||||||
|
// TODO: Custom formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Creation date
|
||||||
|
// TODO: Target date
|
||||||
|
// TODO: Shipment date
|
||||||
|
// TODO: Line items
|
||||||
|
// TODO: Total price
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InvenTreeTable
|
||||||
|
url={apiUrl(ApiPaths.sales_order_list)}
|
||||||
|
tableKey={tableKey}
|
||||||
|
columns={tableColumns}
|
||||||
|
props={{
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
customer_detail: true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { ActionIcon, Text, Tooltip } from '@mantine/core';
|
import { ActionIcon, Text, Tooltip } from '@mantine/core';
|
||||||
import { showNotification } from '@mantine/notifications';
|
|
||||||
import { IconCirclePlus } from '@tabler/icons-react';
|
import { IconCirclePlus } from '@tabler/icons-react';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
@ -9,7 +8,6 @@ import {
|
|||||||
openDeleteApiForm,
|
openDeleteApiForm,
|
||||||
openEditApiForm
|
openEditApiForm
|
||||||
} from '../../../functions/forms';
|
} from '../../../functions/forms';
|
||||||
import { notYetImplemented } from '../../../functions/notifications';
|
|
||||||
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
import { useTableRefresh } from '../../../hooks/TableRefresh';
|
||||||
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../../states/ApiState';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
|
@ -25,7 +25,9 @@ export const navTabs = [
|
|||||||
{ text: <Trans>Dashboard</Trans>, name: 'dashboard' },
|
{ text: <Trans>Dashboard</Trans>, name: 'dashboard' },
|
||||||
{ text: <Trans>Parts</Trans>, name: 'part' },
|
{ text: <Trans>Parts</Trans>, name: 'part' },
|
||||||
{ text: <Trans>Stock</Trans>, name: 'stock' },
|
{ text: <Trans>Stock</Trans>, name: 'stock' },
|
||||||
{ text: <Trans>Build</Trans>, name: 'build' }
|
{ text: <Trans>Build</Trans>, name: 'build' },
|
||||||
|
{ text: <Trans>Purchasing</Trans>, name: 'purchasing' },
|
||||||
|
{ text: <Trans>Sales</Trans>, name: 'sales' }
|
||||||
];
|
];
|
||||||
if (IS_DEV_OR_DEMO) {
|
if (IS_DEV_OR_DEMO) {
|
||||||
navTabs.push({ text: <Trans>Playground</Trans>, name: 'playground' });
|
navTabs.push({ text: <Trans>Playground</Trans>, name: 'playground' });
|
||||||
|
@ -19,8 +19,8 @@ import {
|
|||||||
} from '../../components/items/Placeholder';
|
} from '../../components/items/Placeholder';
|
||||||
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 { AttachmentTable } from '../../components/tables/AttachmentTable';
|
|
||||||
import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
|
import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
|
||||||
|
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 { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
|
@ -31,7 +31,7 @@ import { ApiImage } from '../../components/images/ApiImage';
|
|||||||
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
||||||
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 { AttachmentTable } from '../../components/tables/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
||||||
import { PartParameterTable } from '../../components/tables/part/PartParameterTable';
|
import { PartParameterTable } from '../../components/tables/part/PartParameterTable';
|
||||||
import { PartVariantTable } from '../../components/tables/part/PartVariantTable';
|
import { PartVariantTable } from '../../components/tables/part/PartVariantTable';
|
||||||
import { RelatedPartTable } from '../../components/tables/part/RelatedPartTable';
|
import { RelatedPartTable } from '../../components/tables/part/RelatedPartTable';
|
||||||
|
48
src/frontend/src/pages/purchasing/PurchasingIndex.tsx
Normal file
48
src/frontend/src/pages/purchasing/PurchasingIndex.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Stack } from '@mantine/core';
|
||||||
|
import {
|
||||||
|
IconBuildingFactory2,
|
||||||
|
IconBuildingStore,
|
||||||
|
IconShoppingCart
|
||||||
|
} from '@tabler/icons-react';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
|
import { PanelGroup } from '../../components/nav/PanelGroup';
|
||||||
|
import { CompanyTable } from '../../components/tables/general/CompanyTable';
|
||||||
|
import { PurchaseOrderTable } from '../../components/tables/purchasing/PurchaseOrderTable';
|
||||||
|
|
||||||
|
export default function PurchasingIndex() {
|
||||||
|
const panels = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'purchaseorders',
|
||||||
|
label: t`Purchase Orders`,
|
||||||
|
icon: <IconShoppingCart />,
|
||||||
|
content: <PurchaseOrderTable />
|
||||||
|
// TODO: Add optional "calendar" display here...
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'suppliers',
|
||||||
|
label: t`Suppliers`,
|
||||||
|
icon: <IconBuildingStore />,
|
||||||
|
content: <CompanyTable params={{ is_supplier: true }} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'manufacturer',
|
||||||
|
label: t`Manufacturers`,
|
||||||
|
icon: <IconBuildingFactory2 />,
|
||||||
|
content: <CompanyTable params={{ is_manufacturer: true }} />
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack>
|
||||||
|
<PageDetail title={t`Purchasing`} />
|
||||||
|
<PanelGroup pageKey="purchasing-index" panels={panels} />
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
48
src/frontend/src/pages/sales/SalesIndex.tsx
Normal file
48
src/frontend/src/pages/sales/SalesIndex.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Stack } from '@mantine/core';
|
||||||
|
import {
|
||||||
|
IconBuildingStore,
|
||||||
|
IconTruckDelivery,
|
||||||
|
IconTruckReturn
|
||||||
|
} from '@tabler/icons-react';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
|
import { PanelGroup } from '../../components/nav/PanelGroup';
|
||||||
|
import { CompanyTable } from '../../components/tables/general/CompanyTable';
|
||||||
|
import { ReturnOrderTable } from '../../components/tables/sales/ReturnOrderTable';
|
||||||
|
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
||||||
|
|
||||||
|
export default function PurchasingIndex() {
|
||||||
|
const panels = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'salesorders',
|
||||||
|
label: t`Sales Orders`,
|
||||||
|
icon: <IconTruckDelivery />,
|
||||||
|
content: <SalesOrderTable />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'returnorders',
|
||||||
|
label: t`Return Orders`,
|
||||||
|
icon: <IconTruckReturn />,
|
||||||
|
content: <ReturnOrderTable />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'suppliers',
|
||||||
|
label: t`Customers`,
|
||||||
|
icon: <IconBuildingStore />,
|
||||||
|
content: <CompanyTable params={{ is_customer: true }} />
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack>
|
||||||
|
<PageDetail title={t`Sales`} />
|
||||||
|
<PanelGroup pageKey="sales-index" panels={panels} />
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -15,7 +15,7 @@ import { useParams } from 'react-router-dom';
|
|||||||
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
||||||
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 { AttachmentTable } from '../../components/tables/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/general/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, apiUrl } from '../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
||||||
|
@ -34,6 +34,14 @@ export const BuildDetail = Loadable(
|
|||||||
lazy(() => import('./pages/build/BuildDetail'))
|
lazy(() => import('./pages/build/BuildDetail'))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const PurchasingIndex = Loadable(
|
||||||
|
lazy(() => import('./pages/purchasing/PurchasingIndex'))
|
||||||
|
);
|
||||||
|
|
||||||
|
export const SalesIndex = Loadable(
|
||||||
|
lazy(() => import('./pages/sales/SalesIndex'))
|
||||||
|
);
|
||||||
|
|
||||||
export const Scan = Loadable(lazy(() => import('./pages/Index/Scan')));
|
export const Scan = Loadable(lazy(() => import('./pages/Index/Scan')));
|
||||||
|
|
||||||
export const Dashboard = Loadable(
|
export const Dashboard = Loadable(
|
||||||
@ -99,6 +107,12 @@ export const routes = (
|
|||||||
<Route index element={<BuildIndex />} />
|
<Route index element={<BuildIndex />} />
|
||||||
<Route path=":id/" element={<BuildDetail />} />
|
<Route path=":id/" element={<BuildDetail />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="purchasing/">
|
||||||
|
<Route index element={<PurchasingIndex />} />
|
||||||
|
</Route>
|
||||||
|
<Route path="sales/">
|
||||||
|
<Route index element={<SalesIndex />} />
|
||||||
|
</Route>
|
||||||
<Route path="/profile/:tabValue" element={<Profile />} />
|
<Route path="/profile/:tabValue" element={<Profile />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/" errorElement={<ErrorPage />}>
|
<Route path="/" errorElement={<ErrorPage />}>
|
||||||
|
@ -68,6 +68,9 @@ export enum ApiPaths {
|
|||||||
// Sales Order URLs
|
// Sales Order URLs
|
||||||
sales_order_list = 'api-sales-order-list',
|
sales_order_list = 'api-sales-order-list',
|
||||||
|
|
||||||
|
// Return Order URLs
|
||||||
|
return_order_list = 'api-return-order-list',
|
||||||
|
|
||||||
// Plugin URLs
|
// Plugin URLs
|
||||||
plugin_list = 'api-plugin-list',
|
plugin_list = 'api-plugin-list',
|
||||||
|
|
||||||
@ -146,6 +149,8 @@ export function apiEndpoint(path: ApiPaths): string {
|
|||||||
return 'order/po/';
|
return 'order/po/';
|
||||||
case ApiPaths.sales_order_list:
|
case ApiPaths.sales_order_list:
|
||||||
return 'order/so/';
|
return 'order/so/';
|
||||||
|
case ApiPaths.return_order_list:
|
||||||
|
return 'order/ro/';
|
||||||
case ApiPaths.plugin_list:
|
case ApiPaths.plugin_list:
|
||||||
return 'plugins/';
|
return 'plugins/';
|
||||||
case ApiPaths.project_code_list:
|
case ApiPaths.project_code_list:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user