mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-18 10:46:31 +00:00
[UI] Various fixes (#10038)
* Fix badge wrapping * Move revision selector - Simplify top header * Remove "detail" attribute from <PageDetail> * Implement "FORMS_CLOSE_USING_ESCAPE" option * Implement "STICKY_HEADER" setting * Remove unused setting * Improved badge layout * Sticky header fix
This commit is contained in:
@@ -22,7 +22,6 @@ The *Display Settings* screen shows general display configuration options:
|
|||||||
{{ usersetting("STICKY_HEADER") }}
|
{{ usersetting("STICKY_HEADER") }}
|
||||||
{{ usersetting("DATE_DISPLAY_FORMAT") }}
|
{{ usersetting("DATE_DISPLAY_FORMAT") }}
|
||||||
{{ usersetting("FORMS_CLOSE_USING_ESCAPE") }}
|
{{ usersetting("FORMS_CLOSE_USING_ESCAPE") }}
|
||||||
{{ usersetting("PART_SHOW_QUANTITY_IN_FORMS") }}
|
|
||||||
{{ usersetting("DISPLAY_STOCKTAKE_TAB") }}
|
{{ usersetting("DISPLAY_STOCKTAKE_TAB") }}
|
||||||
{{ usersetting("SHOW_FULL_CATEGORY_IN_TABLES")}}
|
{{ usersetting("SHOW_FULL_CATEGORY_IN_TABLES")}}
|
||||||
{{ usersetting("ENABLE_LAST_BREADCRUMB") }}
|
{{ usersetting("ENABLE_LAST_BREADCRUMB") }}
|
||||||
|
@@ -173,12 +173,6 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = {
|
|||||||
'default': False,
|
'default': False,
|
||||||
'validator': bool,
|
'validator': bool,
|
||||||
},
|
},
|
||||||
'PART_SHOW_QUANTITY_IN_FORMS': {
|
|
||||||
'name': _('Show Quantity in Forms'),
|
|
||||||
'description': _('Display available part quantity in some forms'),
|
|
||||||
'default': True,
|
|
||||||
'validator': bool,
|
|
||||||
},
|
|
||||||
'FORMS_CLOSE_USING_ESCAPE': {
|
'FORMS_CLOSE_USING_ESCAPE': {
|
||||||
'name': _('Escape Key Closes Forms'),
|
'name': _('Escape Key Closes Forms'),
|
||||||
'description': _('Use the escape key to close modal forms'),
|
'description': _('Use the escape key to close modal forms'),
|
||||||
|
@@ -62,6 +62,7 @@ export function Header() {
|
|||||||
const { isLoggedIn } = useUserState();
|
const { isLoggedIn } = useUserState();
|
||||||
const [notificationCount, setNotificationCount] = useState<number>(0);
|
const [notificationCount, setNotificationCount] = useState<number>(0);
|
||||||
const globalSettings = useGlobalSettingsState();
|
const globalSettings = useGlobalSettingsState();
|
||||||
|
const userSettings = useUserSettingsState();
|
||||||
|
|
||||||
const navbar_message = useMemo(() => {
|
const navbar_message = useMemo(() => {
|
||||||
return server.customize?.navbar_message;
|
return server.customize?.navbar_message;
|
||||||
@@ -107,8 +108,21 @@ export function Header() {
|
|||||||
else closeNavDrawer();
|
else closeNavDrawer();
|
||||||
}, [navigationOpen]);
|
}, [navigationOpen]);
|
||||||
|
|
||||||
|
const headerStyle: any = useMemo(() => {
|
||||||
|
const sticky: boolean = userSettings.isSet('STICKY_HEADER', true);
|
||||||
|
|
||||||
|
if (sticky) {
|
||||||
|
return {
|
||||||
|
position: 'sticky',
|
||||||
|
top: 0
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}, [userSettings]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.layoutHeader}>
|
<div className={classes.layoutHeader} style={headerStyle}>
|
||||||
<SearchDrawer opened={searchDrawerOpened} onClose={closeSearchDrawer} />
|
<SearchDrawer opened={searchDrawerOpened} onClose={closeSearchDrawer} />
|
||||||
<NavigationDrawer opened={navDrawerOpened} close={closeNavDrawer} />
|
<NavigationDrawer opened={navDrawerOpened} close={closeNavDrawer} />
|
||||||
<NotificationDrawer
|
<NotificationDrawer
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Group, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
import { Group, Paper, Space, Stack, Text } from '@mantine/core';
|
||||||
import { useHotkeys } from '@mantine/hooks';
|
import { useHotkeys } from '@mantine/hooks';
|
||||||
|
|
||||||
import { Fragment, type ReactNode, useMemo } from 'react';
|
import { Fragment, type ReactNode, useMemo } from 'react';
|
||||||
@@ -14,7 +14,6 @@ interface PageDetailInterface {
|
|||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
imageUrl?: string;
|
imageUrl?: string;
|
||||||
detail?: ReactNode;
|
|
||||||
badges?: ReactNode[];
|
badges?: ReactNode[];
|
||||||
breadcrumbs?: Breadcrumb[];
|
breadcrumbs?: Breadcrumb[];
|
||||||
lastCrumb?: Breadcrumb[];
|
lastCrumb?: Breadcrumb[];
|
||||||
@@ -34,7 +33,6 @@ export function PageDetail({
|
|||||||
title,
|
title,
|
||||||
icon,
|
icon,
|
||||||
subtitle,
|
subtitle,
|
||||||
detail,
|
|
||||||
badges,
|
badges,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
breadcrumbs,
|
breadcrumbs,
|
||||||
@@ -74,20 +72,6 @@ export function PageDetail({
|
|||||||
[subtitle]
|
[subtitle]
|
||||||
);
|
);
|
||||||
|
|
||||||
const maxCols = useMemo(() => {
|
|
||||||
let cols = 1;
|
|
||||||
|
|
||||||
if (!!detail) {
|
|
||||||
cols++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!!badges) {
|
|
||||||
cols++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cols;
|
|
||||||
}, [detail, badges]);
|
|
||||||
|
|
||||||
// breadcrumb caching
|
// breadcrumb caching
|
||||||
const computedBreadcrumbs = useMemo(() => {
|
const computedBreadcrumbs = useMemo(() => {
|
||||||
if (userSettings.isSet('ENABLE_LAST_BREADCRUMB', false)) {
|
if (userSettings.isSet('ENABLE_LAST_BREADCRUMB', false)) {
|
||||||
@@ -114,14 +98,13 @@ export function PageDetail({
|
|||||||
wrap='nowrap'
|
wrap='nowrap'
|
||||||
align='flex-start'
|
align='flex-start'
|
||||||
>
|
>
|
||||||
<SimpleGrid
|
<Group
|
||||||
cols={{
|
justify='space-between'
|
||||||
base: 1,
|
wrap='nowrap'
|
||||||
md: Math.min(2, maxCols),
|
align='flex-start'
|
||||||
lg: Math.min(3, maxCols)
|
style={{ flexGrow: 1 }}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Group justify='left' wrap='nowrap'>
|
<Group justify='start' wrap='nowrap' align='flex-start'>
|
||||||
{imageUrl && (
|
{imageUrl && (
|
||||||
<ApiImage
|
<ApiImage
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
@@ -142,20 +125,15 @@ export function PageDetail({
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
{detail && <div>{detail}</div>}
|
|
||||||
{badges && (
|
{badges && (
|
||||||
<Group
|
<Group justify='flex-end' gap='xs' align='center'>
|
||||||
justify='center'
|
|
||||||
gap='xs'
|
|
||||||
align='flex-start'
|
|
||||||
wrap='nowrap'
|
|
||||||
>
|
|
||||||
{badges?.map((badge, idx) => (
|
{badges?.map((badge, idx) => (
|
||||||
<Fragment key={idx}>{badge}</Fragment>
|
<Fragment key={idx}>{badge}</Fragment>
|
||||||
))}
|
))}
|
||||||
|
<Space w='md' />
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
</SimpleGrid>
|
</Group>
|
||||||
{actions && (
|
{actions && (
|
||||||
<Group gap={5} justify='right' wrap='nowrap' align='flex-start'>
|
<Group gap={5} justify='right' wrap='nowrap' align='flex-start'>
|
||||||
{actions.map((action, idx) => (
|
{actions.map((action, idx) => (
|
||||||
|
@@ -4,12 +4,15 @@ import { useCallback } from 'react';
|
|||||||
|
|
||||||
import type { UseModalProps, UseModalReturn } from '@lib/types/Modals';
|
import type { UseModalProps, UseModalReturn } from '@lib/types/Modals';
|
||||||
import { StylishText } from '../components/items/StylishText';
|
import { StylishText } from '../components/items/StylishText';
|
||||||
|
import { useUserSettingsState } from '../states/SettingsStates';
|
||||||
|
|
||||||
export function useModal(props: UseModalProps): UseModalReturn {
|
export function useModal(props: UseModalProps): UseModalReturn {
|
||||||
const onOpen = useCallback(() => {
|
const onOpen = useCallback(() => {
|
||||||
props.onOpen?.();
|
props.onOpen?.();
|
||||||
}, [props.onOpen]);
|
}, [props.onOpen]);
|
||||||
|
|
||||||
|
const userSettings = useUserSettingsState();
|
||||||
|
|
||||||
const onClose = useCallback(() => {
|
const onClose = useCallback(() => {
|
||||||
props.onClose?.();
|
props.onClose?.();
|
||||||
}, [props.onClose]);
|
}, [props.onClose]);
|
||||||
@@ -27,6 +30,7 @@ export function useModal(props: UseModalProps): UseModalReturn {
|
|||||||
<Modal
|
<Modal
|
||||||
key={props.id}
|
key={props.id}
|
||||||
opened={opened}
|
opened={opened}
|
||||||
|
closeOnEscape={userSettings.isSet('FORMS_CLOSE_USING_ESCAPE')}
|
||||||
onClose={close}
|
onClose={close}
|
||||||
closeOnClickOutside={props.closeOnClickOutside}
|
closeOnClickOutside={props.closeOnClickOutside}
|
||||||
size={props.size ?? 'xl'}
|
size={props.size ?? 'xl'}
|
||||||
|
@@ -53,7 +53,6 @@ export default function UserSettings() {
|
|||||||
'STICKY_HEADER',
|
'STICKY_HEADER',
|
||||||
'DATE_DISPLAY_FORMAT',
|
'DATE_DISPLAY_FORMAT',
|
||||||
'FORMS_CLOSE_USING_ESCAPE',
|
'FORMS_CLOSE_USING_ESCAPE',
|
||||||
'PART_SHOW_QUANTITY_IN_FORMS',
|
|
||||||
'DISPLAY_STOCKTAKE_TAB',
|
'DISPLAY_STOCKTAKE_TAB',
|
||||||
'ENABLE_LAST_BREADCRUMB',
|
'ENABLE_LAST_BREADCRUMB',
|
||||||
'SHOW_FULL_LOCATION_IN_TABLES',
|
'SHOW_FULL_LOCATION_IN_TABLES',
|
||||||
|
@@ -102,6 +102,47 @@ import PartPricingPanel from './PartPricingPanel';
|
|||||||
import PartStocktakeDetail from './PartStocktakeDetail';
|
import PartStocktakeDetail from './PartStocktakeDetail';
|
||||||
import PartSupplierDetail from './PartSupplierDetail';
|
import PartSupplierDetail from './PartSupplierDetail';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a part revision selector component
|
||||||
|
*/
|
||||||
|
function RevisionSelector({
|
||||||
|
part,
|
||||||
|
options
|
||||||
|
}: {
|
||||||
|
part: any;
|
||||||
|
options: any[];
|
||||||
|
}) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
id='part-revision-select'
|
||||||
|
aria-label='part-revision-select'
|
||||||
|
options={options}
|
||||||
|
value={{
|
||||||
|
value: part.pk,
|
||||||
|
label: part.full_name,
|
||||||
|
part: part
|
||||||
|
}}
|
||||||
|
isSearchable={false}
|
||||||
|
formatOptionLabel={(option: any) =>
|
||||||
|
RenderPart({
|
||||||
|
instance: option.part,
|
||||||
|
showSecondary: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onChange={(value: any) => {
|
||||||
|
navigate(getDetailUrl(ModelType.part, value.value));
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
menuPortal: (base: any) => ({ ...base, zIndex: 9999 }),
|
||||||
|
menu: (base: any) => ({ ...base, zIndex: 9999 }),
|
||||||
|
menuList: (base: any) => ({ ...base, zIndex: 9999 })
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detail view for a single Part instance
|
* Detail view for a single Part instance
|
||||||
*/
|
*/
|
||||||
@@ -146,6 +187,95 @@ export default function PartDetail() {
|
|||||||
refetchOnMount: true
|
refetchOnMount: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fetch information on part revision
|
||||||
|
const partRevisionQuery = useQuery({
|
||||||
|
refetchOnMount: true,
|
||||||
|
queryKey: [
|
||||||
|
'part_revisions',
|
||||||
|
part.pk,
|
||||||
|
part.revision_of,
|
||||||
|
part.revision_count
|
||||||
|
],
|
||||||
|
queryFn: async () => {
|
||||||
|
if (!part.revision_of && !part.revision_count) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const revisions = [];
|
||||||
|
|
||||||
|
// First, fetch information for the top-level part
|
||||||
|
if (part.revision_of) {
|
||||||
|
await api
|
||||||
|
.get(apiUrl(ApiEndpoints.part_list, part.revision_of))
|
||||||
|
.then((response) => {
|
||||||
|
revisions.push(response.data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
revisions.push(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = apiUrl(ApiEndpoints.part_list);
|
||||||
|
|
||||||
|
await api
|
||||||
|
.get(url, {
|
||||||
|
params: {
|
||||||
|
revision_of: part.revision_of || part.pk
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
switch (response.status) {
|
||||||
|
case 200:
|
||||||
|
response.data.forEach((r: any) => {
|
||||||
|
revisions.push(r);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return revisions;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const partRevisionOptions: any[] = useMemo(() => {
|
||||||
|
if (partRevisionQuery.isFetching || !partRevisionQuery.data) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!part.revision_of && !part.revision_count) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: any[] = partRevisionQuery.data.map((revision: any) => {
|
||||||
|
return {
|
||||||
|
value: revision.pk,
|
||||||
|
label: revision.full_name,
|
||||||
|
part: revision
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add this part if not already available
|
||||||
|
if (!options.find((o) => o.value == part.pk)) {
|
||||||
|
options.push({
|
||||||
|
value: part.pk,
|
||||||
|
label: part.full_name,
|
||||||
|
part: part
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return options.sort((a, b) => {
|
||||||
|
return `${a.part.revision}`.localeCompare(b.part.revision);
|
||||||
|
});
|
||||||
|
}, [part, partRevisionQuery.isFetching, partRevisionQuery.data]);
|
||||||
|
|
||||||
|
const enableRevisionSelection: boolean = useMemo(() => {
|
||||||
|
return (
|
||||||
|
partRevisionOptions.length > 0 &&
|
||||||
|
globalSettings.isSet('PART_ENABLE_REVISION')
|
||||||
|
);
|
||||||
|
}, [partRevisionOptions, globalSettings]);
|
||||||
|
|
||||||
const detailsPanel = useMemo(() => {
|
const detailsPanel = useMemo(() => {
|
||||||
if (instanceQuery.isFetching) {
|
if (instanceQuery.isFetching) {
|
||||||
return <Skeleton />;
|
return <Skeleton />;
|
||||||
@@ -481,6 +611,7 @@ export default function PartDetail() {
|
|||||||
|
|
||||||
return part ? (
|
return part ? (
|
||||||
<ItemDetailsGrid>
|
<ItemDetailsGrid>
|
||||||
|
<Stack gap='xs'>
|
||||||
<Grid grow>
|
<Grid grow>
|
||||||
<DetailsImage
|
<DetailsImage
|
||||||
appRole={UserRoles.part}
|
appRole={UserRoles.part}
|
||||||
@@ -499,6 +630,13 @@ export default function PartDetail() {
|
|||||||
<DetailsTable fields={tl} item={data} />
|
<DetailsTable fields={tl} item={data} />
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{enableRevisionSelection && (
|
||||||
|
<Stack gap='xs'>
|
||||||
|
<Text>{t`Select Part Revision`}</Text>
|
||||||
|
<RevisionSelector part={part} options={partRevisionOptions} />
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
<DetailsTable fields={tr} item={data} />
|
<DetailsTable fields={tr} item={data} />
|
||||||
<DetailsTable fields={bl} item={data} />
|
<DetailsTable fields={bl} item={data} />
|
||||||
<DetailsTable fields={br} item={data} />
|
<DetailsTable fields={br} item={data} />
|
||||||
@@ -513,6 +651,8 @@ export default function PartDetail() {
|
|||||||
serials,
|
serials,
|
||||||
instanceQuery.isFetching,
|
instanceQuery.isFetching,
|
||||||
instanceQuery.data,
|
instanceQuery.data,
|
||||||
|
enableRevisionSelection,
|
||||||
|
partRevisionOptions,
|
||||||
partRequirementsQuery.isFetching,
|
partRequirementsQuery.isFetching,
|
||||||
partRequirements
|
partRequirements
|
||||||
]);
|
]);
|
||||||
@@ -680,88 +820,6 @@ export default function PartDetail() {
|
|||||||
];
|
];
|
||||||
}, [id, part, user, globalSettings, userSettings, detailsPanel]);
|
}, [id, part, user, globalSettings, userSettings, detailsPanel]);
|
||||||
|
|
||||||
// Fetch information on part revision
|
|
||||||
const partRevisionQuery = useQuery({
|
|
||||||
refetchOnMount: true,
|
|
||||||
queryKey: [
|
|
||||||
'part_revisions',
|
|
||||||
part.pk,
|
|
||||||
part.revision_of,
|
|
||||||
part.revision_count
|
|
||||||
],
|
|
||||||
queryFn: async () => {
|
|
||||||
if (!part.revision_of && !part.revision_count) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const revisions = [];
|
|
||||||
|
|
||||||
// First, fetch information for the top-level part
|
|
||||||
if (part.revision_of) {
|
|
||||||
await api
|
|
||||||
.get(apiUrl(ApiEndpoints.part_list, part.revision_of))
|
|
||||||
.then((response) => {
|
|
||||||
revisions.push(response.data);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
revisions.push(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = apiUrl(ApiEndpoints.part_list);
|
|
||||||
|
|
||||||
await api
|
|
||||||
.get(url, {
|
|
||||||
params: {
|
|
||||||
revision_of: part.revision_of || part.pk
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
switch (response.status) {
|
|
||||||
case 200:
|
|
||||||
response.data.forEach((r: any) => {
|
|
||||||
revisions.push(r);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return revisions;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const partRevisionOptions: any[] = useMemo(() => {
|
|
||||||
if (partRevisionQuery.isFetching || !partRevisionQuery.data) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!part.revision_of && !part.revision_count) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const options: any[] = partRevisionQuery.data.map((revision: any) => {
|
|
||||||
return {
|
|
||||||
value: revision.pk,
|
|
||||||
label: revision.full_name,
|
|
||||||
part: revision
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add this part if not already available
|
|
||||||
if (!options.find((o) => o.value == part.pk)) {
|
|
||||||
options.push({
|
|
||||||
value: part.pk,
|
|
||||||
label: part.full_name,
|
|
||||||
part: part
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.sort((a, b) => {
|
|
||||||
return `${a.part.revision}`.localeCompare(b.part.revision);
|
|
||||||
});
|
|
||||||
}, [part, partRevisionQuery.isFetching, partRevisionQuery.data]);
|
|
||||||
|
|
||||||
const breadcrumbs = useMemo(() => {
|
const breadcrumbs = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{ name: t`Parts`, url: '/part' },
|
{ name: t`Parts`, url: '/part' },
|
||||||
@@ -1003,13 +1061,6 @@ export default function PartDetail() {
|
|||||||
];
|
];
|
||||||
}, [id, part, user, stockAdjustActions.menuActions]);
|
}, [id, part, user, stockAdjustActions.menuActions]);
|
||||||
|
|
||||||
const enableRevisionSelection: boolean = useMemo(() => {
|
|
||||||
return (
|
|
||||||
partRevisionOptions.length > 0 &&
|
|
||||||
globalSettings.isSet('PART_ENABLE_REVISION')
|
|
||||||
);
|
|
||||||
}, [partRevisionOptions, globalSettings]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{editPart.modal}
|
{editPart.modal}
|
||||||
@@ -1059,38 +1110,6 @@ export default function PartDetail() {
|
|||||||
editAction={editPart.open}
|
editAction={editPart.open}
|
||||||
editEnabled={user.hasChangeRole(UserRoles.part)}
|
editEnabled={user.hasChangeRole(UserRoles.part)}
|
||||||
actions={partActions}
|
actions={partActions}
|
||||||
detail={
|
|
||||||
enableRevisionSelection ? (
|
|
||||||
<Stack gap='xs'>
|
|
||||||
<Text>{t`Select Part Revision`}</Text>
|
|
||||||
<Select
|
|
||||||
id='part-revision-select'
|
|
||||||
aria-label='part-revision-select'
|
|
||||||
options={partRevisionOptions}
|
|
||||||
value={{
|
|
||||||
value: part.pk,
|
|
||||||
label: part.full_name,
|
|
||||||
part: part
|
|
||||||
}}
|
|
||||||
isSearchable={false}
|
|
||||||
formatOptionLabel={(option: any) =>
|
|
||||||
RenderPart({
|
|
||||||
instance: option.part,
|
|
||||||
showSecondary: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onChange={(value: any) => {
|
|
||||||
navigate(getDetailUrl(ModelType.part, value.value));
|
|
||||||
}}
|
|
||||||
styles={{
|
|
||||||
menuPortal: (base: any) => ({ ...base, zIndex: 9999 }),
|
|
||||||
menu: (base: any) => ({ ...base, zIndex: 9999 }),
|
|
||||||
menuList: (base: any) => ({ ...base, zIndex: 9999 })
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<PanelGroup
|
<PanelGroup
|
||||||
pageKey='part'
|
pageKey='part'
|
||||||
|
@@ -9,6 +9,7 @@ import {
|
|||||||
Group,
|
Group,
|
||||||
Paper,
|
Paper,
|
||||||
Select,
|
Select,
|
||||||
|
Space,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
@@ -252,7 +253,6 @@ function FilterAddGroup({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap='xs'>
|
<Stack gap='xs'>
|
||||||
<Divider />
|
|
||||||
<Select
|
<Select
|
||||||
data={filterOptions}
|
data={filterOptions}
|
||||||
searchable={true}
|
searchable={true}
|
||||||
@@ -311,12 +311,13 @@ export function FilterSelectDrawer({
|
|||||||
}}
|
}}
|
||||||
title={<StylishText size='lg'>{title ?? t`Table Filters`}</StylishText>}
|
title={<StylishText size='lg'>{title ?? t`Table Filters`}</StylishText>}
|
||||||
>
|
>
|
||||||
|
<Divider />
|
||||||
|
<Space h='sm' />
|
||||||
<Stack gap='xs'>
|
<Stack gap='xs'>
|
||||||
{hasFilters &&
|
{hasFilters &&
|
||||||
filterSet.activeFilters?.map((f) => (
|
filterSet.activeFilters?.map((f) => (
|
||||||
<FilterItem key={f.name} flt={f} filterSet={filterSet} />
|
<FilterItem key={f.name} flt={f} filterSet={filterSet} />
|
||||||
))}
|
))}
|
||||||
{hasFilters && <Divider />}
|
|
||||||
{addFilter && (
|
{addFilter && (
|
||||||
<Stack gap='xs'>
|
<Stack gap='xs'>
|
||||||
<FilterAddGroup
|
<FilterAddGroup
|
||||||
|
Reference in New Issue
Block a user