mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Admin tweaks (#7248)
* Update admin site - Implement 'autocomplete' for more fields - Improves admin loading time * Add "admin" buttons to the PUI interface * Only allow superuser access
This commit is contained in:
parent
acb1ec4c83
commit
dc741b6183
@ -7,6 +7,15 @@ from import_export.admin import ImportExportModelAdmin
|
|||||||
import common.models
|
import common.models
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(common.models.ProjectCode)
|
||||||
|
class ProjectCodeAdmin(ImportExportModelAdmin):
|
||||||
|
"""Admin settings for ProjectCode."""
|
||||||
|
|
||||||
|
list_display = ('code', 'description')
|
||||||
|
|
||||||
|
search_fields = ('code', 'description')
|
||||||
|
|
||||||
|
|
||||||
class SettingsAdmin(ImportExportModelAdmin):
|
class SettingsAdmin(ImportExportModelAdmin):
|
||||||
"""Admin settings for InvenTreeSetting."""
|
"""Admin settings for InvenTreeSetting."""
|
||||||
|
|
||||||
|
@ -213,6 +213,8 @@ class AddressAdmin(ImportExportModelAdmin):
|
|||||||
|
|
||||||
search_fields = ['company', 'country', 'postal_code']
|
search_fields = ['company', 'country', 'postal_code']
|
||||||
|
|
||||||
|
autocomplete_fields = ['company']
|
||||||
|
|
||||||
|
|
||||||
class ContactResource(InvenTreeResource):
|
class ContactResource(InvenTreeResource):
|
||||||
"""Class for managing Contact data import/export."""
|
"""Class for managing Contact data import/export."""
|
||||||
@ -237,3 +239,5 @@ class ContactAdmin(ImportExportModelAdmin):
|
|||||||
list_display = ('company', 'name', 'role', 'email', 'phone')
|
list_display = ('company', 'name', 'role', 'email', 'phone')
|
||||||
|
|
||||||
search_fields = ['company', 'name', 'email']
|
search_fields = ['company', 'name', 'email']
|
||||||
|
|
||||||
|
autocomplete_fields = ['company']
|
||||||
|
@ -114,7 +114,7 @@ class PurchaseOrderAdmin(ImportExportModelAdmin):
|
|||||||
|
|
||||||
inlines = [PurchaseOrderLineItemInlineAdmin]
|
inlines = [PurchaseOrderLineItemInlineAdmin]
|
||||||
|
|
||||||
autocomplete_fields = ('supplier',)
|
autocomplete_fields = ['supplier', 'project_code', 'contact', 'address']
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderResource(
|
class SalesOrderResource(
|
||||||
@ -152,7 +152,7 @@ class SalesOrderAdmin(ImportExportModelAdmin):
|
|||||||
|
|
||||||
search_fields = ['reference', 'customer__name', 'description']
|
search_fields = ['reference', 'customer__name', 'description']
|
||||||
|
|
||||||
autocomplete_fields = ('customer',)
|
autocomplete_fields = ['customer', 'project_code', 'contact', 'address']
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderLineItemResource(PriceResourceMixin, InvenTreeResource):
|
class PurchaseOrderLineItemResource(PriceResourceMixin, InvenTreeResource):
|
||||||
@ -317,7 +317,7 @@ class ReturnOrderAdmin(ImportExportModelAdmin):
|
|||||||
|
|
||||||
search_fields = ['reference', 'customer__name', 'description']
|
search_fields = ['reference', 'customer__name', 'description']
|
||||||
|
|
||||||
autocomplete_fields = ['customer']
|
autocomplete_fields = ['customer', 'project_code', 'contact', 'address']
|
||||||
|
|
||||||
|
|
||||||
class ReturnOrderLineItemResource(PriceResourceMixin, InvenTreeResource):
|
class ReturnOrderLineItemResource(PriceResourceMixin, InvenTreeResource):
|
||||||
|
@ -250,6 +250,8 @@ class PartAdmin(ImportExportModelAdmin):
|
|||||||
'category',
|
'category',
|
||||||
'default_location',
|
'default_location',
|
||||||
'default_supplier',
|
'default_supplier',
|
||||||
|
'bom_checked_by',
|
||||||
|
'creation_user',
|
||||||
]
|
]
|
||||||
|
|
||||||
inlines = [PartParameterInline]
|
inlines = [PartParameterInline]
|
||||||
@ -260,7 +262,7 @@ class PartPricingAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ('part', 'overall_min', 'overall_max')
|
list_display = ('part', 'overall_min', 'overall_max')
|
||||||
|
|
||||||
autcomplete_fields = ['part']
|
autocomplete_fields = ['part']
|
||||||
|
|
||||||
|
|
||||||
class PartStocktakeAdmin(admin.ModelAdmin):
|
class PartStocktakeAdmin(admin.ModelAdmin):
|
||||||
|
@ -292,6 +292,7 @@ class StockItemAdmin(ImportExportModelAdmin):
|
|||||||
'sales_order',
|
'sales_order',
|
||||||
'stocktake_user',
|
'stocktake_user',
|
||||||
'supplier_part',
|
'supplier_part',
|
||||||
|
'consumed_by',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@ import { ReactNode } from 'react';
|
|||||||
import { notYetImplemented } from '../../functions/notifications';
|
import { notYetImplemented } from '../../functions/notifications';
|
||||||
|
|
||||||
export type ActionButtonProps = {
|
export type ActionButtonProps = {
|
||||||
key?: string;
|
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
text?: string;
|
text?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
variant?: string;
|
variant?: string;
|
||||||
size?: number | string;
|
size?: number | string;
|
||||||
|
radius?: number | string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onClick?: any;
|
onClick?: any;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
@ -26,15 +26,16 @@ export function ActionButton(props: ActionButtonProps) {
|
|||||||
return (
|
return (
|
||||||
!hidden && (
|
!hidden && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
key={`tooltip-${props.key}`}
|
key={`tooltip-${props.text}`}
|
||||||
disabled={!props.tooltip && !props.text}
|
disabled={!props.tooltip && !props.text}
|
||||||
label={props.tooltip ?? props.text}
|
label={props.tooltip ?? props.text}
|
||||||
position={props.tooltipAlignment ?? 'left'}
|
position={props.tooltipAlignment ?? 'left'}
|
||||||
>
|
>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
key={`action-icon-${props.key}`}
|
key={`action-icon-${props.text}`}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
radius="xs"
|
p={17}
|
||||||
|
radius={props.radius ?? 'xs'}
|
||||||
color={props.color}
|
color={props.color}
|
||||||
size={props.size}
|
size={props.size}
|
||||||
onClick={props.onClick ?? notYetImplemented}
|
onClick={props.onClick ?? notYetImplemented}
|
||||||
|
88
src/frontend/src/components/buttons/AdminButton.tsx
Normal file
88
src/frontend/src/components/buttons/AdminButton.tsx
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { IconUserStar } from '@tabler/icons-react';
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
|
import { navigateToLink } from '../../functions/navigation';
|
||||||
|
import { base_url } from '../../main';
|
||||||
|
import { useLocalState } from '../../states/LocalState';
|
||||||
|
import { useUserState } from '../../states/UserState';
|
||||||
|
import { ModelInformationDict } from '../render/ModelType';
|
||||||
|
import { ActionButton } from './ActionButton';
|
||||||
|
|
||||||
|
export type AdminButtonProps = {
|
||||||
|
model: ModelType;
|
||||||
|
pk: number | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A button that is used to navigate to the admin page for the selected item.
|
||||||
|
*
|
||||||
|
* This button is only rendered if:
|
||||||
|
* - The admin interface is enabled for the server
|
||||||
|
* - The selected model has an associated admin URL
|
||||||
|
* - The user has "superuser" role
|
||||||
|
* - The user has at least read rights for the selected item
|
||||||
|
*/
|
||||||
|
export default function AdminButton(props: AdminButtonProps) {
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
|
const enabled: boolean = useMemo(() => {
|
||||||
|
// Only users with superuser permission will see this button
|
||||||
|
if (!user || !user.isLoggedIn() || !user.isSuperuser()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if the server has the admin interface enabled
|
||||||
|
|
||||||
|
const modelDef = ModelInformationDict[props.model];
|
||||||
|
|
||||||
|
// No admin URL associated with the model
|
||||||
|
if (!modelDef.admin_url) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No primary key provided
|
||||||
|
if (!props.pk) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}, [user, props.model, props.pk]);
|
||||||
|
|
||||||
|
const openAdmin = useCallback(
|
||||||
|
(event: any) => {
|
||||||
|
const modelDef = ModelInformationDict[props.model];
|
||||||
|
const host = useLocalState.getState().host;
|
||||||
|
|
||||||
|
if (!modelDef.admin_url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check the actual "admin" URL (it may be custom)
|
||||||
|
const url = `${host}/admin${modelDef.admin_url}${props.pk}/`;
|
||||||
|
|
||||||
|
if (event?.ctrlKey || event?.shiftKey) {
|
||||||
|
// Open the link in a new tab
|
||||||
|
window.open(url, '_blank');
|
||||||
|
} else {
|
||||||
|
window.open(url, '_self');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[props.model, props.pk]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionButton
|
||||||
|
icon={<IconUserStar />}
|
||||||
|
color="blue"
|
||||||
|
size="lg"
|
||||||
|
radius="sm"
|
||||||
|
variant="filled"
|
||||||
|
tooltip={t`Open in admin interface`}
|
||||||
|
hidden={!enabled}
|
||||||
|
onClick={openAdmin}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -10,6 +10,7 @@ export interface ModelInformationInterface {
|
|||||||
url_detail?: string;
|
url_detail?: string;
|
||||||
api_endpoint: ApiEndpoints;
|
api_endpoint: ApiEndpoints;
|
||||||
cui_detail?: string;
|
cui_detail?: string;
|
||||||
|
admin_url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ModelDict = {
|
export type ModelDict = {
|
||||||
@ -23,7 +24,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/part',
|
url_overview: '/part',
|
||||||
url_detail: '/part/:pk/',
|
url_detail: '/part/:pk/',
|
||||||
cui_detail: '/part/:pk/',
|
cui_detail: '/part/:pk/',
|
||||||
api_endpoint: ApiEndpoints.part_list
|
api_endpoint: ApiEndpoints.part_list,
|
||||||
|
admin_url: '/part/part/'
|
||||||
},
|
},
|
||||||
partparametertemplate: {
|
partparametertemplate: {
|
||||||
label: t`Part Parameter Template`,
|
label: t`Part Parameter Template`,
|
||||||
@ -45,7 +47,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/supplierpart',
|
url_overview: '/supplierpart',
|
||||||
url_detail: '/purchasing/supplier-part/:pk/',
|
url_detail: '/purchasing/supplier-part/:pk/',
|
||||||
cui_detail: '/supplier-part/:pk/',
|
cui_detail: '/supplier-part/:pk/',
|
||||||
api_endpoint: ApiEndpoints.supplier_part_list
|
api_endpoint: ApiEndpoints.supplier_part_list,
|
||||||
|
admin_url: '/company/supplierpart/'
|
||||||
},
|
},
|
||||||
manufacturerpart: {
|
manufacturerpart: {
|
||||||
label: t`Manufacturer Part`,
|
label: t`Manufacturer Part`,
|
||||||
@ -53,7 +56,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/manufacturerpart',
|
url_overview: '/manufacturerpart',
|
||||||
url_detail: '/purchasing/manufacturer-part/:pk/',
|
url_detail: '/purchasing/manufacturer-part/:pk/',
|
||||||
cui_detail: '/manufacturer-part/:pk/',
|
cui_detail: '/manufacturer-part/:pk/',
|
||||||
api_endpoint: ApiEndpoints.manufacturer_part_list
|
api_endpoint: ApiEndpoints.manufacturer_part_list,
|
||||||
|
admin_url: '/company/manufacturerpart/'
|
||||||
},
|
},
|
||||||
partcategory: {
|
partcategory: {
|
||||||
label: t`Part Category`,
|
label: t`Part Category`,
|
||||||
@ -61,7 +65,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/part/category',
|
url_overview: '/part/category',
|
||||||
url_detail: '/part/category/:pk/',
|
url_detail: '/part/category/:pk/',
|
||||||
cui_detail: '/part/category/:pk/',
|
cui_detail: '/part/category/:pk/',
|
||||||
api_endpoint: ApiEndpoints.category_list
|
api_endpoint: ApiEndpoints.category_list,
|
||||||
|
admin_url: '/part/partcategory/'
|
||||||
},
|
},
|
||||||
stockitem: {
|
stockitem: {
|
||||||
label: t`Stock Item`,
|
label: t`Stock Item`,
|
||||||
@ -69,7 +74,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/stock/item',
|
url_overview: '/stock/item',
|
||||||
url_detail: '/stock/item/:pk/',
|
url_detail: '/stock/item/:pk/',
|
||||||
cui_detail: '/stock/item/:pk/',
|
cui_detail: '/stock/item/:pk/',
|
||||||
api_endpoint: ApiEndpoints.stock_item_list
|
api_endpoint: ApiEndpoints.stock_item_list,
|
||||||
|
admin_url: '/stock/stockitem/'
|
||||||
},
|
},
|
||||||
stocklocation: {
|
stocklocation: {
|
||||||
label: t`Stock Location`,
|
label: t`Stock Location`,
|
||||||
@ -77,7 +83,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/stock/location',
|
url_overview: '/stock/location',
|
||||||
url_detail: '/stock/location/:pk/',
|
url_detail: '/stock/location/:pk/',
|
||||||
cui_detail: '/stock/location/:pk/',
|
cui_detail: '/stock/location/:pk/',
|
||||||
api_endpoint: ApiEndpoints.stock_location_list
|
api_endpoint: ApiEndpoints.stock_location_list,
|
||||||
|
admin_url: '/stock/stocklocation/'
|
||||||
},
|
},
|
||||||
stocklocationtype: {
|
stocklocationtype: {
|
||||||
label: t`Stock Location Type`,
|
label: t`Stock Location Type`,
|
||||||
@ -95,7 +102,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/build',
|
url_overview: '/build',
|
||||||
url_detail: '/build/:pk/',
|
url_detail: '/build/:pk/',
|
||||||
cui_detail: '/build/:pk/',
|
cui_detail: '/build/:pk/',
|
||||||
api_endpoint: ApiEndpoints.build_order_list
|
api_endpoint: ApiEndpoints.build_order_list,
|
||||||
|
admin_url: '/build/build/'
|
||||||
},
|
},
|
||||||
buildline: {
|
buildline: {
|
||||||
label: t`Build Line`,
|
label: t`Build Line`,
|
||||||
@ -111,7 +119,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/company',
|
url_overview: '/company',
|
||||||
url_detail: '/company/:pk/',
|
url_detail: '/company/:pk/',
|
||||||
cui_detail: '/company/:pk/',
|
cui_detail: '/company/:pk/',
|
||||||
api_endpoint: ApiEndpoints.company_list
|
api_endpoint: ApiEndpoints.company_list,
|
||||||
|
admin_url: '/company/company/'
|
||||||
},
|
},
|
||||||
projectcode: {
|
projectcode: {
|
||||||
label: t`Project Code`,
|
label: t`Project Code`,
|
||||||
@ -126,7 +135,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/purchasing/purchase-order',
|
url_overview: '/purchasing/purchase-order',
|
||||||
url_detail: '/purchasing/purchase-order/:pk/',
|
url_detail: '/purchasing/purchase-order/:pk/',
|
||||||
cui_detail: '/order/purchase-order/:pk/',
|
cui_detail: '/order/purchase-order/:pk/',
|
||||||
api_endpoint: ApiEndpoints.purchase_order_list
|
api_endpoint: ApiEndpoints.purchase_order_list,
|
||||||
|
admin_url: '/order/purchaseorder/'
|
||||||
},
|
},
|
||||||
purchaseorderline: {
|
purchaseorderline: {
|
||||||
label: t`Purchase Order Line`,
|
label: t`Purchase Order Line`,
|
||||||
@ -139,7 +149,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/sales/sales-order',
|
url_overview: '/sales/sales-order',
|
||||||
url_detail: '/sales/sales-order/:pk/',
|
url_detail: '/sales/sales-order/:pk/',
|
||||||
cui_detail: '/order/sales-order/:pk/',
|
cui_detail: '/order/sales-order/:pk/',
|
||||||
api_endpoint: ApiEndpoints.sales_order_list
|
api_endpoint: ApiEndpoints.sales_order_list,
|
||||||
|
admin_url: '/order/salesorder/'
|
||||||
},
|
},
|
||||||
salesordershipment: {
|
salesordershipment: {
|
||||||
label: t`Sales Order Shipment`,
|
label: t`Sales Order Shipment`,
|
||||||
@ -154,7 +165,8 @@ export const ModelInformationDict: ModelDict = {
|
|||||||
url_overview: '/sales/return-order',
|
url_overview: '/sales/return-order',
|
||||||
url_detail: '/sales/return-order/:pk/',
|
url_detail: '/sales/return-order/:pk/',
|
||||||
cui_detail: '/order/return-order/:pk/',
|
cui_detail: '/order/return-order/:pk/',
|
||||||
api_endpoint: ApiEndpoints.return_order_list
|
api_endpoint: ApiEndpoints.return_order_list,
|
||||||
|
admin_url: '/order/returnorder/'
|
||||||
},
|
},
|
||||||
address: {
|
address: {
|
||||||
label: t`Address`,
|
label: t`Address`,
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
@ -347,8 +348,8 @@ export default function BuildDetail() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const buildActions = useMemo(() => {
|
const buildActions = useMemo(() => {
|
||||||
// TODO: Disable certain actions based on user permissions
|
|
||||||
return [
|
return [
|
||||||
|
<AdminButton model={ModelType.build} pk={build.pk} />,
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
key="barcode"
|
key="barcode"
|
||||||
tooltip={t`Barcode Actions`}
|
tooltip={t`Barcode Actions`}
|
||||||
@ -386,7 +387,8 @@ export default function BuildDetail() {
|
|||||||
}),
|
}),
|
||||||
CancelItemAction({
|
CancelItemAction({
|
||||||
tooltip: t`Cancel order`,
|
tooltip: t`Cancel order`,
|
||||||
onClick: () => cancelBuild.open()
|
onClick: () => cancelBuild.open(),
|
||||||
|
hidden: !user.hasChangeRole(UserRoles.build)
|
||||||
// TODO: Hide if build cannot be cancelled
|
// TODO: Hide if build cannot be cancelled
|
||||||
}),
|
}),
|
||||||
DuplicateItemAction({
|
DuplicateItemAction({
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
import { ReactNode, useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import DetailsBadge from '../../components/details/DetailsBadge';
|
import DetailsBadge from '../../components/details/DetailsBadge';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
@ -32,6 +33,7 @@ import { PageDetail } from '../../components/nav/PageDetail';
|
|||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { companyFields } from '../../forms/CompanyForms';
|
import { companyFields } from '../../forms/CompanyForms';
|
||||||
import { useEditApiFormModal } from '../../hooks/UseForm';
|
import { useEditApiFormModal } from '../../hooks/UseForm';
|
||||||
@ -285,6 +287,7 @@ export default function CompanyDetail(props: Readonly<CompanyDetailProps>) {
|
|||||||
|
|
||||||
const companyActions = useMemo(() => {
|
const companyActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
<AdminButton model={ModelType.company} pk={company.pk} />,
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
key="company"
|
key="company"
|
||||||
tooltip={t`Company Actions`}
|
tooltip={t`Company Actions`}
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
@ -204,6 +205,10 @@ export default function ManufacturerPartDetail() {
|
|||||||
|
|
||||||
const manufacturerPartActions = useMemo(() => {
|
const manufacturerPartActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
<AdminButton
|
||||||
|
model={ModelType.manufacturerpart}
|
||||||
|
pk={manufacturerPart.pk}
|
||||||
|
/>,
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
key="part"
|
key="part"
|
||||||
tooltip={t`Manufacturer Part Actions`}
|
tooltip={t`Manufacturer Part Actions`}
|
||||||
@ -223,7 +228,7 @@ export default function ManufacturerPartDetail() {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user, manufacturerPart]);
|
||||||
|
|
||||||
const breadcrumbs = useMemo(() => {
|
const breadcrumbs = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
import { ReactNode, useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import DetailsBadge from '../../components/details/DetailsBadge';
|
import DetailsBadge from '../../components/details/DetailsBadge';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
@ -243,6 +244,7 @@ export default function SupplierPartDetail() {
|
|||||||
|
|
||||||
const supplierPartActions = useMemo(() => {
|
const supplierPartActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
<AdminButton model={ModelType.supplierpart} pk={supplierPart.pk} />,
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
key="part"
|
key="part"
|
||||||
tooltip={t`Supplier Part Actions`}
|
tooltip={t`Supplier Part Actions`}
|
||||||
@ -262,7 +264,7 @@ export default function SupplierPartDetail() {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user, supplierPart]);
|
||||||
|
|
||||||
const supplierPartFields = useSupplierPartFields();
|
const supplierPartFields = useSupplierPartFields();
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
import {
|
import {
|
||||||
@ -201,6 +202,7 @@ export default function CategoryDetail({}: {}) {
|
|||||||
|
|
||||||
const categoryActions = useMemo(() => {
|
const categoryActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
<AdminButton model={ModelType.partcategory} pk={category.pk} />,
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
key="category"
|
key="category"
|
||||||
tooltip={t`Category Actions`}
|
tooltip={t`Category Actions`}
|
||||||
@ -219,7 +221,7 @@ export default function CategoryDetail({}: {}) {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [id, user]);
|
}, [id, user, category.pk]);
|
||||||
|
|
||||||
const categoryPanels: PanelType[] = useMemo(
|
const categoryPanels: PanelType[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
@ -34,6 +34,7 @@ import { ReactNode, useMemo, useState } from 'react';
|
|||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import DetailsBadge from '../../components/details/DetailsBadge';
|
import DetailsBadge from '../../components/details/DetailsBadge';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
@ -746,6 +747,7 @@ export default function PartDetail() {
|
|||||||
|
|
||||||
const partActions = useMemo(() => {
|
const partActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
<AdminButton model={ModelType.part} pk={part.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
actions={[
|
||||||
ViewBarcodeAction({}),
|
ViewBarcodeAction({}),
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
import { ReactNode, useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
@ -300,6 +301,7 @@ export default function PurchaseOrderDetail() {
|
|||||||
|
|
||||||
const poActions = useMemo(() => {
|
const poActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
<AdminButton model={ModelType.purchaseorder} pk={order.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
actions={[
|
||||||
ViewBarcodeAction({}),
|
ViewBarcodeAction({}),
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
import { ReactNode, useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
@ -285,6 +286,7 @@ export default function ReturnOrderDetail() {
|
|||||||
|
|
||||||
const orderActions = useMemo(() => {
|
const orderActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
<AdminButton model={ModelType.returnorder} pk={order.pk} />,
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
key="order-actions"
|
key="order-actions"
|
||||||
tooltip={t`Order Actions`}
|
tooltip={t`Order Actions`}
|
||||||
@ -306,7 +308,7 @@ export default function ReturnOrderDetail() {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user, order]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
import { ReactNode, useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
@ -297,6 +298,7 @@ export default function SalesOrderDetail() {
|
|||||||
|
|
||||||
const soActions = useMemo(() => {
|
const soActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
<AdminButton model={ModelType.salesorder} pk={order.pk} />,
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
key="order-actions"
|
key="order-actions"
|
||||||
tooltip={t`Order Actions`}
|
tooltip={t`Order Actions`}
|
||||||
@ -316,7 +318,7 @@ export default function SalesOrderDetail() {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user, order]);
|
||||||
|
|
||||||
const orderBadges: ReactNode[] = useMemo(() => {
|
const orderBadges: ReactNode[] = useMemo(() => {
|
||||||
return instanceQuery.isLoading
|
return instanceQuery.isLoading
|
||||||
|
@ -10,6 +10,7 @@ import { useMemo, useState } from 'react';
|
|||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { ActionButton } from '../../components/buttons/ActionButton';
|
import { ActionButton } from '../../components/buttons/ActionButton';
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
import {
|
import {
|
||||||
@ -266,6 +267,7 @@ export default function Stock() {
|
|||||||
|
|
||||||
const locationActions = useMemo(
|
const locationActions = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
<AdminButton model={ModelType.stocklocation} pk={location.pk} />,
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<InvenTreeIcon icon="stocktake" />}
|
icon={<InvenTreeIcon icon="stocktake" />}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
import { ReactNode, useMemo, useState } from 'react';
|
import { ReactNode, useMemo, useState } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
import DetailsBadge from '../../components/details/DetailsBadge';
|
import DetailsBadge from '../../components/details/DetailsBadge';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
@ -408,6 +409,7 @@ export default function StockDetail() {
|
|||||||
|
|
||||||
const stockActions = useMemo(
|
const stockActions = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
<AdminButton model={ModelType.stockitem} pk={stockitem.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
actions={[
|
||||||
ViewBarcodeAction({}),
|
ViewBarcodeAction({}),
|
||||||
|
Loading…
Reference in New Issue
Block a user