diff --git a/src/frontend/lib/enums/ApiEndpoints.tsx b/src/frontend/lib/enums/ApiEndpoints.tsx index 2b3b0b774d..20a8b8d5fd 100644 --- a/src/frontend/lib/enums/ApiEndpoints.tsx +++ b/src/frontend/lib/enums/ApiEndpoints.tsx @@ -111,8 +111,6 @@ export enum ApiEndpoints { // Part API endpoints part_list = 'part/', - part_parameter_list = 'part/parameter/', - part_parameter_template_list = 'part/parameter/template/', part_thumbs_list = 'part/thumbs/', part_pricing = 'part/:id/pricing/', part_requirements = 'part/:id/requirements/', diff --git a/src/frontend/lib/enums/ModelInformation.tsx b/src/frontend/lib/enums/ModelInformation.tsx index 30bb2185aa..b529e2c49a 100644 --- a/src/frontend/lib/enums/ModelInformation.tsx +++ b/src/frontend/lib/enums/ModelInformation.tsx @@ -33,18 +33,18 @@ export const ModelInformationDict: ModelDict = { admin_url: '/part/part/', icon: 'part' }, + parameter: { + label: () => t`Parameter`, + label_multiple: () => t`Parameters`, + api_endpoint: ApiEndpoints.parameter_list, + icon: 'list_details' + }, parametertemplate: { label: () => t`Parameter Template`, label_multiple: () => t`Parameter Templates`, - api_endpoint: ApiEndpoints.parameter_template_list - }, - partparametertemplate: { - label: () => t`Part Parameter Template`, - label_multiple: () => t`Part Parameter Templates`, - url_overview: '/settings/admin/part-parameters', - url_detail: '/partparametertemplate/:pk/', - api_endpoint: ApiEndpoints.part_parameter_template_list, - icon: 'test_templates' + api_endpoint: ApiEndpoints.parameter_template_list, + admin_url: '/common/parametertemplate/', + icon: 'list' }, parttesttemplate: { label: () => t`Part Test Template`, diff --git a/src/frontend/lib/enums/ModelType.tsx b/src/frontend/lib/enums/ModelType.tsx index 0b4b1fdf19..57aa1b7205 100644 --- a/src/frontend/lib/enums/ModelType.tsx +++ b/src/frontend/lib/enums/ModelType.tsx @@ -6,7 +6,6 @@ export enum ModelType { supplierpart = 'supplierpart', manufacturerpart = 'manufacturerpart', partcategory = 'partcategory', - partparametertemplate = 'partparametertemplate', parttesttemplate = 'parttesttemplate', projectcode = 'projectcode', stockitem = 'stockitem', diff --git a/src/frontend/src/forms/PartForms.tsx b/src/frontend/src/forms/PartForms.tsx index 4125d00788..61c14a0103 100644 --- a/src/frontend/src/forms/PartForms.tsx +++ b/src/frontend/src/forms/PartForms.tsx @@ -1,11 +1,7 @@ +import type { ApiFormFieldSet } from '@lib/types/Forms'; import { t } from '@lingui/core/macro'; import { IconBuildingStore, IconCopy, IconPackages } from '@tabler/icons-react'; import { useMemo, useState } from 'react'; - -import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; -import { apiUrl } from '@lib/functions/Api'; -import type { ApiFormFieldSet } from '@lib/types/Forms'; -import { useApi } from '../contexts/ApiContext'; import { useGlobalSettingsState } from '../states/SettingsStates'; /** @@ -224,97 +220,6 @@ export function partCategoryFields({ return fields; } -export function usePartParameterFields({ - editTemplate -}: { - editTemplate?: boolean; -}): ApiFormFieldSet { - const api = useApi(); - - // Valid field choices - const [choices, setChoices] = useState([]); - - // Field type for "data" input - const [fieldType, setFieldType] = useState<'string' | 'boolean' | 'choice'>( - 'string' - ); - - return useMemo(() => { - return { - part: { - disabled: true - }, - template: { - disabled: editTemplate == false, - onValueChange: (value: any, record: any) => { - // Adjust the type of the "data" field based on the selected template - if (record?.checkbox) { - // This is a "checkbox" field - setChoices([]); - setFieldType('boolean'); - } else if (record?.choices) { - const _choices: string[] = record.choices.split(','); - - if (_choices.length > 0) { - setChoices( - _choices.map((choice) => { - return { - display_name: choice.trim(), - value: choice.trim() - }; - }) - ); - setFieldType('choice'); - } else { - setChoices([]); - setFieldType('string'); - } - } else if (record?.selectionlist) { - api - .get( - apiUrl(ApiEndpoints.selectionlist_detail, record.selectionlist) - ) - .then((res) => { - setChoices( - res.data.choices.map((item: any) => { - return { - value: item.value, - display_name: item.label - }; - }) - ); - setFieldType('choice'); - }); - } else { - setChoices([]); - setFieldType('string'); - } - } - }, - data: { - type: fieldType, - field_type: fieldType, - choices: fieldType === 'choice' ? choices : undefined, - default: fieldType === 'boolean' ? false : undefined, - adjustValue: (value: any) => { - // Coerce boolean value into a string (required by backend) - - let v: string = value.toString().trim(); - - if (fieldType === 'boolean') { - if (v.toLowerCase() !== 'true') { - v = 'false'; - } - } - - return v; - } - }, - note: {} - }; - }, [editTemplate, fieldType, choices]); -} - export function partStocktakeFields(): ApiFormFieldSet { return { part: { diff --git a/src/frontend/src/pages/part/CategoryDetail.tsx b/src/frontend/src/pages/part/CategoryDetail.tsx index 55b9ce440f..a785857dd2 100644 --- a/src/frontend/src/pages/part/CategoryDetail.tsx +++ b/src/frontend/src/pages/part/CategoryDetail.tsx @@ -312,7 +312,11 @@ export default function CategoryDetail() { name: 'parameters', label: t`Part Parameters`, icon: , - content: + content: ( + <> + + + ) } ], [category, id] diff --git a/src/frontend/src/tables/general/ParametricDataTable.tsx b/src/frontend/src/tables/general/ParametricDataTable.tsx new file mode 100644 index 0000000000..048e242e7e --- /dev/null +++ b/src/frontend/src/tables/general/ParametricDataTable.tsx @@ -0,0 +1,409 @@ +import { cancelEvent } from '@lib/functions/Events'; +import { + ApiEndpoints, + type ApiFormFieldSet, + ModelType, + UserRoles, + YesNoButton, + apiUrl, + formatDecimal, + getDetailUrl, + navigateToLink +} from '@lib/index'; +import type { TableFilter } from '@lib/types/Filters'; +import type { TableColumn } from '@lib/types/Tables'; +import { t } from '@lingui/core/macro'; +import { Group } from '@mantine/core'; +import { useHover } from '@mantine/hooks'; +import { useQuery } from '@tanstack/react-query'; +import { type ReactNode, useCallback, useMemo, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useApi } from '../../contexts/ApiContext'; +import { useParameterFields } from '../../forms/CommonForms'; +import { + useCreateApiFormModal, + useEditApiFormModal +} from '../../hooks/UseForm'; +import { useTable } from '../../hooks/UseTable'; +import { useUserState } from '../../states/UserState'; +import { InvenTreeTable } from '../InvenTreeTable'; +import { TableHoverCard } from '../TableHoverCard'; +import { + PARAMETER_FILTER_OPERATORS, + ParameterFilter +} from './ParametricDataTableFilters'; + +// Render an individual parameter cell +function ParameterCell({ + record, + template, + canEdit +}: Readonly<{ + record: any; + template: any; + canEdit: boolean; +}>) { + const { hovered, ref } = useHover(); + + // Find matching template parameter + const parameter = useMemo(() => { + return record.parameters?.find((p: any) => p.template == template.pk); + }, [record, template]); + + const extra: any[] = []; + + // Format the value for display + const value: ReactNode = useMemo(() => { + let v: any = parameter?.data; + + // Handle boolean values + if (template?.checkbox && v != undefined) { + v = ; + } + + return v; + }, [parameter, template]); + + if ( + template.units && + parameter && + parameter.data_numeric && + parameter.data_numeric != parameter.data + ) { + const numeric = formatDecimal(parameter.data_numeric, { digits: 15 }); + extra.push(`${numeric} [${template.units}]`); + } + + if (hovered && canEdit) { + extra.push(t`Click to edit`); + } + + return ( +
+ + + + + +
+ ); +} + +/** + * A table which displays parametric data for generic model types. + * The table can be extended by passing in additional column, filters, and actions. + */ +export default function ParametricDataTable({ + modelType, + endpoint, + queryParams, + customFilters, + customColumns +}: { + modelType: ModelType; + endpoint: ApiEndpoints | string; + queryParams?: Record; + customFilters?: TableFilter[]; + customColumns?: TableColumn[]; +}) { + const api = useApi(); + const table = useTable(`parametric-data-${modelType}`); + const user = useUserState(); + const navigate = useNavigate(); + + // Fetch all active parameter templates for the given model type + const parameterTemplates = useQuery({ + queryKey: ['parameter-templates', modelType], + queryFn: async () => { + return api + .get(apiUrl(ApiEndpoints.parameter_template_list), { + params: { + active: true, + for_model: modelType + } + }) + .then((response) => response.data); + }, + refetchOnMount: true + }); + + /* Store filters against selected part parameters. + * These are stored in the format: + * { + * parameter_1: { + * '=': 'value1', + * '<': 'value2', + * ... + * }, + * parameter_2: { + * '=': 'value3', + * }, + * ... + * } + * + * Which allows multiple filters to be applied against each parameter template. + */ + const [parameterFilters, setParameterFilters] = useState({}); + + /* Remove filters for a specific parameter template + * - If no operator is specified, remove all filters for this template + * - If an operator is specified, remove filters for that operator only + */ + const clearParameterFilter = useCallback( + (templateId: number, operator?: string) => { + const filterName = `parameter_${templateId}`; + + if (!operator) { + // If no operator is specified, remove all filters for this template + setParameterFilters((prev: any) => { + const newFilters = { ...prev }; + // Remove any filters that match the template ID + Object.keys(newFilters).forEach((key: string) => { + if (key == filterName) { + delete newFilters[key]; + } + }); + return newFilters; + }); + + return; + } + + // An operator is specified, so we remove filters for that operator only + setParameterFilters((prev: any) => { + const filters = { ...prev }; + + const paramFilters = filters[filterName] || {}; + + if (paramFilters[operator] !== undefined) { + // Remove the specific operator filter + delete paramFilters[operator]; + } + + return { + ...filters, + [filterName]: paramFilters + }; + }); + + table.refreshTable(); + }, + [setParameterFilters, table.refreshTable] + ); + + /** + * Add (or update) a filter for a specific parameter template. + * @param templateId - The ID of the parameter template to filter on. + * @param value - The value to filter by. + * @param operator - The operator to use for filtering (e.g., '=', '<', '>', etc.). + */ + const addParameterFilter = useCallback( + (templateId: number, value: string, operator: string) => { + const filterName = `parameter_${templateId}`; + + const filterValue = value?.toString().trim() ?? ''; + + if (filterValue.length > 0) { + setParameterFilters((prev: any) => { + const filters = { ...prev }; + const paramFilters = filters[filterName] || {}; + + paramFilters[operator] = filterValue; + + return { + ...filters, + [filterName]: paramFilters + }; + }); + + table.refreshTable(); + } + }, + [setParameterFilters, clearParameterFilter, table.refreshTable] + ); + + // Construct the query filters for the table based on the parameter filters + const parametricQueryFilters = useMemo(() => { + const filters: Record = {}; + + Object.keys(parameterFilters).forEach((key: string) => { + const paramFilters: any = parameterFilters[key]; + + Object.keys(paramFilters).forEach((operator: string) => { + const name = `${key}${PARAMETER_FILTER_OPERATORS[operator] || ''}`; + const value = paramFilters[operator]; + + filters[name] = value; + }); + }); + + return filters; + }, [parameterFilters]); + + const [selectedInstance, setSelectedInstance] = useState(-1); + const [selectedTemplate, setSelectedTemplate] = useState(-1); + const [selectedParameter, setSelectedParameter] = useState(-1); + + const parameterFields: ApiFormFieldSet = useParameterFields({ + modelType: ModelType.part, + modelId: selectedInstance + }); + + const addParameter = useCreateApiFormModal({ + url: ApiEndpoints.parameter_list, + title: t`Add Parameter`, + fields: useMemo(() => ({ ...parameterFields }), [parameterFields]), + focus: 'data', + onFormSuccess: (parameter: any) => { + updateParameterRecord(selectedInstance, parameter); + }, + initialData: { + part: selectedInstance, + template: selectedTemplate + } + }); + + const editParameter = useEditApiFormModal({ + url: ApiEndpoints.parameter_list, + title: t`Edit Parameter`, + pk: selectedParameter, + fields: useMemo(() => ({ ...parameterFields }), [parameterFields]), + focus: 'data', + onFormSuccess: (parameter: any) => { + updateParameterRecord(selectedInstance, parameter); + } + }); + + // Update a single parameter record in the table + const updateParameterRecord = useCallback( + (part: number, parameter: any) => { + const records = table.records; + const recordIndex = records.findIndex((record: any) => record.pk == part); + + if (recordIndex < 0) { + // No matching part: reload the entire table + table.refreshTable(); + return; + } + + const parameterIndex = records[recordIndex].parameters.findIndex( + (p: any) => p.pk == parameter.pk + ); + + if (parameterIndex < 0) { + // No matching parameter - append new parameter + records[recordIndex].parameters.push(parameter); + } else { + records[recordIndex].parameters[parameterIndex] = parameter; + } + + table.updateRecord(records[recordIndex]); + }, + [table.records, table.updateRecord] + ); + + const parameterColumns: TableColumn[] = useMemo(() => { + const data = parameterTemplates?.data || []; + + return data.map((template: any) => { + let title = template.name; + + if (template.units) { + title += ` [${template.units}]`; + } + + const filters = parameterFilters[`parameter_${template.pk}`] || {}; + + return { + accessor: `parameter_${template.pk}`, + title: title, + sortable: true, + extra: { + template: template.pk + }, + render: (record: any) => ( + + ), + filtering: Object.keys(filters).length > 0, + filter: ({ close }: { close: () => void }) => { + return ( + + ); + } + }; + }); + }, [user, parameterTemplates.data, parameterFilters]); + + // Callback function when a parameter cell is clicked + const onParameterClick = useCallback((template: number, instance: any) => { + setSelectedTemplate(template); + setSelectedInstance(instance.pk); + const parameter = instance.parameters?.find( + (p: any) => p.template == template + ); + + if (parameter) { + setSelectedParameter(parameter.pk); + editParameter.open(); + } else { + addParameter.open(); + } + }, []); + + const tableFilters: TableFilter[] = useMemo(() => { + return [...(customFilters || [])]; + }, [customFilters]); + + const tableColumns: TableColumn[] = useMemo(() => { + return [...(customColumns || []), ...parameterColumns]; + }, [customColumns, parameterColumns]); + + return ( + <> + {addParameter.modal} + {editParameter.modal} + { + cancelEvent(event); + + // Is this a "parameter" cell? + if (column?.accessor?.toString()?.startsWith('parameter_')) { + const col = column as any; + onParameterClick(col.extra.template, record); + } else if (record?.pk) { + // Navigate through to the detail page + const url = getDetailUrl(modelType, record.pk); + navigateToLink(url, navigate, event); + } + } + }} + /> + + ); +} diff --git a/src/frontend/src/tables/part/ParametricPartTableFilters.tsx b/src/frontend/src/tables/general/ParametricDataTableFilters.tsx similarity index 100% rename from src/frontend/src/tables/part/ParametricPartTableFilters.tsx rename to src/frontend/src/tables/general/ParametricDataTableFilters.tsx diff --git a/src/frontend/src/tables/part/ParametricPartTable.tsx b/src/frontend/src/tables/part/ParametricPartTable.tsx index 4203fdd96f..f99e3ee668 100644 --- a/src/frontend/src/tables/part/ParametricPartTable.tsx +++ b/src/frontend/src/tables/part/ParametricPartTable.tsx @@ -1,353 +1,18 @@ -import { t } from '@lingui/core/macro'; -import { Group } from '@mantine/core'; -import { useHover } from '@mantine/hooks'; -import { useQuery } from '@tanstack/react-query'; -import { type ReactNode, useCallback, useMemo, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; - -import { YesNoButton } from '@lib/components/YesNoButton'; import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { ModelType } from '@lib/enums/ModelType'; -import { UserRoles } from '@lib/enums/Roles'; -import { apiUrl } from '@lib/functions/Api'; -import { cancelEvent } from '@lib/functions/Events'; -import { getDetailUrl } from '@lib/functions/Navigation'; -import { navigateToLink } from '@lib/functions/Navigation'; import type { TableFilter } from '@lib/types/Filters'; -import type { ApiFormFieldSet } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; -import { useApi } from '../../contexts/ApiContext'; -import { formatDecimal } from '../../defaults/formatters'; -import { usePartParameterFields } from '../../forms/PartForms'; -import { - useCreateApiFormModal, - useEditApiFormModal -} from '../../hooks/UseForm'; -import { useTable } from '../../hooks/UseTable'; -import { useUserState } from '../../states/UserState'; +import { t } from '@lingui/core/macro'; +import { useMemo } from 'react'; import { DescriptionColumn, PartColumn } from '../ColumnRenderers'; -import { InvenTreeTable } from '../InvenTreeTable'; -import { TableHoverCard } from '../TableHoverCard'; -import { - PARAMETER_FILTER_OPERATORS, - ParameterFilter -} from './ParametricPartTableFilters'; - -// Render an individual parameter cell -function ParameterCell({ - record, - template, - canEdit -}: Readonly<{ - record: any; - template: any; - canEdit: boolean; -}>) { - const { hovered, ref } = useHover(); - - // Find matching template parameter - const parameter = useMemo(() => { - return record.parameters?.find((p: any) => p.template == template.pk); - }, [record, template]); - - const extra: any[] = []; - - // Format the value for display - const value: ReactNode = useMemo(() => { - let v: any = parameter?.data; - - // Handle boolean values - if (template?.checkbox && v != undefined) { - v = ; - } - - return v; - }, [parameter, template]); - - if ( - template.units && - parameter && - parameter.data_numeric && - parameter.data_numeric != parameter.data - ) { - const numeric = formatDecimal(parameter.data_numeric, { digits: 15 }); - extra.push(`${numeric} [${template.units}]`); - } - - if (hovered && canEdit) { - extra.push(t`Click to edit`); - } - - return ( -
- - - - - -
- ); -} +import ParametricDataTable from '../general/ParametricDataTable'; export default function ParametricPartTable({ categoryId }: Readonly<{ categoryId?: any; }>) { - const api = useApi(); - const table = useTable('parametric-parts'); - const user = useUserState(); - const navigate = useNavigate(); - - const categoryParameters = useQuery({ - queryKey: ['category-parameters', categoryId], - queryFn: async () => { - return api - .get(apiUrl(ApiEndpoints.part_parameter_template_list), { - params: { - category: categoryId - } - }) - .then((response) => response.data); - }, - refetchOnMount: true - }); - - /* Store filters against selected part parameters. - * These are stored in the format: - * { - * parameter_1: { - * '=': 'value1', - * '<': 'value2', - * ... - * }, - * parameter_2: { - * '=': 'value3', - * }, - * ... - * } - * - * Which allows multiple filters to be applied against each parameter template. - */ - const [parameterFilters, setParameterFilters] = useState({}); - - /* Remove filters for a specific parameter template - * - If no operator is specified, remove all filters for this template - * - If an operator is specified, remove filters for that operator only - */ - const clearParameterFilter = useCallback( - (templateId: number, operator?: string) => { - const filterName = `parameter_${templateId}`; - - if (!operator) { - // If no operator is specified, remove all filters for this template - setParameterFilters((prev: any) => { - const newFilters = { ...prev }; - // Remove any filters that match the template ID - Object.keys(newFilters).forEach((key: string) => { - if (key == filterName) { - delete newFilters[key]; - } - }); - return newFilters; - }); - - return; - } - - // An operator is specified, so we remove filters for that operator only - setParameterFilters((prev: any) => { - const filters = { ...prev }; - - const paramFilters = filters[filterName] || {}; - - if (paramFilters[operator] !== undefined) { - // Remove the specific operator filter - delete paramFilters[operator]; - } - - return { - ...filters, - [filterName]: paramFilters - }; - }); - - table.refreshTable(); - }, - [setParameterFilters, table.refreshTable] - ); - - /** - * Add (or update) a filter for a specific parameter template. - * @param templateId - The ID of the parameter template to filter on. - * @param value - The value to filter by. - * @param operator - The operator to use for filtering (e.g., '=', '<', '>', etc.). - */ - const addParameterFilter = useCallback( - (templateId: number, value: string, operator: string) => { - const filterName = `parameter_${templateId}`; - - const filterValue = value?.toString().trim() ?? ''; - - if (filterValue.length > 0) { - setParameterFilters((prev: any) => { - const filters = { ...prev }; - const paramFilters = filters[filterName] || {}; - - paramFilters[operator] = filterValue; - - return { - ...filters, - [filterName]: paramFilters - }; - }); - - table.refreshTable(); - } - }, - [setParameterFilters, clearParameterFilter, table.refreshTable] - ); - - // Construct the query filters for the table based on the parameter filters - const parametricQueryFilters = useMemo(() => { - const filters: Record = {}; - - Object.keys(parameterFilters).forEach((key: string) => { - const paramFilters: any = parameterFilters[key]; - - Object.keys(paramFilters).forEach((operator: string) => { - const name = `${key}${PARAMETER_FILTER_OPERATORS[operator] || ''}`; - const value = paramFilters[operator]; - - filters[name] = value; - }); - }); - - return filters; - }, [parameterFilters]); - - const [selectedPart, setSelectedPart] = useState(0); - const [selectedTemplate, setSelectedTemplate] = useState(0); - const [selectedParameter, setSelectedParameter] = useState(0); - - const partParameterFields: ApiFormFieldSet = usePartParameterFields({ - editTemplate: false - }); - - const addParameter = useCreateApiFormModal({ - url: ApiEndpoints.part_parameter_list, - title: t`Add Part Parameter`, - fields: useMemo(() => ({ ...partParameterFields }), [partParameterFields]), - focus: 'data', - onFormSuccess: (parameter: any) => { - updateParameterRecord(selectedPart, parameter); - }, - initialData: { - part: selectedPart, - template: selectedTemplate - } - }); - - const editParameter = useEditApiFormModal({ - url: ApiEndpoints.part_parameter_list, - title: t`Edit Part Parameter`, - pk: selectedParameter, - fields: useMemo(() => ({ ...partParameterFields }), [partParameterFields]), - focus: 'data', - onFormSuccess: (parameter: any) => { - updateParameterRecord(selectedPart, parameter); - } - }); - - // Update a single parameter record in the table - const updateParameterRecord = useCallback( - (part: number, parameter: any) => { - const records = table.records; - const partIndex = records.findIndex((record: any) => record.pk == part); - - if (partIndex < 0) { - // No matching part: reload the entire table - table.refreshTable(); - return; - } - - const parameterIndex = records[partIndex].parameters.findIndex( - (p: any) => p.pk == parameter.pk - ); - - if (parameterIndex < 0) { - // No matching parameter - append new parameter - records[partIndex].parameters.push(parameter); - } else { - records[partIndex].parameters[parameterIndex] = parameter; - } - - table.updateRecord(records[partIndex]); - }, - [table.records, table.updateRecord] - ); - - const parameterColumns: TableColumn[] = useMemo(() => { - const data = categoryParameters?.data || []; - - return data.map((template: any) => { - let title = template.name; - - if (template.units) { - title += ` [${template.units}]`; - } - - const filters = parameterFilters[`parameter_${template.pk}`] || {}; - - return { - accessor: `parameter_${template.pk}`, - title: title, - sortable: true, - extra: { - template: template.pk - }, - render: (record: any) => ( - - ), - filtering: Object.keys(filters).length > 0, - filter: ({ close }: { close: () => void }) => { - return ( - - ); - } - }; - }); - }, [user, categoryParameters.data, parameterFilters]); - - const onParameterClick = useCallback((template: number, part: any) => { - setSelectedTemplate(template); - setSelectedPart(part.pk); - const parameter = part.parameters?.find((p: any) => p.template == template); - - if (parameter) { - setSelectedParameter(parameter.pk); - editParameter.open(); - } else { - addParameter.open(); - } - }, []); - - const tableFilters: TableFilter[] = useMemo(() => { + const customFilters: TableFilter[] = useMemo(() => { return [ { name: 'active', @@ -367,8 +32,8 @@ export default function ParametricPartTable({ ]; }, []); - const tableColumns: TableColumn[] = useMemo(() => { - const partColumns: TableColumn[] = [ + const customColumns: TableColumn[] = useMemo(() => { + return [ PartColumn({ part: '', switchable: false @@ -386,43 +51,19 @@ export default function ParametricPartTable({ sortable: true } ]; - - return [...partColumns, ...parameterColumns]; - }, [parameterColumns]); + }, []); return ( - <> - {addParameter.modal} - {editParameter.modal} - { - cancelEvent(event); - - // Is this a "parameter" cell? - if (column?.accessor?.toString()?.startsWith('parameter_')) { - const col = column as any; - onParameterClick(col.extra.template, record); - } else if (record?.pk) { - // Navigate through to the part detail page - const url = getDetailUrl(ModelType.part, record.pk); - navigateToLink(url, navigate, event); - } - } - }} - /> - + ); }