Implement CompanyDetails page

This commit is contained in:
Oliver 2024-03-01 01:37:35 +00:00
parent 64109a7974
commit b674fc980d
3 changed files with 120 additions and 4 deletions

View File

@ -5,6 +5,7 @@ import {
IconBox, IconBox,
IconBuilding, IconBuilding,
IconBuildingFactory2, IconBuildingFactory2,
IconBuildingStore,
IconCalendar, IconCalendar,
IconCalendarStats, IconCalendarStats,
IconCheck, IconCheck,
@ -22,6 +23,7 @@ import {
IconLink, IconLink,
IconList, IconList,
IconListTree, IconListTree,
IconMail,
IconMapPin, IconMapPin,
IconMapPinHeart, IconMapPinHeart,
IconNotes, IconNotes,
@ -30,6 +32,7 @@ import {
IconPackageImport, IconPackageImport,
IconPackages, IconPackages,
IconPaperclip, IconPaperclip,
IconPhone,
IconPhoto, IconPhoto,
IconProgressCheck, IconProgressCheck,
IconQuestionMark, IconQuestionMark,
@ -49,6 +52,7 @@ import {
IconUserStar, IconUserStar,
IconUsersGroup, IconUsersGroup,
IconVersions, IconVersions,
IconWorld,
IconWorldCode, IconWorldCode,
IconX IconX
} from '@tabler/icons-react'; } from '@tabler/icons-react';
@ -87,6 +91,7 @@ const icons: { [key: string]: (props: TablerIconsProps) => React.JSX.Element } =
used_in: IconStack2, used_in: IconStack2,
manufacturers: IconBuildingFactory2, manufacturers: IconBuildingFactory2,
suppliers: IconBuilding, suppliers: IconBuilding,
customers: IconBuildingStore,
purchase_orders: IconShoppingCart, purchase_orders: IconShoppingCart,
sales_orders: IconTruckDelivery, sales_orders: IconTruckDelivery,
scheduling: IconCalendarStats, scheduling: IconCalendarStats,
@ -121,6 +126,7 @@ const icons: { [key: string]: (props: TablerIconsProps) => React.JSX.Element } =
link: IconLink, link: IconLink,
responsible: IconUserStar, responsible: IconUserStar,
pricing: IconCurrencyDollar, pricing: IconCurrencyDollar,
currency: IconCurrencyDollar,
stocktake: IconClipboardList, stocktake: IconClipboardList,
user: IconUser, user: IconUser,
group: IconUsersGroup, group: IconUsersGroup,
@ -128,7 +134,10 @@ const icons: { [key: string]: (props: TablerIconsProps) => React.JSX.Element } =
copy: IconCopy, copy: IconCopy,
quantity: IconNumbers, quantity: IconNumbers,
progress: IconProgressCheck, progress: IconProgressCheck,
reference: IconHash reference: IconHash,
website: IconWorld,
email: IconMail,
phone: IconPhone
}; };
/** /**

View File

@ -1,5 +1,5 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { LoadingOverlay, Skeleton, Stack } from '@mantine/core'; import { Grid, LoadingOverlay, Skeleton, Stack } from '@mantine/core';
import { import {
IconBuildingFactory2, IconBuildingFactory2,
IconBuildingWarehouse, IconBuildingWarehouse,
@ -18,6 +18,8 @@ import {
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { DetailsImage } from '../../components/details/DetailsImage';
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
import { import {
ActionDropdown, ActionDropdown,
DeleteItemAction, DeleteItemAction,
@ -35,6 +37,7 @@ import { useEditApiFormModal } from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import { apiUrl } from '../../states/ApiState'; import { apiUrl } from '../../states/ApiState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { DetailsField, DetailsTable } from '../../tables/Details';
import { AddressTable } from '../../tables/company/AddressTable'; import { AddressTable } from '../../tables/company/AddressTable';
import { ContactTable } from '../../tables/company/ContactTable'; import { ContactTable } from '../../tables/company/ContactTable';
import { AttachmentTable } from '../../tables/general/AttachmentTable'; import { AttachmentTable } from '../../tables/general/AttachmentTable';
@ -69,12 +72,99 @@ export default function CompanyDetail(props: CompanyDetailProps) {
refetchOnMount: true refetchOnMount: true
}); });
const detailsPanel = useMemo(() => {
if (instanceQuery.isFetching) {
return <Skeleton />;
}
let tl: DetailsField[] = [
{
type: 'text',
name: 'description',
label: t`Description`
},
{
type: 'link',
name: 'website',
label: t`Website`,
external: true,
copy: true,
hidden: !company.website
},
{
type: 'text',
name: 'phone',
label: t`Phone Number`,
copy: true,
hidden: !company.phone
},
{
type: 'text',
name: 'email',
label: t`Email Address`,
copy: true,
hidden: !company.email
}
];
let tr: DetailsField[] = [
{
type: 'string',
name: 'currency',
label: t`Default Currency`
},
{
type: 'boolean',
name: 'is_supplier',
label: t`Supplier`,
icon: 'suppliers'
},
{
type: 'boolean',
name: 'is_manufacturer',
label: t`Manufacturer`,
icon: 'manufacturers'
},
{
type: 'boolean',
name: 'is_customer',
label: t`Customer`,
icon: 'customers'
}
];
return (
<ItemDetailsGrid>
<Grid>
<Grid.Col span={4}>
<DetailsImage
appRole={UserRoles.purchase_order}
apiPath={ApiEndpoints.company_list}
src={company.image}
pk={company.pk}
refresh={refreshInstance}
imageActions={{
uploadFile: true,
deleteFile: true
}}
/>
</Grid.Col>
<Grid.Col span={8}>
<DetailsTable item={company} fields={tl} />
</Grid.Col>
</Grid>
<DetailsTable item={company} fields={tr} />
</ItemDetailsGrid>
);
}, [company, instanceQuery]);
const companyPanels: PanelType[] = useMemo(() => { const companyPanels: PanelType[] = useMemo(() => {
return [ return [
{ {
name: 'details', name: 'details',
label: t`Details`, label: t`Details`,
icon: <IconInfoCircle /> icon: <IconInfoCircle />,
content: detailsPanel
}, },
{ {
name: 'manufactured-parts', name: 'manufactured-parts',

View File

@ -16,6 +16,7 @@ import { Suspense, useMemo } from 'react';
import { api } from '../App'; import { api } from '../App';
import { ProgressBar } from '../components/items/ProgressBar'; import { ProgressBar } from '../components/items/ProgressBar';
import { YesNoButton } from '../components/items/YesNoButton';
import { getModelInfo } from '../components/render/ModelType'; import { getModelInfo } from '../components/render/ModelType';
import { StatusRenderer } from '../components/render/StatusRenderer'; import { StatusRenderer } from '../components/render/StatusRenderer';
import { ApiEndpoints } from '../enums/ApiEndpoints'; import { ApiEndpoints } from '../enums/ApiEndpoints';
@ -45,7 +46,13 @@ export type DetailsField =
badge?: BadgeType; badge?: BadgeType;
copy?: boolean; copy?: boolean;
value_formatter?: () => ValueFormatterReturn; value_formatter?: () => ValueFormatterReturn;
} & (StringDetailField | LinkDetailField | ProgressBarfield | StatusField); } & (
| StringDetailField
| BooleanField
| LinkDetailField
| ProgressBarfield
| StatusField
);
type BadgeType = 'owner' | 'user' | 'group'; type BadgeType = 'owner' | 'user' | 'group';
type ValueFormatterReturn = string | number | null; type ValueFormatterReturn = string | number | null;
@ -55,6 +62,10 @@ type StringDetailField = {
unit?: boolean; unit?: boolean;
}; };
type BooleanField = {
type: 'boolean';
};
type LinkDetailField = { type LinkDetailField = {
type: 'link'; type: 'link';
} & (InternalLinkField | ExternalLinkField); } & (InternalLinkField | ExternalLinkField);
@ -288,6 +299,10 @@ function TableStringValue(props: FieldProps) {
); );
} }
function BooleanValue(props: FieldProps) {
return <YesNoButton value={props.field_value} />;
}
function TableAnchorValue(props: FieldProps) { function TableAnchorValue(props: FieldProps) {
if (props.field_data.external) { if (props.field_data.external) {
return ( return (
@ -404,6 +419,8 @@ export function DetailsTableField({
case 'text': case 'text':
case 'string': case 'string':
return TableStringValue; return TableStringValue;
case 'boolean':
return BooleanValue;
case 'link': case 'link':
return TableAnchorValue; return TableAnchorValue;
case 'progressbar': case 'progressbar':