mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	[PUI] Small updates (#6320)
* Ensure .ts files are generated - "yarn run compile" before "yarn run dev" - ensures that .ts locale files are all generated * Implement "Add Part Category" button * Create new stock location * Rename customActionGroups to tableActions * Rename customFilters to tableFilters * Edit category from table * Edit stock location from table * Add some placeholder buttons * More placeholders
This commit is contained in:
		| @@ -20,8 +20,10 @@ export type ActionButtonProps = { | |||||||
|  * Construct a simple action button with consistent styling |  * Construct a simple action button with consistent styling | ||||||
|  */ |  */ | ||||||
| export function ActionButton(props: ActionButtonProps) { | export function ActionButton(props: ActionButtonProps) { | ||||||
|  |   const hidden = props.hidden ?? false; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     !props.hidden && ( |     !hidden && ( | ||||||
|       <Tooltip |       <Tooltip | ||||||
|         key={`tooltip-${props.key}`} |         key={`tooltip-${props.key}`} | ||||||
|         disabled={!props.tooltip && !props.text} |         disabled={!props.tooltip && !props.text} | ||||||
|   | |||||||
| @@ -234,7 +234,6 @@ export function SearchDrawer({ | |||||||
|  |  | ||||||
|   // Re-fetch data whenever the search term is updated |   // Re-fetch data whenever the search term is updated | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     // TODO: Implement search functionality |  | ||||||
|     searchQuery.refetch(); |     searchQuery.refetch(); | ||||||
|   }, [searchText]); |   }, [searchText]); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | import { t } from '@lingui/macro'; | ||||||
| import { ReactNode } from 'react'; | import { ReactNode } from 'react'; | ||||||
|  |  | ||||||
| import { RenderInlineModel } from './Instance'; | import { RenderInlineModel } from './Instance'; | ||||||
| @@ -6,10 +7,13 @@ import { RenderInlineModel } from './Instance'; | |||||||
|  * Inline rendering of a single Part instance |  * Inline rendering of a single Part instance | ||||||
|  */ |  */ | ||||||
| export function RenderPart({ instance }: { instance: any }): ReactNode { | export function RenderPart({ instance }: { instance: any }): ReactNode { | ||||||
|  |   const stock = t`Stock` + `: ${instance.in_stock}`; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <RenderInlineModel |     <RenderInlineModel | ||||||
|       primary={instance.name} |       primary={instance.name} | ||||||
|       secondary={instance.description} |       secondary={instance.description} | ||||||
|  |       suffix={stock} | ||||||
|       image={instance.thumnbnail || instance.image} |       image={instance.thumnbnail || instance.image} | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -46,8 +46,8 @@ const defaultPageSize: number = 25; | |||||||
|  * @param enableRefresh : boolean - Enable refresh actions |  * @param enableRefresh : boolean - Enable refresh actions | ||||||
|  * @param pageSize : number - Number of records per page |  * @param pageSize : number - Number of records per page | ||||||
|  * @param barcodeActions : any[] - List of barcode actions |  * @param barcodeActions : any[] - List of barcode actions | ||||||
|  * @param customFilters : TableFilter[] - List of custom filters |  * @param tableFilters : TableFilter[] - List of custom filters | ||||||
|  * @param customActionGroups : any[] - List of custom action groups |  * @param tableActions : any[] - List of custom action groups | ||||||
|  * @param printingActions : any[] - List of printing actions |  * @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 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 |  * @param rowActions : (record: any) => RowAction[] - Callback function to generate row actions | ||||||
| @@ -66,8 +66,8 @@ export type InvenTreeTableProps<T = any> = { | |||||||
|   enableRefresh?: boolean; |   enableRefresh?: boolean; | ||||||
|   pageSize?: number; |   pageSize?: number; | ||||||
|   barcodeActions?: any[]; |   barcodeActions?: any[]; | ||||||
|   customFilters?: TableFilter[]; |   tableFilters?: TableFilter[]; | ||||||
|   customActionGroups?: React.ReactNode[]; |   tableActions?: React.ReactNode[]; | ||||||
|   printingActions?: any[]; |   printingActions?: any[]; | ||||||
|   idAccessor?: string; |   idAccessor?: string; | ||||||
|   dataFormatter?: (data: T) => any; |   dataFormatter?: (data: T) => any; | ||||||
| @@ -91,8 +91,8 @@ const defaultInvenTreeTableProps: InvenTreeTableProps = { | |||||||
|   defaultSortColumn: '', |   defaultSortColumn: '', | ||||||
|   printingActions: [], |   printingActions: [], | ||||||
|   barcodeActions: [], |   barcodeActions: [], | ||||||
|   customFilters: [], |   tableFilters: [], | ||||||
|   customActionGroups: [], |   tableActions: [], | ||||||
|   idAccessor: 'pk', |   idAccessor: 'pk', | ||||||
|   onRowClick: (record: any, index: number, event: any) => {} |   onRowClick: (record: any, index: number, event: any) => {} | ||||||
| }; | }; | ||||||
| @@ -425,9 +425,9 @@ export function InvenTreeTable<T = any>({ | |||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       {tableProps.enableFilters && |       {tableProps.enableFilters && | ||||||
|         (tableProps.customFilters?.length ?? 0) > 0 && ( |         (tableProps.tableFilters?.length ?? 0) > 0 && ( | ||||||
|           <FilterSelectDrawer |           <FilterSelectDrawer | ||||||
|             availableFilters={tableProps.customFilters ?? []} |             availableFilters={tableProps.tableFilters ?? []} | ||||||
|             tableState={tableState} |             tableState={tableState} | ||||||
|             opened={filtersVisible} |             opened={filtersVisible} | ||||||
|             onClose={() => setFiltersVisible(false)} |             onClose={() => setFiltersVisible(false)} | ||||||
| @@ -436,7 +436,7 @@ export function InvenTreeTable<T = any>({ | |||||||
|       <Stack spacing="sm"> |       <Stack spacing="sm"> | ||||||
|         <Group position="apart"> |         <Group position="apart"> | ||||||
|           <Group position="left" key="custom-actions" spacing={5}> |           <Group position="left" key="custom-actions" spacing={5}> | ||||||
|             {tableProps.customActionGroups?.map((group, idx) => ( |             {tableProps.tableActions?.map((group, idx) => ( | ||||||
|               <Fragment key={idx}>{group}</Fragment> |               <Fragment key={idx}>{group}</Fragment> | ||||||
|             ))} |             ))} | ||||||
|             {(tableProps.barcodeActions?.length ?? 0 > 0) && ( |             {(tableProps.barcodeActions?.length ?? 0 > 0) && ( | ||||||
| @@ -490,7 +490,7 @@ export function InvenTreeTable<T = any>({ | |||||||
|               /> |               /> | ||||||
|             )} |             )} | ||||||
|             {tableProps.enableFilters && |             {tableProps.enableFilters && | ||||||
|               (tableProps.customFilters?.length ?? 0 > 0) && ( |               (tableProps.tableFilters?.length ?? 0 > 0) && ( | ||||||
|                 <Indicator |                 <Indicator | ||||||
|                   size="xs" |                   size="xs" | ||||||
|                   label={tableState.activeFilters.length} |                   label={tableState.activeFilters.length} | ||||||
|   | |||||||
| @@ -363,7 +363,7 @@ export function BomTable({ | |||||||
|           part_detail: true, |           part_detail: true, | ||||||
|           sub_part_detail: true |           sub_part_detail: true | ||||||
|         }, |         }, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|         onRowClick: (row) => navigate(`/part/${row.sub_part}`), |         onRowClick: (row) => navigate(`/part/${row.sub_part}`), | ||||||
|         rowActions: rowActions |         rowActions: rowActions | ||||||
|       }} |       }} | ||||||
|   | |||||||
| @@ -116,7 +116,7 @@ export function UsedInTable({ | |||||||
|           part_detail: true, |           part_detail: true, | ||||||
|           sub_part_detail: true |           sub_part_detail: true | ||||||
|         }, |         }, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|         onRowClick: (row) => navigate(`/part/${row.part}`) |         onRowClick: (row) => navigate(`/part/${row.part}`) | ||||||
|       }} |       }} | ||||||
|     /> |     /> | ||||||
|   | |||||||
| @@ -155,7 +155,7 @@ export function BuildOrderTable({ params = {} }: { params?: any }) { | |||||||
|           ...params, |           ...params, | ||||||
|           part_detail: true |           part_detail: true | ||||||
|         }, |         }, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|         onRowClick: (row) => navigate(`/build/${row.pk}`) |         onRowClick: (row) => navigate(`/build/${row.pk}`) | ||||||
|       }} |       }} | ||||||
|     /> |     /> | ||||||
|   | |||||||
| @@ -185,7 +185,7 @@ export function AddressTable({ | |||||||
|       columns={columns} |       columns={columns} | ||||||
|       props={{ |       props={{ | ||||||
|         rowActions: rowActions, |         rowActions: rowActions, | ||||||
|         customActionGroups: tableActions, |         tableActions: tableActions, | ||||||
|         params: { |         params: { | ||||||
|           ...params, |           ...params, | ||||||
|           company: companyId |           company: companyId | ||||||
|   | |||||||
| @@ -133,7 +133,7 @@ export function ContactTable({ | |||||||
|       columns={columns} |       columns={columns} | ||||||
|       props={{ |       props={{ | ||||||
|         rowActions: rowActions, |         rowActions: rowActions, | ||||||
|         customActionGroups: tableActions, |         tableActions: tableActions, | ||||||
|         params: { |         params: { | ||||||
|           ...params, |           ...params, | ||||||
|           company: companyId |           company: companyId | ||||||
|   | |||||||
| @@ -178,7 +178,7 @@ export function AttachmentTable({ | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const customActionGroups: ReactNode[] = useMemo(() => { |   const tableActions: ReactNode[] = useMemo(() => { | ||||||
|     let actions = []; |     let actions = []; | ||||||
|  |  | ||||||
|     if (allowEdit) { |     if (allowEdit) { | ||||||
| @@ -235,7 +235,7 @@ export function AttachmentTable({ | |||||||
|           props={{ |           props={{ | ||||||
|             noRecordsText: t`No attachments found`, |             noRecordsText: t`No attachments found`, | ||||||
|             enableSelection: true, |             enableSelection: true, | ||||||
|             customActionGroups: customActionGroups, |             tableActions: tableActions, | ||||||
|             rowActions: allowEdit && allowDelete ? rowActions : undefined, |             rowActions: allowEdit && allowDelete ? rowActions : undefined, | ||||||
|             params: { |             params: { | ||||||
|               [model]: pk |               [model]: pk | ||||||
|   | |||||||
| @@ -1,23 +1,30 @@ | |||||||
| import { t } from '@lingui/macro'; | import { t } from '@lingui/macro'; | ||||||
| import { useMemo } from 'react'; | import { useCallback, useMemo } from 'react'; | ||||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | 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 { useTable } from '../../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../../states/ApiState'; | import { apiUrl } from '../../../states/ApiState'; | ||||||
|  | import { useUserState } from '../../../states/UserState'; | ||||||
|  | import { AddItemButton } from '../../buttons/AddItemButton'; | ||||||
| import { YesNoButton } from '../../items/YesNoButton'; | import { YesNoButton } from '../../items/YesNoButton'; | ||||||
| import { TableColumn } from '../Column'; | import { TableColumn } from '../Column'; | ||||||
| import { DescriptionColumn } from '../ColumnRenderers'; | import { DescriptionColumn } from '../ColumnRenderers'; | ||||||
| import { TableFilter } from '../Filter'; | import { TableFilter } from '../Filter'; | ||||||
| import { InvenTreeTable } from '../InvenTreeTable'; | import { InvenTreeTable } from '../InvenTreeTable'; | ||||||
|  | import { RowEditAction } from '../RowActions'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * PartCategoryTable - Displays a table of part categories |  * PartCategoryTable - Displays a table of part categories | ||||||
|  */ |  */ | ||||||
| export function PartCategoryTable({ params = {} }: { params?: any }) { | export function PartCategoryTable({ parentId }: { parentId?: any }) { | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|  |  | ||||||
|   const table = useTable('partcategory'); |   const table = useTable('partcategory'); | ||||||
|  |   const user = useUserState(); | ||||||
|  |  | ||||||
|   const tableColumns: TableColumn[] = useMemo(() => { |   const tableColumns: TableColumn[] = useMemo(() => { | ||||||
|     return [ |     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 [ | ||||||
|  |       <AddItemButton | ||||||
|  |         tooltip={t`Add Part Category`} | ||||||
|  |         onClick={addCategory} | ||||||
|  |         disabled={!can_add} | ||||||
|  |       /> | ||||||
|  |     ]; | ||||||
|  |   }, [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 ( |   return ( | ||||||
|     <InvenTreeTable |     <InvenTreeTable | ||||||
|       url={apiUrl(ApiPaths.category_list)} |       url={apiUrl(ApiPaths.category_list)} | ||||||
| @@ -71,11 +134,12 @@ export function PartCategoryTable({ params = {} }: { params?: any }) { | |||||||
|       columns={tableColumns} |       columns={tableColumns} | ||||||
|       props={{ |       props={{ | ||||||
|         enableDownload: true, |         enableDownload: true, | ||||||
|         enableSelection: true, |  | ||||||
|         params: { |         params: { | ||||||
|           ...params |           parent: parentId ?? 'null' | ||||||
|         }, |         }, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|  |         tableActions: tableActions, | ||||||
|  |         rowActions: rowActions, | ||||||
|         onRowClick: (record, index, event) => { |         onRowClick: (record, index, event) => { | ||||||
|           navigate(`/part/category/${record.pk}`); |           navigate(`/part/category/${record.pk}`); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -180,8 +180,8 @@ export function PartParameterTable({ partId }: { partId: any }) { | |||||||
|       columns={tableColumns} |       columns={tableColumns} | ||||||
|       props={{ |       props={{ | ||||||
|         rowActions: rowActions, |         rowActions: rowActions, | ||||||
|         customActionGroups: tableActions, |         tableActions: tableActions, | ||||||
|         customFilters: [ |         tableFilters: [ | ||||||
|           { |           { | ||||||
|             name: 'include_variants', |             name: 'include_variants', | ||||||
|             label: t`Include Variants`, |             label: t`Include Variants`, | ||||||
|   | |||||||
| @@ -131,8 +131,8 @@ export default function PartParameterTemplateTable() { | |||||||
|       columns={tableColumns} |       columns={tableColumns} | ||||||
|       props={{ |       props={{ | ||||||
|         rowActions: rowActions, |         rowActions: rowActions, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|         customActionGroups: tableActions |         tableActions: tableActions | ||||||
|       }} |       }} | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -275,7 +275,7 @@ export function PartListTable({ props }: { props: InvenTreeTableProps }) { | |||||||
|       props={{ |       props={{ | ||||||
|         ...props, |         ...props, | ||||||
|         enableDownload: true, |         enableDownload: true, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|         params: { |         params: { | ||||||
|           ...props.params, |           ...props.params, | ||||||
|           category_detail: true |           category_detail: true | ||||||
|   | |||||||
| @@ -140,8 +140,8 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) { | |||||||
|         params: { |         params: { | ||||||
|           part: partId |           part: partId | ||||||
|         }, |         }, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|         customActionGroups: tableActions, |         tableActions: tableActions, | ||||||
|         rowActions: rowActions |         rowActions: rowActions | ||||||
|       }} |       }} | ||||||
|     /> |     /> | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ export function PartVariantTable({ partId }: { partId: string }) { | |||||||
|     <PartListTable |     <PartListTable | ||||||
|       props={{ |       props={{ | ||||||
|         enableDownload: false, |         enableDownload: false, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|         params: { |         params: { | ||||||
|           ancestor: partId |           ancestor: partId | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -133,7 +133,7 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode { | |||||||
|           category_detail: true |           category_detail: true | ||||||
|         }, |         }, | ||||||
|         rowActions: rowActions, |         rowActions: rowActions, | ||||||
|         customActionGroups: customActions |         tableActions: customActions | ||||||
|       }} |       }} | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -493,8 +493,8 @@ export function PluginListTable({ props }: { props: InvenTreeTableProps }) { | |||||||
|           }, |           }, | ||||||
|           rowActions: rowActions, |           rowActions: rowActions, | ||||||
|           onRowClick: (plugin) => navigate(`${plugin.pk}/`), |           onRowClick: (plugin) => navigate(`${plugin.pk}/`), | ||||||
|           customActionGroups: tableActions, |           tableActions: tableActions, | ||||||
|           customFilters: [ |           tableFilters: [ | ||||||
|             { |             { | ||||||
|               name: 'active', |               name: 'active', | ||||||
|               label: t`Active`, |               label: t`Active`, | ||||||
|   | |||||||
| @@ -5,9 +5,11 @@ import { ApiPaths } from '../../../enums/ApiEndpoints'; | |||||||
| import { UserRoles } from '../../../enums/Roles'; | import { UserRoles } from '../../../enums/Roles'; | ||||||
| import { useManufacturerPartFields } from '../../../forms/CompanyForms'; | import { useManufacturerPartFields } from '../../../forms/CompanyForms'; | ||||||
| import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms'; | import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms'; | ||||||
|  | import { notYetImplemented } from '../../../functions/notifications'; | ||||||
| import { useTable } from '../../../hooks/UseTable'; | import { useTable } from '../../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../../states/ApiState'; | import { apiUrl } from '../../../states/ApiState'; | ||||||
| import { useUserState } from '../../../states/UserState'; | import { useUserState } from '../../../states/UserState'; | ||||||
|  | import { AddItemButton } from '../../buttons/AddItemButton'; | ||||||
| import { Thumbnail } from '../../images/Thumbnail'; | import { Thumbnail } from '../../images/Thumbnail'; | ||||||
| import { TableColumn } from '../Column'; | import { TableColumn } from '../Column'; | ||||||
| import { DescriptionColumn, LinkColumn, PartColumn } from '../ColumnRenderers'; | import { DescriptionColumn, LinkColumn, PartColumn } from '../ColumnRenderers'; | ||||||
| @@ -57,9 +59,22 @@ export function ManufacturerPartTable({ params }: { params: any }): ReactNode { | |||||||
|     ]; |     ]; | ||||||
|   }, [params]); |   }, [params]); | ||||||
|  |  | ||||||
|  |   const addManufacturerPart = useCallback(() => { | ||||||
|  |     notYetImplemented(); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|   const tableActions = useMemo(() => { |   const tableActions = useMemo(() => { | ||||||
|     // TODO: Custom actions |     let can_add = | ||||||
|     return []; |       user.hasAddRole(UserRoles.purchase_order) && | ||||||
|  |       user.hasAddRole(UserRoles.part); | ||||||
|  |  | ||||||
|  |     return [ | ||||||
|  |       <AddItemButton | ||||||
|  |         tooltip={t`Add Manufacturer Part`} | ||||||
|  |         onClick={addManufacturerPart} | ||||||
|  |         hidden={!can_add} | ||||||
|  |       /> | ||||||
|  |     ]; | ||||||
|   }, [user]); |   }, [user]); | ||||||
|  |  | ||||||
|   const editManufacturerPartFields = useManufacturerPartFields(); |   const editManufacturerPartFields = useManufacturerPartFields(); | ||||||
| @@ -112,7 +127,7 @@ export function ManufacturerPartTable({ params }: { params: any }): ReactNode { | |||||||
|           manufacturer_detail: true |           manufacturer_detail: true | ||||||
|         }, |         }, | ||||||
|         rowActions: rowActions, |         rowActions: rowActions, | ||||||
|         customActionGroups: tableActions |         tableActions: tableActions | ||||||
|       }} |       }} | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -260,7 +260,7 @@ export function PurchaseOrderLineItemTable({ | |||||||
|           part_detail: true |           part_detail: true | ||||||
|         }, |         }, | ||||||
|         rowActions: rowActions, |         rowActions: rowActions, | ||||||
|         customActionGroups: tableActions |         tableActions: tableActions | ||||||
|       }} |       }} | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -1,11 +1,15 @@ | |||||||
| import { t } from '@lingui/macro'; | import { t } from '@lingui/macro'; | ||||||
| import { useMemo } from 'react'; | import { useCallback, useMemo } from 'react'; | ||||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||||
| import { ModelType } from '../../../enums/ModelType'; | import { ModelType } from '../../../enums/ModelType'; | ||||||
|  | import { UserRoles } from '../../../enums/Roles'; | ||||||
|  | import { notYetImplemented } from '../../../functions/notifications'; | ||||||
| import { useTable } from '../../../hooks/UseTable'; | import { useTable } from '../../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../../states/ApiState'; | import { apiUrl } from '../../../states/ApiState'; | ||||||
|  | import { useUserState } from '../../../states/UserState'; | ||||||
|  | import { AddItemButton } from '../../buttons/AddItemButton'; | ||||||
| import { Thumbnail } from '../../images/Thumbnail'; | import { Thumbnail } from '../../images/Thumbnail'; | ||||||
| import { | import { | ||||||
|   CreationDateColumn, |   CreationDateColumn, | ||||||
| @@ -33,6 +37,7 @@ export function PurchaseOrderTable({ params }: { params?: any }) { | |||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|  |  | ||||||
|   const table = useTable('purchase-order'); |   const table = useTable('purchase-order'); | ||||||
|  |   const user = useUserState(); | ||||||
|  |  | ||||||
|   const tableFilters: TableFilter[] = useMemo(() => { |   const tableFilters: TableFilter[] = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
| @@ -94,6 +99,20 @@ export function PurchaseOrderTable({ params }: { params?: any }) { | |||||||
|     ]; |     ]; | ||||||
|   }, []); |   }, []); | ||||||
|  |  | ||||||
|  |   const addPurchaseOrder = useCallback(() => { | ||||||
|  |     notYetImplemented(); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|  |   const tableActions = useMemo(() => { | ||||||
|  |     return [ | ||||||
|  |       <AddItemButton | ||||||
|  |         tooltip={t`Add Purchase Order`} | ||||||
|  |         onClick={addPurchaseOrder} | ||||||
|  |         hidden={!user.hasAddRole(UserRoles.purchase_order)} | ||||||
|  |       /> | ||||||
|  |     ]; | ||||||
|  |   }, [user]); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <InvenTreeTable |     <InvenTreeTable | ||||||
|       url={apiUrl(ApiPaths.purchase_order_list)} |       url={apiUrl(ApiPaths.purchase_order_list)} | ||||||
| @@ -104,7 +123,8 @@ export function PurchaseOrderTable({ params }: { params?: any }) { | |||||||
|           ...params, |           ...params, | ||||||
|           supplier_detail: true |           supplier_detail: true | ||||||
|         }, |         }, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|  |         tableActions: tableActions, | ||||||
|         onRowClick: (row: any) => { |         onRowClick: (row: any) => { | ||||||
|           if (row.pk) { |           if (row.pk) { | ||||||
|             navigate(`/purchasing/purchase-order/${row.pk}`); |             navigate(`/purchasing/purchase-order/${row.pk}`); | ||||||
|   | |||||||
| @@ -229,7 +229,7 @@ export function SupplierPartTable({ params }: { params: any }): ReactNode { | |||||||
|             manufacturer_detail: true |             manufacturer_detail: true | ||||||
|           }, |           }, | ||||||
|           rowActions: rowActions, |           rowActions: rowActions, | ||||||
|           customActionGroups: tableActions |           tableActions: tableActions | ||||||
|         }} |         }} | ||||||
|       /> |       /> | ||||||
|     </> |     </> | ||||||
|   | |||||||
| @@ -1,11 +1,15 @@ | |||||||
| import { t } from '@lingui/macro'; | import { t } from '@lingui/macro'; | ||||||
| import { useMemo } from 'react'; | import { useCallback, useMemo } from 'react'; | ||||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||||
| import { ModelType } from '../../../enums/ModelType'; | import { ModelType } from '../../../enums/ModelType'; | ||||||
|  | import { UserRoles } from '../../../enums/Roles'; | ||||||
|  | import { notYetImplemented } from '../../../functions/notifications'; | ||||||
| import { useTable } from '../../../hooks/UseTable'; | import { useTable } from '../../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../../states/ApiState'; | import { apiUrl } from '../../../states/ApiState'; | ||||||
|  | import { useUserState } from '../../../states/UserState'; | ||||||
|  | import { AddItemButton } from '../../buttons/AddItemButton'; | ||||||
| import { Thumbnail } from '../../images/Thumbnail'; | import { Thumbnail } from '../../images/Thumbnail'; | ||||||
| import { | import { | ||||||
|   CreationDateColumn, |   CreationDateColumn, | ||||||
| @@ -27,6 +31,7 @@ import { InvenTreeTable } from '../InvenTreeTable'; | |||||||
|  |  | ||||||
| export function ReturnOrderTable({ params }: { params?: any }) { | export function ReturnOrderTable({ params }: { params?: any }) { | ||||||
|   const table = useTable('return-orders'); |   const table = useTable('return-orders'); | ||||||
|  |   const user = useUserState(); | ||||||
|  |  | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|  |  | ||||||
| @@ -90,6 +95,20 @@ export function ReturnOrderTable({ params }: { params?: any }) { | |||||||
|     ]; |     ]; | ||||||
|   }, []); |   }, []); | ||||||
|  |  | ||||||
|  |   const addReturnOrder = useCallback(() => { | ||||||
|  |     notYetImplemented(); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|  |   const tableActions = useMemo(() => { | ||||||
|  |     return [ | ||||||
|  |       <AddItemButton | ||||||
|  |         tooltip={t`Add Return Order`} | ||||||
|  |         onClick={addReturnOrder} | ||||||
|  |         hidden={!user.hasAddRole(UserRoles.sales_order)} | ||||||
|  |       /> | ||||||
|  |     ]; | ||||||
|  |   }, [user]); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <InvenTreeTable |     <InvenTreeTable | ||||||
|       url={apiUrl(ApiPaths.return_order_list)} |       url={apiUrl(ApiPaths.return_order_list)} | ||||||
| @@ -100,7 +119,8 @@ export function ReturnOrderTable({ params }: { params?: any }) { | |||||||
|           ...params, |           ...params, | ||||||
|           customer_detail: true |           customer_detail: true | ||||||
|         }, |         }, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|  |         tableActions: tableActions, | ||||||
|         onRowClick: (row: any) => { |         onRowClick: (row: any) => { | ||||||
|           if (row.pk) { |           if (row.pk) { | ||||||
|             navigate(`/sales/return-order/${row.pk}/`); |             navigate(`/sales/return-order/${row.pk}/`); | ||||||
|   | |||||||
| @@ -1,11 +1,15 @@ | |||||||
| import { t } from '@lingui/macro'; | import { t } from '@lingui/macro'; | ||||||
| import { useMemo } from 'react'; | import { useCallback, useMemo } from 'react'; | ||||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||||
| import { ModelType } from '../../../enums/ModelType'; | import { ModelType } from '../../../enums/ModelType'; | ||||||
|  | import { UserRoles } from '../../../enums/Roles'; | ||||||
|  | import { notYetImplemented } from '../../../functions/notifications'; | ||||||
| import { useTable } from '../../../hooks/UseTable'; | import { useTable } from '../../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../../states/ApiState'; | import { apiUrl } from '../../../states/ApiState'; | ||||||
|  | import { useUserState } from '../../../states/UserState'; | ||||||
|  | import { AddItemButton } from '../../buttons/AddItemButton'; | ||||||
| import { Thumbnail } from '../../images/Thumbnail'; | import { Thumbnail } from '../../images/Thumbnail'; | ||||||
| import { | import { | ||||||
|   CreationDateColumn, |   CreationDateColumn, | ||||||
| @@ -28,6 +32,7 @@ import { InvenTreeTable } from '../InvenTreeTable'; | |||||||
|  |  | ||||||
| export function SalesOrderTable({ params }: { params?: any }) { | export function SalesOrderTable({ params }: { params?: any }) { | ||||||
|   const table = useTable('sales-order'); |   const table = useTable('sales-order'); | ||||||
|  |   const user = useUserState(); | ||||||
|  |  | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|  |  | ||||||
| @@ -91,6 +96,20 @@ export function SalesOrderTable({ params }: { params?: any }) { | |||||||
|     ]; |     ]; | ||||||
|   }, []); |   }, []); | ||||||
|  |  | ||||||
|  |   const addSalesOrder = useCallback(() => { | ||||||
|  |     notYetImplemented(); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|  |   const tableActions = useMemo(() => { | ||||||
|  |     return [ | ||||||
|  |       <AddItemButton | ||||||
|  |         tooltip={t`Add Sales Order`} | ||||||
|  |         onClick={addSalesOrder} | ||||||
|  |         hidden={!user.hasAddRole(UserRoles.sales_order)} | ||||||
|  |       /> | ||||||
|  |     ]; | ||||||
|  |   }, [user]); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <InvenTreeTable |     <InvenTreeTable | ||||||
|       url={apiUrl(ApiPaths.sales_order_list)} |       url={apiUrl(ApiPaths.sales_order_list)} | ||||||
| @@ -101,7 +120,8 @@ export function SalesOrderTable({ params }: { params?: any }) { | |||||||
|           ...params, |           ...params, | ||||||
|           customer_detail: true |           customer_detail: true | ||||||
|         }, |         }, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|  |         tableActions: tableActions, | ||||||
|         onRowClick: (row: any) => { |         onRowClick: (row: any) => { | ||||||
|           if (row.pk) { |           if (row.pk) { | ||||||
|             navigate(`/sales/sales-order/${row.pk}/`); |             navigate(`/sales/sales-order/${row.pk}/`); | ||||||
|   | |||||||
| @@ -66,7 +66,7 @@ export function CurrencyTable() { | |||||||
|       tableState={table} |       tableState={table} | ||||||
|       columns={columns} |       columns={columns} | ||||||
|       props={{ |       props={{ | ||||||
|         customActionGroups: tableActions, |         tableActions: tableActions, | ||||||
|         dataFormatter: (data) => { |         dataFormatter: (data) => { | ||||||
|           let rates = data?.exchange_rates ?? {}; |           let rates = data?.exchange_rates ?? {}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ export default function CustomUnitsTable() { | |||||||
|       columns={columns} |       columns={columns} | ||||||
|       props={{ |       props={{ | ||||||
|         rowActions: rowActions, |         rowActions: rowActions, | ||||||
|         customActionGroups: tableActions |         tableActions: tableActions | ||||||
|       }} |       }} | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -166,7 +166,7 @@ export function GroupTable() { | |||||||
|         columns={columns} |         columns={columns} | ||||||
|         props={{ |         props={{ | ||||||
|           rowActions: rowActions, |           rowActions: rowActions, | ||||||
|           customActionGroups: tableActions, |           tableActions: tableActions, | ||||||
|           onRowClick: (record) => openDetailDrawer(record.pk) |           onRowClick: (record) => openDetailDrawer(record.pk) | ||||||
|         }} |         }} | ||||||
|       /> |       /> | ||||||
|   | |||||||
| @@ -106,7 +106,7 @@ export default function ProjectCodeTable() { | |||||||
|       columns={columns} |       columns={columns} | ||||||
|       props={{ |       props={{ | ||||||
|         rowActions: rowActions, |         rowActions: rowActions, | ||||||
|         customActionGroups: tableActions |         tableActions: tableActions | ||||||
|       }} |       }} | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -261,7 +261,7 @@ export function UserTable() { | |||||||
|         columns={columns} |         columns={columns} | ||||||
|         props={{ |         props={{ | ||||||
|           rowActions: rowActions, |           rowActions: rowActions, | ||||||
|           customActionGroups: tableActions, |           tableActions: tableActions, | ||||||
|           onRowClick: (record) => openDetailDrawer(record.pk) |           onRowClick: (record) => openDetailDrawer(record.pk) | ||||||
|         }} |         }} | ||||||
|       /> |       /> | ||||||
|   | |||||||
| @@ -344,7 +344,7 @@ export function StockItemTable({ params = {} }: { params?: any }) { | |||||||
|       props={{ |       props={{ | ||||||
|         enableDownload: true, |         enableDownload: true, | ||||||
|         enableSelection: true, |         enableSelection: true, | ||||||
|         customFilters: tableFilters, |         tableFilters: tableFilters, | ||||||
|         onRowClick: (record) => navigate(`/stock/item/${record.pk}`), |         onRowClick: (record) => navigate(`/stock/item/${record.pk}`), | ||||||
|         params: { |         params: { | ||||||
|           ...params, |           ...params, | ||||||
|   | |||||||
| @@ -1,21 +1,28 @@ | |||||||
| import { t } from '@lingui/macro'; | import { t } from '@lingui/macro'; | ||||||
| import { useMemo } from 'react'; | import { useCallback, useMemo } from 'react'; | ||||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||||
|  | import { UserRoles } from '../../../enums/Roles'; | ||||||
|  | import { stockLocationFields } from '../../../forms/StockForms'; | ||||||
|  | import { openCreateApiForm, openEditApiForm } from '../../../functions/forms'; | ||||||
| import { useTable } from '../../../hooks/UseTable'; | import { useTable } from '../../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../../states/ApiState'; | import { apiUrl } from '../../../states/ApiState'; | ||||||
|  | import { useUserState } from '../../../states/UserState'; | ||||||
|  | import { AddItemButton } from '../../buttons/AddItemButton'; | ||||||
| import { YesNoButton } from '../../items/YesNoButton'; | import { YesNoButton } from '../../items/YesNoButton'; | ||||||
| import { TableColumn } from '../Column'; | import { TableColumn } from '../Column'; | ||||||
| import { DescriptionColumn } from '../ColumnRenderers'; | import { DescriptionColumn } from '../ColumnRenderers'; | ||||||
| import { TableFilter } from '../Filter'; | import { TableFilter } from '../Filter'; | ||||||
| import { InvenTreeTable } from '../InvenTreeTable'; | import { InvenTreeTable } from '../InvenTreeTable'; | ||||||
|  | import { RowEditAction } from '../RowActions'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Stock location table |  * Stock location table | ||||||
|  */ |  */ | ||||||
| export function StockLocationTable({ params = {} }: { params?: any }) { | export function StockLocationTable({ parentId }: { parentId?: any }) { | ||||||
|   const table = useTable('stocklocation'); |   const table = useTable('stocklocation'); | ||||||
|  |   const user = useUserState(); | ||||||
|  |  | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|  |  | ||||||
| @@ -85,7 +92,63 @@ export function StockLocationTable({ params = {} }: { params?: any }) { | |||||||
|         render: (record: any) => record.location_type_detail?.name |         render: (record: any) => record.location_type_detail?.name | ||||||
|       } |       } | ||||||
|     ]; |     ]; | ||||||
|   }, [params]); |   }, []); | ||||||
|  |  | ||||||
|  |   const addLocation = useCallback(() => { | ||||||
|  |     let fields = stockLocationFields({}); | ||||||
|  |  | ||||||
|  |     if (parentId) { | ||||||
|  |       fields['parent'].value = parentId; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     openCreateApiForm({ | ||||||
|  |       url: apiUrl(ApiPaths.stock_location_list), | ||||||
|  |       title: t`Add Stock Location`, | ||||||
|  |       fields: fields, | ||||||
|  |       onFormSuccess(data: any) { | ||||||
|  |         if (data.pk) { | ||||||
|  |           navigate(`/stock/location/${data.pk}`); | ||||||
|  |         } else { | ||||||
|  |           table.refreshTable(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   }, [parentId]); | ||||||
|  |  | ||||||
|  |   const tableActions = useMemo(() => { | ||||||
|  |     let can_add = user.hasAddRole(UserRoles.stock_location); | ||||||
|  |  | ||||||
|  |     return [ | ||||||
|  |       <AddItemButton | ||||||
|  |         tooltip={t`Add Stock Location`} | ||||||
|  |         onClick={addLocation} | ||||||
|  |         disabled={!can_add} | ||||||
|  |       /> | ||||||
|  |     ]; | ||||||
|  |   }, [user]); | ||||||
|  |  | ||||||
|  |   const rowActions = useCallback( | ||||||
|  |     (record: any) => { | ||||||
|  |       let can_edit = user.hasChangeRole(UserRoles.stock_location); | ||||||
|  |  | ||||||
|  |       return [ | ||||||
|  |         RowEditAction({ | ||||||
|  |           hidden: !can_edit, | ||||||
|  |           onClick: () => { | ||||||
|  |             openEditApiForm({ | ||||||
|  |               url: ApiPaths.stock_location_list, | ||||||
|  |               pk: record.pk, | ||||||
|  |               title: t`Edit Stock Location`, | ||||||
|  |               fields: stockLocationFields({}), | ||||||
|  |               successMessage: t`Stock location updated`, | ||||||
|  |               onFormSuccess: table.refreshTable | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |       ]; | ||||||
|  |     }, | ||||||
|  |     [user] | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <InvenTreeTable |     <InvenTreeTable | ||||||
| @@ -94,8 +157,12 @@ export function StockLocationTable({ params = {} }: { params?: any }) { | |||||||
|       columns={tableColumns} |       columns={tableColumns} | ||||||
|       props={{ |       props={{ | ||||||
|         enableDownload: true, |         enableDownload: true, | ||||||
|         params: params, |         params: { | ||||||
|         customFilters: tableFilters, |           parent: parentId ?? 'null' | ||||||
|  |         }, | ||||||
|  |         tableFilters: tableFilters, | ||||||
|  |         tableActions: tableActions, | ||||||
|  |         rowActions: rowActions, | ||||||
|         onRowClick: (record) => { |         onRowClick: (record) => { | ||||||
|           navigate(`/stock/location/${record.pk}`); |           navigate(`/stock/location/${record.pk}`); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -133,3 +133,19 @@ export function useEditStockItem({ | |||||||
|     onFormSuccess: callback |     onFormSuccess: callback | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function stockLocationFields({}: {}): ApiFormFieldSet { | ||||||
|  |   let fields: ApiFormFieldSet = { | ||||||
|  |     parent: { | ||||||
|  |       description: t`Parent stock location`, | ||||||
|  |       required: false | ||||||
|  |     }, | ||||||
|  |     name: {}, | ||||||
|  |     description: {}, | ||||||
|  |     structural: {}, | ||||||
|  |     external: {}, | ||||||
|  |     location_type: {} | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return fields; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -64,13 +64,7 @@ export default function CategoryDetail({}: {}) { | |||||||
|         name: 'subcategories', |         name: 'subcategories', | ||||||
|         label: t`Part Categories`, |         label: t`Part Categories`, | ||||||
|         icon: <IconSitemap />, |         icon: <IconSitemap />, | ||||||
|         content: ( |         content: <PartCategoryTable parentId={id} /> | ||||||
|           <PartCategoryTable |  | ||||||
|             params={{ |  | ||||||
|               parent: id |  | ||||||
|             }} |  | ||||||
|           /> |  | ||||||
|         ) |  | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         name: 'parameters', |         name: 'parameters', | ||||||
|   | |||||||
| @@ -53,13 +53,7 @@ export default function Stock() { | |||||||
|         name: 'sublocations', |         name: 'sublocations', | ||||||
|         label: t`Stock Locations`, |         label: t`Stock Locations`, | ||||||
|         icon: <IconSitemap />, |         icon: <IconSitemap />, | ||||||
|         content: ( |         content: <StockLocationTable parentId={id} /> | ||||||
|           <StockLocationTable |  | ||||||
|             params={{ |  | ||||||
|               parent: id |  | ||||||
|             }} |  | ||||||
|           /> |  | ||||||
|         ) |  | ||||||
|       } |       } | ||||||
|     ]; |     ]; | ||||||
|   }, [location, id]); |   }, [location, id]); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user