mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 03:26:45 +00:00
[UI] Responsiveness Improvements (#8885)
* Shorten string in page title * Style fixes * Adjust cell width props * Refactor <PageDetail> component - Improve responsiveness * Simplify <ItemDetailsGrid /> * Refactor <DetailsImage>
This commit is contained in:
parent
ea1b2e3079
commit
46f6450ee1
@ -380,16 +380,15 @@ export function DetailsTableField({
|
||||
<Table.Tr style={{ verticalAlign: 'top' }}>
|
||||
<Table.Td
|
||||
style={{
|
||||
width: '50',
|
||||
maxWidth: '50'
|
||||
}}
|
||||
>
|
||||
<InvenTreeIcon icon={field.icon ?? (field.name as InvenTreeIconType)} />
|
||||
</Table.Td>
|
||||
<Table.Td style={{ maxWidth: '65%', lineBreak: 'auto' }}>
|
||||
<Table.Td style={{ minWidth: 75, lineBreak: 'auto', flex: 2 }}>
|
||||
<Text>{field.label}</Text>
|
||||
</Table.Td>
|
||||
<Table.Td style={{ lineBreak: 'anywhere' }}>
|
||||
<Table.Td style={{ lineBreak: 'anywhere', minWidth: 100, flex: 10 }}>
|
||||
<FieldType field_data={field} field_value={fieldValue} />
|
||||
</Table.Td>
|
||||
<Table.Td style={{ width: '50' }}>
|
||||
@ -409,7 +408,11 @@ export function DetailsTable({
|
||||
title?: string;
|
||||
}>) {
|
||||
return (
|
||||
<Paper p='xs' withBorder radius='xs'>
|
||||
<Paper
|
||||
p='xs'
|
||||
withBorder
|
||||
style={{ overflowX: 'hidden', width: '100%', minWidth: 200 }}
|
||||
>
|
||||
<Stack gap='xs'>
|
||||
{title && <StylishText size='lg'>{title}</StylishText>}
|
||||
<Table striped verticalSpacing={5} horizontalSpacing='sm'>
|
||||
|
@ -2,6 +2,7 @@ import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
AspectRatio,
|
||||
Button,
|
||||
Grid,
|
||||
Group,
|
||||
Image,
|
||||
Overlay,
|
||||
@ -421,31 +422,39 @@ export function DetailsImage(props: Readonly<DetailImageProps>) {
|
||||
return (
|
||||
<>
|
||||
{downloadImage.modal}
|
||||
<AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1} pos='relative'>
|
||||
<>
|
||||
<ApiImage
|
||||
src={img}
|
||||
mah={IMAGE_DIMENSION}
|
||||
maw={IMAGE_DIMENSION}
|
||||
onClick={expandImage}
|
||||
/>
|
||||
{permissions.hasChangeRole(props.appRole) &&
|
||||
hasOverlay &&
|
||||
hovered && (
|
||||
<Overlay color='black' opacity={0.8} onClick={expandImage}>
|
||||
<ImageActionButtons
|
||||
visible={hovered}
|
||||
actions={props.imageActions}
|
||||
apiPath={props.apiPath}
|
||||
hasImage={!!props.src}
|
||||
pk={props.pk}
|
||||
setImage={setAndRefresh}
|
||||
downloadImage={downloadImage.open}
|
||||
/>
|
||||
</Overlay>
|
||||
)}
|
||||
</>
|
||||
</AspectRatio>
|
||||
<Grid.Col span={{ base: 12, sm: 4 }}>
|
||||
<AspectRatio
|
||||
ref={ref}
|
||||
maw={IMAGE_DIMENSION}
|
||||
ratio={1}
|
||||
pos='relative'
|
||||
visibleFrom='xs'
|
||||
>
|
||||
<>
|
||||
<ApiImage
|
||||
src={img}
|
||||
mah={IMAGE_DIMENSION}
|
||||
maw={IMAGE_DIMENSION}
|
||||
onClick={expandImage}
|
||||
/>
|
||||
{permissions.hasChangeRole(props.appRole) &&
|
||||
hasOverlay &&
|
||||
hovered && (
|
||||
<Overlay color='black' opacity={0.8} onClick={expandImage}>
|
||||
<ImageActionButtons
|
||||
visible={hovered}
|
||||
actions={props.imageActions}
|
||||
apiPath={props.apiPath}
|
||||
hasImage={!!props.src}
|
||||
pk={props.pk}
|
||||
setImage={setAndRefresh}
|
||||
downloadImage={downloadImage.open}
|
||||
/>
|
||||
</Overlay>
|
||||
)}
|
||||
</>
|
||||
</AspectRatio>
|
||||
</Grid.Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { Paper, SimpleGrid } from '@mantine/core';
|
||||
import { useElementSize } from '@mantine/hooks';
|
||||
import type React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export function ItemDetailsGrid(props: React.PropsWithChildren<{}>) {
|
||||
const { ref, width } = useElementSize();
|
||||
|
||||
const cols = useMemo(() => (width > 700 ? 2 : 1), [width]);
|
||||
|
||||
return (
|
||||
<Paper p='xs'>
|
||||
<SimpleGrid cols={cols} spacing='xs' verticalSpacing='xs' ref={ref}>
|
||||
<SimpleGrid
|
||||
cols={{ base: 1, '900px': 2 }}
|
||||
type='container'
|
||||
spacing='xs'
|
||||
verticalSpacing='xs'
|
||||
>
|
||||
{props.children}
|
||||
</SimpleGrid>
|
||||
</Paper>
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Group, Paper, Space, Stack, Text } from '@mantine/core';
|
||||
import { Group, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||
import { useHotkeys } from '@mantine/hooks';
|
||||
import { Fragment, type ReactNode } from 'react';
|
||||
import { Fragment, type ReactNode, useMemo } from 'react';
|
||||
|
||||
import { shortenString } from '../../functions/tables';
|
||||
import { ApiImage } from '../images/ApiImage';
|
||||
import { StylishText } from '../items/StylishText';
|
||||
import { type Breadcrumb, BreadcrumbList } from './BreadcrumbList';
|
||||
@ -51,9 +52,41 @@ export function PageDetail({
|
||||
]
|
||||
]);
|
||||
|
||||
const pageTitleString = useMemo(
|
||||
() =>
|
||||
shortenString({
|
||||
str: title,
|
||||
len: 50
|
||||
}),
|
||||
[title]
|
||||
);
|
||||
|
||||
const description = useMemo(
|
||||
() =>
|
||||
shortenString({
|
||||
str: subtitle,
|
||||
len: 75
|
||||
}),
|
||||
[subtitle]
|
||||
);
|
||||
|
||||
const maxCols = useMemo(() => {
|
||||
let cols = 1;
|
||||
|
||||
if (!!detail) {
|
||||
cols++;
|
||||
}
|
||||
|
||||
if (!!badges) {
|
||||
cols++;
|
||||
}
|
||||
|
||||
return cols;
|
||||
}, [detail, badges]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title={title} />
|
||||
<PageTitle title={pageTitleString} />
|
||||
<Stack gap='xs'>
|
||||
{breadcrumbs && breadcrumbs.length > 0 && (
|
||||
<BreadcrumbList
|
||||
@ -62,8 +95,19 @@ export function PageDetail({
|
||||
/>
|
||||
)}
|
||||
<Paper p='xs' radius='xs' shadow='xs'>
|
||||
<Stack gap='xs'>
|
||||
<Group justify='space-between' wrap='nowrap'>
|
||||
<Group
|
||||
justify='space-between'
|
||||
gap='xs'
|
||||
wrap='nowrap'
|
||||
align='flex-start'
|
||||
>
|
||||
<SimpleGrid
|
||||
cols={{
|
||||
base: 1,
|
||||
md: Math.min(2, maxCols),
|
||||
lg: Math.min(3, maxCols)
|
||||
}}
|
||||
>
|
||||
<Group justify='left' wrap='nowrap'>
|
||||
{imageUrl && (
|
||||
<ApiImage
|
||||
@ -72,6 +116,7 @@ export function PageDetail({
|
||||
miw={42}
|
||||
mah={42}
|
||||
maw={42}
|
||||
visibleFrom='sm'
|
||||
/>
|
||||
)}
|
||||
<Stack gap='xs'>
|
||||
@ -79,30 +124,33 @@ export function PageDetail({
|
||||
{subtitle && (
|
||||
<Group gap='xs'>
|
||||
{icon}
|
||||
<Text size='sm' truncate>
|
||||
{subtitle}
|
||||
</Text>
|
||||
<Text size='sm'>{description}</Text>
|
||||
</Group>
|
||||
)}
|
||||
</Stack>
|
||||
</Group>
|
||||
<Space />
|
||||
{detail}
|
||||
<Group justify='right' gap='xs' wrap='nowrap'>
|
||||
{badges?.map((badge, idx) => (
|
||||
<Fragment key={idx}>{badge}</Fragment>
|
||||
))}
|
||||
</Group>
|
||||
<Space />
|
||||
{actions && (
|
||||
<Group gap={5} justify='right'>
|
||||
{actions.map((action, idx) => (
|
||||
<Fragment key={idx}>{action}</Fragment>
|
||||
{detail && <div>{detail}</div>}
|
||||
{badges && (
|
||||
<Group
|
||||
justify='center'
|
||||
gap='xs'
|
||||
align='flex-start'
|
||||
wrap='nowrap'
|
||||
>
|
||||
{badges?.map((badge, idx) => (
|
||||
<Fragment key={idx}>{badge}</Fragment>
|
||||
))}
|
||||
</Group>
|
||||
)}
|
||||
</Group>
|
||||
</Stack>
|
||||
</SimpleGrid>
|
||||
{actions && (
|
||||
<Group gap={5} justify='right' wrap='nowrap' align='flex-start'>
|
||||
{actions.map((action, idx) => (
|
||||
<Fragment key={idx}>{action}</Fragment>
|
||||
))}
|
||||
</Group>
|
||||
)}
|
||||
</Group>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</>
|
||||
|
@ -239,16 +239,14 @@ export default function BuildDetail() {
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
apiPath={ApiEndpoints.part_list}
|
||||
src={build.part_detail?.image ?? build.part_detail?.thumbnail}
|
||||
pk={build.part}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
apiPath={ApiEndpoints.part_list}
|
||||
src={build.part_detail?.image ?? build.part_detail?.thumbnail}
|
||||
pk={build.part}
|
||||
/>
|
||||
<Grid.Col span={{ base: 12, sm: 8 }}>
|
||||
<DetailsTable fields={tl} item={build} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
@ -145,22 +145,20 @@ export default function CompanyDetail(props: Readonly<CompanyDetailProps>) {
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.purchase_order}
|
||||
apiPath={apiUrl(ApiEndpoints.company_list, company.pk)}
|
||||
src={company.image}
|
||||
pk={company.pk}
|
||||
refresh={refreshInstance}
|
||||
imageActions={{
|
||||
uploadFile: true,
|
||||
downloadImage: true,
|
||||
deleteFile: true
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.purchase_order}
|
||||
apiPath={apiUrl(ApiEndpoints.company_list, company.pk)}
|
||||
src={company.image}
|
||||
pk={company.pk}
|
||||
refresh={refreshInstance}
|
||||
imageActions={{
|
||||
uploadFile: true,
|
||||
downloadImage: true,
|
||||
deleteFile: true
|
||||
}}
|
||||
/>
|
||||
<Grid.Col span={{ base: 12, sm: 8 }}>
|
||||
<DetailsTable item={company} fields={tl} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
@ -133,19 +133,17 @@ export default function ManufacturerPartDetail() {
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
src={manufacturerPart?.part_detail?.image}
|
||||
apiPath={apiUrl(
|
||||
ApiEndpoints.part_list,
|
||||
manufacturerPart?.part_detail?.pk
|
||||
)}
|
||||
pk={manufacturerPart?.part_detail?.pk}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
src={manufacturerPart?.part_detail?.image}
|
||||
apiPath={apiUrl(
|
||||
ApiEndpoints.part_list,
|
||||
manufacturerPart?.part_detail?.pk
|
||||
)}
|
||||
pk={manufacturerPart?.part_detail?.pk}
|
||||
/>
|
||||
<Grid.Col span={{ base: 12, sm: 8 }}>
|
||||
<DetailsTable title={t`Part Details`} fields={tl} item={data} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
@ -217,18 +217,16 @@ export default function SupplierPartDetail() {
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
src={supplierPart?.part_detail?.image}
|
||||
apiPath={apiUrl(
|
||||
ApiEndpoints.part_list,
|
||||
supplierPart?.part_detail?.pk
|
||||
)}
|
||||
pk={supplierPart?.part_detail?.pk}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
src={supplierPart?.part_detail?.image}
|
||||
apiPath={apiUrl(
|
||||
ApiEndpoints.part_list,
|
||||
supplierPart?.part_detail?.pk
|
||||
)}
|
||||
pk={supplierPart?.part_detail?.pk}
|
||||
/>
|
||||
<Grid.Col span={8}>
|
||||
<DetailsTable title={t`Part Details`} fields={tl} item={data} />
|
||||
</Grid.Col>
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
Grid,
|
||||
Loader,
|
||||
Skeleton,
|
||||
Space,
|
||||
Stack,
|
||||
Text
|
||||
} from '@mantine/core';
|
||||
@ -439,23 +438,21 @@ export default function PartDetail() {
|
||||
|
||||
return part ? (
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
imageActions={{
|
||||
selectExisting: true,
|
||||
downloadImage: true,
|
||||
uploadFile: true,
|
||||
deleteFile: true
|
||||
}}
|
||||
src={part.image}
|
||||
apiPath={apiUrl(ApiEndpoints.part_list, part.pk)}
|
||||
refresh={refreshInstance}
|
||||
pk={part.pk}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
imageActions={{
|
||||
selectExisting: true,
|
||||
downloadImage: true,
|
||||
uploadFile: true,
|
||||
deleteFile: true
|
||||
}}
|
||||
src={part.image}
|
||||
apiPath={apiUrl(ApiEndpoints.part_list, part.pk)}
|
||||
refresh={refreshInstance}
|
||||
pk={part.pk}
|
||||
/>
|
||||
<Grid.Col span={{ base: 12, sm: 8 }}>
|
||||
<DetailsTable fields={tl} item={data} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
@ -1040,9 +1037,7 @@ export default function PartDetail() {
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
) : (
|
||||
<Space />
|
||||
)
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
<PanelGroup
|
||||
|
@ -263,16 +263,14 @@ export default function PurchaseOrderDetail() {
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.purchase_order}
|
||||
apiPath={ApiEndpoints.company_list}
|
||||
src={order.supplier_detail?.image}
|
||||
pk={order.supplier}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.purchase_order}
|
||||
apiPath={ApiEndpoints.company_list}
|
||||
src={order.supplier_detail?.image}
|
||||
pk={order.supplier}
|
||||
/>
|
||||
<Grid.Col span={{ base: 12, sm: 8 }}>
|
||||
<DetailsTable fields={tl} item={order} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
@ -233,16 +233,14 @@ export default function ReturnOrderDetail() {
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.purchase_order}
|
||||
apiPath={ApiEndpoints.company_list}
|
||||
src={order.customer_detail?.image}
|
||||
pk={order.customer}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.purchase_order}
|
||||
apiPath={ApiEndpoints.company_list}
|
||||
src={order.customer_detail?.image}
|
||||
pk={order.customer}
|
||||
/>
|
||||
<Grid.Col span={{ base: 12, sm: 8 }}>
|
||||
<DetailsTable fields={tl} item={order} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
@ -243,16 +243,14 @@ export default function SalesOrderDetail() {
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.purchase_order}
|
||||
apiPath={ApiEndpoints.company_list}
|
||||
src={order.customer_detail?.image}
|
||||
pk={order.customer}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.purchase_order}
|
||||
apiPath={ApiEndpoints.company_list}
|
||||
src={order.customer_detail?.image}
|
||||
pk={order.customer}
|
||||
/>
|
||||
<Grid.Col span={{ base: 12, sm: 8 }}>
|
||||
<DetailsTable fields={tl} item={order} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
@ -175,22 +175,20 @@ export default function SalesOrderShipmentDetail() {
|
||||
return (
|
||||
<>
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.sales_order}
|
||||
apiPath={ApiEndpoints.company_list}
|
||||
src={customer?.image}
|
||||
pk={customer?.pk}
|
||||
imageActions={{
|
||||
selectExisting: false,
|
||||
downloadImage: false,
|
||||
uploadFile: false,
|
||||
deleteFile: false
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.sales_order}
|
||||
apiPath={ApiEndpoints.company_list}
|
||||
src={customer?.image}
|
||||
pk={customer?.pk}
|
||||
imageActions={{
|
||||
selectExisting: false,
|
||||
downloadImage: false,
|
||||
uploadFile: false,
|
||||
deleteFile: false
|
||||
}}
|
||||
/>
|
||||
<Grid.Col span={{ base: 12, sm: 8 }}>
|
||||
<DetailsTable fields={tl} item={data} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
@ -346,19 +346,16 @@ export default function StockDetail() {
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
apiPath={ApiEndpoints.part_list}
|
||||
src={
|
||||
stockitem.part_detail?.image ??
|
||||
stockitem?.part_detail?.thumbnail
|
||||
}
|
||||
pk={stockitem.part}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Grid grow>
|
||||
<DetailsImage
|
||||
appRole={UserRoles.part}
|
||||
apiPath={ApiEndpoints.part_list}
|
||||
src={
|
||||
stockitem.part_detail?.image ?? stockitem?.part_detail?.thumbnail
|
||||
}
|
||||
pk={stockitem.part}
|
||||
/>
|
||||
<Grid.Col span={{ base: 12, sm: 8 }}>
|
||||
<DetailsTable fields={tl} item={data} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
Loading…
x
Reference in New Issue
Block a user