diff --git a/src/frontend/src/components/buttons/ActionButton.tsx b/src/frontend/src/components/buttons/ActionButton.tsx index b9bfa90898..148c0f2102 100644 --- a/src/frontend/src/components/buttons/ActionButton.tsx +++ b/src/frontend/src/components/buttons/ActionButton.tsx @@ -20,8 +20,10 @@ export type ActionButtonProps = { * Construct a simple action button with consistent styling */ export function ActionButton(props: ActionButtonProps) { + const hidden = props.hidden ?? false; + return ( - !props.hidden && ( + !hidden && ( { - // TODO: Implement search functionality searchQuery.refetch(); }, [searchText]); diff --git a/src/frontend/src/components/render/Part.tsx b/src/frontend/src/components/render/Part.tsx index 48af44aef6..fe9d9562ee 100644 --- a/src/frontend/src/components/render/Part.tsx +++ b/src/frontend/src/components/render/Part.tsx @@ -1,3 +1,4 @@ +import { t } from '@lingui/macro'; import { ReactNode } from 'react'; import { RenderInlineModel } from './Instance'; @@ -6,10 +7,13 @@ import { RenderInlineModel } from './Instance'; * Inline rendering of a single Part instance */ export function RenderPart({ instance }: { instance: any }): ReactNode { + const stock = t`Stock` + `: ${instance.in_stock}`; + return ( ); diff --git a/src/frontend/src/components/tables/InvenTreeTable.tsx b/src/frontend/src/components/tables/InvenTreeTable.tsx index cf8684b311..3409b0e319 100644 --- a/src/frontend/src/components/tables/InvenTreeTable.tsx +++ b/src/frontend/src/components/tables/InvenTreeTable.tsx @@ -46,8 +46,8 @@ const defaultPageSize: number = 25; * @param enableRefresh : boolean - Enable refresh actions * @param pageSize : number - Number of records per page * @param barcodeActions : any[] - List of barcode actions - * @param customFilters : TableFilter[] - List of custom filters - * @param customActionGroups : any[] - List of custom action groups + * @param tableFilters : TableFilter[] - List of custom filters + * @param tableActions : any[] - List of custom action groups * @param printingActions : any[] - List of printing actions * @param dataFormatter : (data: any) => any - Callback function to reformat data returned by server (if not in default format) * @param rowActions : (record: any) => RowAction[] - Callback function to generate row actions @@ -66,8 +66,8 @@ export type InvenTreeTableProps = { enableRefresh?: boolean; pageSize?: number; barcodeActions?: any[]; - customFilters?: TableFilter[]; - customActionGroups?: React.ReactNode[]; + tableFilters?: TableFilter[]; + tableActions?: React.ReactNode[]; printingActions?: any[]; idAccessor?: string; dataFormatter?: (data: T) => any; @@ -91,8 +91,8 @@ const defaultInvenTreeTableProps: InvenTreeTableProps = { defaultSortColumn: '', printingActions: [], barcodeActions: [], - customFilters: [], - customActionGroups: [], + tableFilters: [], + tableActions: [], idAccessor: 'pk', onRowClick: (record: any, index: number, event: any) => {} }; @@ -425,9 +425,9 @@ export function InvenTreeTable({ return ( <> {tableProps.enableFilters && - (tableProps.customFilters?.length ?? 0) > 0 && ( + (tableProps.tableFilters?.length ?? 0) > 0 && ( setFiltersVisible(false)} @@ -436,7 +436,7 @@ export function InvenTreeTable({ - {tableProps.customActionGroups?.map((group, idx) => ( + {tableProps.tableActions?.map((group, idx) => ( {group} ))} {(tableProps.barcodeActions?.length ?? 0 > 0) && ( @@ -490,7 +490,7 @@ export function InvenTreeTable({ /> )} {tableProps.enableFilters && - (tableProps.customFilters?.length ?? 0 > 0) && ( + (tableProps.tableFilters?.length ?? 0 > 0) && ( navigate(`/part/${row.sub_part}`), rowActions: rowActions }} diff --git a/src/frontend/src/components/tables/bom/UsedInTable.tsx b/src/frontend/src/components/tables/bom/UsedInTable.tsx index 439af13954..6d2bc496ac 100644 --- a/src/frontend/src/components/tables/bom/UsedInTable.tsx +++ b/src/frontend/src/components/tables/bom/UsedInTable.tsx @@ -116,7 +116,7 @@ export function UsedInTable({ part_detail: true, sub_part_detail: true }, - customFilters: tableFilters, + tableFilters: tableFilters, onRowClick: (row) => navigate(`/part/${row.part}`) }} /> diff --git a/src/frontend/src/components/tables/build/BuildOrderTable.tsx b/src/frontend/src/components/tables/build/BuildOrderTable.tsx index b10ebc685e..0b9c885266 100644 --- a/src/frontend/src/components/tables/build/BuildOrderTable.tsx +++ b/src/frontend/src/components/tables/build/BuildOrderTable.tsx @@ -155,7 +155,7 @@ export function BuildOrderTable({ params = {} }: { params?: any }) { ...params, part_detail: true }, - customFilters: tableFilters, + tableFilters: tableFilters, onRowClick: (row) => navigate(`/build/${row.pk}`) }} /> diff --git a/src/frontend/src/components/tables/company/AddressTable.tsx b/src/frontend/src/components/tables/company/AddressTable.tsx index 35e0cbdeab..5b1f8ae373 100644 --- a/src/frontend/src/components/tables/company/AddressTable.tsx +++ b/src/frontend/src/components/tables/company/AddressTable.tsx @@ -185,7 +185,7 @@ export function AddressTable({ columns={columns} props={{ rowActions: rowActions, - customActionGroups: tableActions, + tableActions: tableActions, params: { ...params, company: companyId diff --git a/src/frontend/src/components/tables/company/ContactTable.tsx b/src/frontend/src/components/tables/company/ContactTable.tsx index 7005abea09..4d4d746bb9 100644 --- a/src/frontend/src/components/tables/company/ContactTable.tsx +++ b/src/frontend/src/components/tables/company/ContactTable.tsx @@ -133,7 +133,7 @@ export function ContactTable({ columns={columns} props={{ rowActions: rowActions, - customActionGroups: tableActions, + tableActions: tableActions, params: { ...params, company: companyId diff --git a/src/frontend/src/components/tables/general/AttachmentTable.tsx b/src/frontend/src/components/tables/general/AttachmentTable.tsx index d48c99957c..5338f4993f 100644 --- a/src/frontend/src/components/tables/general/AttachmentTable.tsx +++ b/src/frontend/src/components/tables/general/AttachmentTable.tsx @@ -178,7 +178,7 @@ export function AttachmentTable({ }); } - const customActionGroups: ReactNode[] = useMemo(() => { + const tableActions: ReactNode[] = useMemo(() => { let actions = []; if (allowEdit) { @@ -235,7 +235,7 @@ export function AttachmentTable({ props={{ noRecordsText: t`No attachments found`, enableSelection: true, - customActionGroups: customActionGroups, + tableActions: tableActions, rowActions: allowEdit && allowDelete ? rowActions : undefined, params: { [model]: pk diff --git a/src/frontend/src/components/tables/part/PartCategoryTable.tsx b/src/frontend/src/components/tables/part/PartCategoryTable.tsx index 78b984014b..30d731d7bc 100644 --- a/src/frontend/src/components/tables/part/PartCategoryTable.tsx +++ b/src/frontend/src/components/tables/part/PartCategoryTable.tsx @@ -1,23 +1,30 @@ import { t } from '@lingui/macro'; -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { ApiPaths } from '../../../enums/ApiEndpoints'; +import { UserRoles } from '../../../enums/Roles'; +import { partCategoryFields } from '../../../forms/PartForms'; +import { openCreateApiForm, openEditApiForm } from '../../../functions/forms'; import { useTable } from '../../../hooks/UseTable'; import { apiUrl } from '../../../states/ApiState'; +import { useUserState } from '../../../states/UserState'; +import { AddItemButton } from '../../buttons/AddItemButton'; import { YesNoButton } from '../../items/YesNoButton'; import { TableColumn } from '../Column'; import { DescriptionColumn } from '../ColumnRenderers'; import { TableFilter } from '../Filter'; import { InvenTreeTable } from '../InvenTreeTable'; +import { RowEditAction } from '../RowActions'; /** * PartCategoryTable - Displays a table of part categories */ -export function PartCategoryTable({ params = {} }: { params?: any }) { +export function PartCategoryTable({ parentId }: { parentId?: any }) { const navigate = useNavigate(); const table = useTable('partcategory'); + const user = useUserState(); const tableColumns: TableColumn[] = useMemo(() => { return [ @@ -64,6 +71,62 @@ export function PartCategoryTable({ params = {} }: { params?: any }) { ]; }, []); + const addCategory = useCallback(() => { + let fields = partCategoryFields({}); + + if (parentId) { + fields['parent'].value = parentId; + } + + openCreateApiForm({ + url: apiUrl(ApiPaths.category_list), + title: t`Add Part Category`, + fields: fields, + onFormSuccess(data: any) { + if (data.pk) { + navigate(`/part/category/${data.pk}`); + } else { + table.refreshTable(); + } + } + }); + }, [parentId]); + + const tableActions = useMemo(() => { + let can_add = user.hasAddRole(UserRoles.part_category); + + return [ + + ]; + }, [user]); + + const rowActions = useCallback( + (record: any) => { + let can_edit = user.hasChangeRole(UserRoles.part_category); + + return [ + RowEditAction({ + hidden: !can_edit, + onClick: () => { + openEditApiForm({ + url: ApiPaths.category_list, + pk: record.pk, + title: t`Edit Part Category`, + fields: partCategoryFields({}), + successMessage: t`Part category updated`, + onFormSuccess: table.refreshTable + }); + } + }) + ]; + }, + [user] + ); + return ( { navigate(`/part/category/${record.pk}`); } diff --git a/src/frontend/src/components/tables/part/PartParameterTable.tsx b/src/frontend/src/components/tables/part/PartParameterTable.tsx index 4a04964521..09fe305410 100644 --- a/src/frontend/src/components/tables/part/PartParameterTable.tsx +++ b/src/frontend/src/components/tables/part/PartParameterTable.tsx @@ -180,8 +180,8 @@ export function PartParameterTable({ partId }: { partId: any }) { columns={tableColumns} props={{ rowActions: rowActions, - customActionGroups: tableActions, - customFilters: [ + tableActions: tableActions, + tableFilters: [ { name: 'include_variants', label: t`Include Variants`, diff --git a/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx b/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx index 2e7df5db99..e1d8781f0e 100644 --- a/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx +++ b/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx @@ -131,8 +131,8 @@ export default function PartParameterTemplateTable() { columns={tableColumns} props={{ rowActions: rowActions, - customFilters: tableFilters, - customActionGroups: tableActions + tableFilters: tableFilters, + tableActions: tableActions }} /> ); diff --git a/src/frontend/src/components/tables/part/PartTable.tsx b/src/frontend/src/components/tables/part/PartTable.tsx index 9f612e4c11..1bff34b1b3 100644 --- a/src/frontend/src/components/tables/part/PartTable.tsx +++ b/src/frontend/src/components/tables/part/PartTable.tsx @@ -275,7 +275,7 @@ export function PartListTable({ props }: { props: InvenTreeTableProps }) { props={{ ...props, enableDownload: true, - customFilters: tableFilters, + tableFilters: tableFilters, params: { ...props.params, category_detail: true diff --git a/src/frontend/src/components/tables/part/PartTestTemplateTable.tsx b/src/frontend/src/components/tables/part/PartTestTemplateTable.tsx index 488b502cd8..4ba26e6d6e 100644 --- a/src/frontend/src/components/tables/part/PartTestTemplateTable.tsx +++ b/src/frontend/src/components/tables/part/PartTestTemplateTable.tsx @@ -140,8 +140,8 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) { params: { part: partId }, - customFilters: tableFilters, - customActionGroups: tableActions, + tableFilters: tableFilters, + tableActions: tableActions, rowActions: rowActions }} /> diff --git a/src/frontend/src/components/tables/part/PartVariantTable.tsx b/src/frontend/src/components/tables/part/PartVariantTable.tsx index 7477b6bc2e..a8c8df2251 100644 --- a/src/frontend/src/components/tables/part/PartVariantTable.tsx +++ b/src/frontend/src/components/tables/part/PartVariantTable.tsx @@ -37,7 +37,7 @@ export function PartVariantTable({ partId }: { partId: string }) { ); diff --git a/src/frontend/src/components/tables/plugin/PluginListTable.tsx b/src/frontend/src/components/tables/plugin/PluginListTable.tsx index 4690a8c555..2cae6cd705 100644 --- a/src/frontend/src/components/tables/plugin/PluginListTable.tsx +++ b/src/frontend/src/components/tables/plugin/PluginListTable.tsx @@ -493,8 +493,8 @@ export function PluginListTable({ props }: { props: InvenTreeTableProps }) { }, rowActions: rowActions, onRowClick: (plugin) => navigate(`${plugin.pk}/`), - customActionGroups: tableActions, - customFilters: [ + tableActions: tableActions, + tableFilters: [ { name: 'active', label: t`Active`, diff --git a/src/frontend/src/components/tables/purchasing/ManufacturerPartTable.tsx b/src/frontend/src/components/tables/purchasing/ManufacturerPartTable.tsx index 17276977ba..2b2d03c758 100644 --- a/src/frontend/src/components/tables/purchasing/ManufacturerPartTable.tsx +++ b/src/frontend/src/components/tables/purchasing/ManufacturerPartTable.tsx @@ -5,9 +5,11 @@ import { ApiPaths } from '../../../enums/ApiEndpoints'; import { UserRoles } from '../../../enums/Roles'; import { useManufacturerPartFields } from '../../../forms/CompanyForms'; import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms'; +import { notYetImplemented } from '../../../functions/notifications'; import { useTable } from '../../../hooks/UseTable'; import { apiUrl } from '../../../states/ApiState'; import { useUserState } from '../../../states/UserState'; +import { AddItemButton } from '../../buttons/AddItemButton'; import { Thumbnail } from '../../images/Thumbnail'; import { TableColumn } from '../Column'; import { DescriptionColumn, LinkColumn, PartColumn } from '../ColumnRenderers'; @@ -57,9 +59,22 @@ export function ManufacturerPartTable({ params }: { params: any }): ReactNode { ]; }, [params]); + const addManufacturerPart = useCallback(() => { + notYetImplemented(); + }, []); + const tableActions = useMemo(() => { - // TODO: Custom actions - return []; + let can_add = + user.hasAddRole(UserRoles.purchase_order) && + user.hasAddRole(UserRoles.part); + + return [ +