diff --git a/src/frontend/src/pages/purchasing/PurchasingIndex.tsx b/src/frontend/src/pages/purchasing/PurchasingIndex.tsx index 75f21d3bce..c4303ceeb4 100644 --- a/src/frontend/src/pages/purchasing/PurchasingIndex.tsx +++ b/src/frontend/src/pages/purchasing/PurchasingIndex.tsx @@ -23,8 +23,10 @@ import SegmentedControlPanel from '../../components/panels/SegmentedControlPanel import { useUserState } from '../../states/UserState'; import { CompanyTable } from '../../tables/company/CompanyTable'; import ParametricCompanyTable from '../../tables/company/ParametricCompanyTable'; +import ManufacturerPartParametricTable from '../../tables/purchasing/ManufacturerPartParametricTable'; import { ManufacturerPartTable } from '../../tables/purchasing/ManufacturerPartTable'; import { PurchaseOrderTable } from '../../tables/purchasing/PurchaseOrderTable'; +import SupplierPartParametricTable from '../../tables/purchasing/SupplierPartParametricTable'; import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable'; export default function PurchasingIndex() { @@ -45,6 +47,17 @@ export default function PurchasingIndex() { defaultValue: 'table' }); + const [manufacturerPartsView, setManufacturerPartsView] = + useLocalStorage({ + key: 'manufacturer-parts-view', + defaultValue: 'table' + }); + + const [supplierPartsView, setSupplierPartsView] = useLocalStorage({ + key: 'supplier-parts-view', + defaultValue: 'table' + }); + const panels = useMemo(() => { return [ SegmentedControlPanel({ @@ -103,12 +116,27 @@ export default function PurchasingIndex() { } ] }), - { + SegmentedControlPanel({ name: 'supplier-parts', label: t`Supplier Parts`, icon: , - content: - }, + selection: supplierPartsView, + onChange: setSupplierPartsView, + options: [ + { + value: 'table', + label: t`Table View`, + icon: , + content: + }, + { + value: 'parametric', + label: t`Parametric View`, + icon: , + content: + } + ] + }), SegmentedControlPanel({ name: 'manufacturer', label: t`Manufacturers`, @@ -137,14 +165,36 @@ export default function PurchasingIndex() { } ] }), - { + SegmentedControlPanel({ name: 'manufacturer-parts', label: t`Manufacturer Parts`, icon: , - content: - } + selection: manufacturerPartsView, + onChange: setManufacturerPartsView, + options: [ + { + value: 'table', + label: t`Table View`, + icon: , + content: + }, + { + value: 'parametric', + label: t`Parametric View`, + icon: , + content: + } + ] + }) ]; - }, [user, manufacturerView, purchaseOrderView, supplierView]); + }, [ + user, + manufacturerPartsView, + manufacturerView, + purchaseOrderView, + supplierPartsView, + supplierView + ]); if (!user.isLoggedIn() || !user.hasViewRole(UserRoles.purchase_order)) { return ; diff --git a/src/frontend/src/tables/general/ParametricDataTable.tsx b/src/frontend/src/tables/general/ParametricDataTable.tsx index 64db50e5a3..22339aa613 100644 --- a/src/frontend/src/tables/general/ParametricDataTable.tsx +++ b/src/frontend/src/tables/general/ParametricDataTable.tsx @@ -2,7 +2,8 @@ import { cancelEvent } from '@lib/functions/Events'; import { ApiEndpoints, type ApiFormFieldSet, - ModelType, + type ModelType, + RowViewAction, UserRoles, YesNoButton, apiUrl, @@ -15,6 +16,7 @@ import type { TableColumn } from '@lib/types/Tables'; import { t } from '@lingui/core/macro'; import { Group } from '@mantine/core'; import { useHover } from '@mantine/hooks'; +import { IconCirclePlus } from '@tabler/icons-react'; import { useQuery } from '@tanstack/react-query'; import { type ReactNode, useCallback, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -247,11 +249,11 @@ export default function ParametricDataTable({ }, [parameterFilters]); const [selectedInstance, setSelectedInstance] = useState(-1); - const [selectedTemplate, setSelectedTemplate] = useState(-1); + const [selectedTemplate, setSelectedTemplate] = useState(null); const [selectedParameter, setSelectedParameter] = useState(-1); const parameterFields: ApiFormFieldSet = useParameterFields({ - modelType: ModelType.part, + modelType: modelType, modelId: selectedInstance }); @@ -262,6 +264,16 @@ export default function ParametricDataTable({ focus: 'data', onFormSuccess: (parameter: any) => { updateParameterRecord(selectedInstance, parameter); + + // Ensure that the parameter template is included in the table + const template = parameterTemplates.data.find( + (t: any) => t.pk == parameter.template + ); + + if (!template) { + // Reload the parameter templates + parameterTemplates.refetch(); + } }, initialData: { part: selectedInstance, @@ -374,6 +386,32 @@ export default function ParametricDataTable({ return [...(customColumns || []), ...parameterColumns]; }, [customColumns, parameterColumns]); + const rowActions = useCallback( + (record: any) => { + return [ + { + title: t`Add Parameter`, + icon: , + color: 'green', + hidden: !user.hasAddPermission(modelType), + onClick: () => { + setSelectedInstance(record.pk); + setSelectedTemplate(null); + addParameter.open(); + } + }, + RowViewAction({ + title: t`View`, + modelType: modelType, + modelId: record.pk, + hidden: !user.hasViewPermission(modelType), + navigate: navigate + }) + ]; + }, + [modelType, user] + ); + return ( <> {addParameter.modal} @@ -384,6 +422,7 @@ export default function ParametricDataTable({ columns={tableColumns} props={{ enableDownload: true, + rowActions: rowActions, tableFilters: tableFilters, params: { ...queryParams, diff --git a/src/frontend/src/tables/purchasing/ManufacturerPartParametricTable.tsx b/src/frontend/src/tables/purchasing/ManufacturerPartParametricTable.tsx new file mode 100644 index 0000000000..b8cfdfa16a --- /dev/null +++ b/src/frontend/src/tables/purchasing/ManufacturerPartParametricTable.tsx @@ -0,0 +1,33 @@ +import { ApiEndpoints, ModelType } from '@lib/index'; +import type { TableFilter } from '@lib/types/Filters'; +import type { TableColumn } from '@lib/types/Tables'; +import { type ReactNode, useMemo } from 'react'; +import ParametricDataTable from '../general/ParametricDataTable'; + +export default function ManufacturerPartParametricTable({ + queryParams +}: { + queryParams?: Record; +}): ReactNode { + const customColumns: TableColumn[] = useMemo(() => { + return []; + }, []); + + const customFilters: TableFilter[] = useMemo(() => { + return []; + }, []); + + return ( + + ); +} diff --git a/src/frontend/src/tables/purchasing/SupplierPartParametricTable.tsx b/src/frontend/src/tables/purchasing/SupplierPartParametricTable.tsx new file mode 100644 index 0000000000..08dd6cfe37 --- /dev/null +++ b/src/frontend/src/tables/purchasing/SupplierPartParametricTable.tsx @@ -0,0 +1,52 @@ +import { ApiEndpoints, ModelType } from '@lib/index'; +import type { TableFilter } from '@lib/types/Filters'; +import type { TableColumn } from '@lib/types/Tables'; +import { t } from '@lingui/core/macro'; +import { type ReactNode, useMemo } from 'react'; +import { CompanyColumn, PartColumn } from '../ColumnRenderers'; +import ParametricDataTable from '../general/ParametricDataTable'; + +export default function SupplierPartParametricTable({ + queryParams +}: { + queryParams?: Record; +}): ReactNode { + const customColumns: TableColumn[] = useMemo(() => { + return [ + PartColumn({ + switchable: false, + part: 'part_detail' + }), + { + accessor: 'supplier', + sortable: true, + render: (record: any) => ( + + ) + }, + { + accessor: 'SKU', + title: t`Supplier Part`, + sortable: true + } + ]; + }, []); + + const customFilters: TableFilter[] = useMemo(() => { + return []; + }, []); + + return ( + + ); +}