From e4c5bfc2fe97ce5d4d5167021b56717c392af448 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 29 Nov 2023 14:05:22 +1100 Subject: [PATCH] More tables (#5999) * Move CompanyTable * Add "Contacts" table to company detail page * Typo fix * Remove 'notes' column * Add preFormWarning and preFormSuccess to API form - Refactor existing deletion forms * Adds "address" table for company view * Prevent wrapping on inline model rendereing * Refactor new tables * Fix unused imports --- src/frontend/src/components/forms/ApiForm.tsx | 15 ++ .../src/components/items/YesNoButton.tsx | 10 +- .../src/components/render/Instance.tsx | 4 +- .../src/components/tables/bom/BomTable.tsx | 4 +- .../tables/company/AddressTable.tsx | 196 ++++++++++++++++++ .../{general => company}/CompanyTable.tsx | 0 .../tables/company/ContactTable.tsx | 144 +++++++++++++ .../tables/part/PartParameterTable.tsx | 4 +- .../part/PartParameterTemplateTable.tsx | 5 +- .../tables/part/PartVariantTable.tsx | 2 +- .../tables/part/RelatedPartTable.tsx | 4 +- .../tables/purchasing/SupplierPartTable.tsx | 4 +- .../tables/settings/CustomUnitsTable.tsx | 5 +- .../components/tables/settings/GroupTable.tsx | 5 +- .../tables/settings/ProjectCodeTable.tsx | 5 +- .../components/tables/settings/UserTable.tsx | 5 +- src/frontend/src/forms/AttachmentForms.tsx | 5 +- src/frontend/src/forms/CompanyForms.tsx | 31 +++ .../src/pages/company/CompanyDetail.tsx | 8 +- .../src/pages/purchasing/PurchasingIndex.tsx | 2 +- src/frontend/src/pages/sales/SalesIndex.tsx | 2 +- 21 files changed, 415 insertions(+), 45 deletions(-) create mode 100644 src/frontend/src/components/tables/company/AddressTable.tsx rename src/frontend/src/components/tables/{general => company}/CompanyTable.tsx (100%) create mode 100644 src/frontend/src/components/tables/company/ContactTable.tsx diff --git a/src/frontend/src/components/forms/ApiForm.tsx b/src/frontend/src/components/forms/ApiForm.tsx index 0231f8c649..b7475eb278 100644 --- a/src/frontend/src/components/forms/ApiForm.tsx +++ b/src/frontend/src/components/forms/ApiForm.tsx @@ -70,6 +70,8 @@ export interface ApiFormProps { fetchInitialData?: boolean; ignorePermissionCheck?: boolean; preFormContent?: JSX.Element; + preFormWarning?: string; + preFormSuccess?: string; postFormContent?: JSX.Element; successMessage?: string; onFormSuccess?: (data: any) => void; @@ -287,7 +289,10 @@ export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) { // Optionally show a success message if (props.successMessage) { + notifications.hide('form-success'); + notifications.show({ + id: 'form-success', title: t`Success`, message: props.successMessage, color: 'green' @@ -371,6 +376,16 @@ export function ApiForm({ id, props }: { id: string; props: ApiFormProps }) { )} {props.preFormContent} + {props.preFormSuccess && ( + + {props.preFormSuccess} + + )} + {props.preFormWarning && ( + + {props.preFormWarning} + + )} {Object.entries(props.fields ?? {}).map(([fieldName, field]) => ( - {value ? t`Yes` : t`No`} + {v ? t`Yes` : t`No`} ); } diff --git a/src/frontend/src/components/render/Instance.tsx b/src/frontend/src/components/render/Instance.tsx index 17efca6512..490874b4af 100644 --- a/src/frontend/src/components/render/Instance.tsx +++ b/src/frontend/src/components/render/Instance.tsx @@ -110,8 +110,8 @@ export function RenderInlineModel({ // TODO: Handle URL return ( - - + + {image && Thumbnail({ src: image, size: 18 })} {primary} {secondary && {secondary}} diff --git a/src/frontend/src/components/tables/bom/BomTable.tsx b/src/frontend/src/components/tables/bom/BomTable.tsx index ee7f340615..a9bc316fa6 100644 --- a/src/frontend/src/components/tables/bom/BomTable.tsx +++ b/src/frontend/src/components/tables/bom/BomTable.tsx @@ -306,9 +306,7 @@ export function BomTable({ title: t`Delete Bom Item`, successMessage: t`Bom item deleted`, onFormSuccess: table.refreshTable, - preFormContent: ( - {t`Are you sure you want to remove this BOM item?`} - ) + preFormWarning: t`Are you sure you want to remove this BOM item?` }); } }) diff --git a/src/frontend/src/components/tables/company/AddressTable.tsx b/src/frontend/src/components/tables/company/AddressTable.tsx new file mode 100644 index 0000000000..35e0cbdeab --- /dev/null +++ b/src/frontend/src/components/tables/company/AddressTable.tsx @@ -0,0 +1,196 @@ +import { t } from '@lingui/macro'; +import { useCallback, useMemo } from 'react'; + +import { ApiPaths } from '../../../enums/ApiEndpoints'; +import { UserRoles } from '../../../enums/Roles'; +import { addressFields } from '../../../forms/CompanyForms'; +import { + openCreateApiForm, + openDeleteApiForm, + openEditApiForm +} from '../../../functions/forms'; +import { useTable } from '../../../hooks/UseTable'; +import { apiUrl } from '../../../states/ApiState'; +import { useUserState } from '../../../states/UserState'; +import { AddItemButton } from '../../buttons/AddItemButton'; +import { YesNoButton } from '../../items/YesNoButton'; +import { TableColumn } from '../Column'; +import { InvenTreeTable } from '../InvenTreeTable'; +import { RowDeleteAction, RowEditAction } from '../RowActions'; + +export function AddressTable({ + companyId, + params +}: { + companyId: number; + params?: any; +}) { + const user = useUserState(); + + const table = useTable('address'); + + const columns: TableColumn[] = useMemo(() => { + return [ + { + accessor: 'title', + title: t`Title`, + sortable: true, + switchable: false + }, + { + accessor: 'primary', + title: t`Primary`, + switchable: false, + sortable: false, + render: (record: any) => YesNoButton({ value: record.primary }) + }, + { + accessor: 'address', + title: t`Address`, + sortable: false, + switchable: false, + render: (record: any) => { + let address = ''; + + if (record?.line1) { + address += record.line1; + } + + if (record?.line2) { + address += ' ' + record.line2; + } + + return address.trim(); + } + }, + { + accessor: 'postal_code', + title: t`Postal Code`, + sortable: false, + switchable: true + }, + { + accessor: 'postal_city', + title: t`City`, + sortable: false, + switchable: true + }, + { + accessor: 'province', + title: t`State / Province`, + sortable: false, + switchable: true + }, + { + accessor: 'country', + title: t`Country`, + sortable: false, + switchable: true + }, + { + accessor: 'shipping_notes', + title: t`Courier Notes`, + sortable: false, + switchable: true + }, + { + accessor: 'internal_shipping_notes', + title: t`Internal Notes`, + sortable: false, + switchable: true + }, + { + accessor: 'link', + title: t`Link`, + sortable: false, + switchable: true + } + ]; + }, []); + + const rowActions = useCallback( + (record: any) => { + let can_edit = + user.hasChangeRole(UserRoles.purchase_order) || + user.hasChangeRole(UserRoles.sales_order); + + let can_delete = + user.hasDeleteRole(UserRoles.purchase_order) || + user.hasDeleteRole(UserRoles.sales_order); + + return [ + RowEditAction({ + hidden: !can_edit, + onClick: () => { + openEditApiForm({ + url: ApiPaths.address_list, + pk: record.pk, + title: t`Edit Address`, + fields: addressFields(), + successMessage: t`Address updated`, + onFormSuccess: table.refreshTable + }); + } + }), + RowDeleteAction({ + hidden: !can_delete, + onClick: () => { + openDeleteApiForm({ + url: ApiPaths.address_list, + pk: record.pk, + title: t`Delete Address`, + successMessage: t`Address deleted`, + onFormSuccess: table.refreshTable, + preFormWarning: t`Are you sure you want to delete this address?` + }); + } + }) + ]; + }, + [user] + ); + + const addAddress = useCallback(() => { + let fields = addressFields(); + + fields['company'].value = companyId; + + openCreateApiForm({ + url: ApiPaths.address_list, + title: t`Add Address`, + fields: fields, + successMessage: t`Address created`, + onFormSuccess: table.refreshTable + }); + }, [companyId]); + + const tableActions = useMemo(() => { + let can_add = + user.hasChangeRole(UserRoles.purchase_order) || + user.hasChangeRole(UserRoles.sales_order); + + return [ + + ]; + }, [user]); + + return ( + + ); +} diff --git a/src/frontend/src/components/tables/general/CompanyTable.tsx b/src/frontend/src/components/tables/company/CompanyTable.tsx similarity index 100% rename from src/frontend/src/components/tables/general/CompanyTable.tsx rename to src/frontend/src/components/tables/company/CompanyTable.tsx diff --git a/src/frontend/src/components/tables/company/ContactTable.tsx b/src/frontend/src/components/tables/company/ContactTable.tsx new file mode 100644 index 0000000000..7005abea09 --- /dev/null +++ b/src/frontend/src/components/tables/company/ContactTable.tsx @@ -0,0 +1,144 @@ +import { t } from '@lingui/macro'; +import { useCallback, useMemo } from 'react'; + +import { ApiPaths } from '../../../enums/ApiEndpoints'; +import { UserRoles } from '../../../enums/Roles'; +import { contactFields } from '../../../forms/CompanyForms'; +import { + openCreateApiForm, + openDeleteApiForm, + openEditApiForm +} from '../../../functions/forms'; +import { useTable } from '../../../hooks/UseTable'; +import { apiUrl } from '../../../states/ApiState'; +import { useUserState } from '../../../states/UserState'; +import { AddItemButton } from '../../buttons/AddItemButton'; +import { TableColumn } from '../Column'; +import { InvenTreeTable } from '../InvenTreeTable'; +import { RowDeleteAction, RowEditAction } from '../RowActions'; + +export function ContactTable({ + companyId, + params +}: { + companyId: number; + params?: any; +}) { + const user = useUserState(); + + const table = useTable('contact'); + + const columns: TableColumn[] = useMemo(() => { + return [ + { + accessor: 'name', + title: t`Name`, + sortable: true, + switchable: false + }, + { + accessor: 'phone', + title: t`Phone`, + switchable: true, + sortable: false + }, + { + accessor: 'email', + title: t`Email`, + switchable: true, + sortable: false + }, + { + accessor: 'role', + title: t`Role`, + switchable: true, + sortable: false + } + ]; + }, []); + + const rowActions = useCallback( + (record: any) => { + let can_edit = + user.hasChangeRole(UserRoles.purchase_order) || + user.hasChangeRole(UserRoles.sales_order); + let can_delete = + user.hasDeleteRole(UserRoles.purchase_order) || + user.hasDeleteRole(UserRoles.sales_order); + + return [ + RowEditAction({ + hidden: !can_edit, + onClick: () => { + openEditApiForm({ + url: ApiPaths.contact_list, + pk: record.pk, + title: t`Edit Contact`, + fields: contactFields(), + successMessage: t`Contact updated`, + onFormSuccess: table.refreshTable + }); + } + }), + RowDeleteAction({ + hidden: !can_delete, + onClick: () => { + openDeleteApiForm({ + url: ApiPaths.contact_list, + pk: record.pk, + title: t`Delete Contact`, + successMessage: t`Contact deleted`, + onFormSuccess: table.refreshTable, + preFormWarning: t`Are you sure you want to delete this contact?` + }); + } + }) + ]; + }, + [user] + ); + + const addContact = useCallback(() => { + var fields = contactFields(); + + fields['company'].value = companyId; + + openCreateApiForm({ + url: ApiPaths.contact_list, + title: t`Create Contact`, + fields: fields, + successMessage: t`Contact created`, + onFormSuccess: table.refreshTable + }); + }, [companyId]); + + const tableActions = useMemo(() => { + let can_add = + user.hasAddRole(UserRoles.purchase_order) || + user.hasAddRole(UserRoles.sales_order); + + return [ + + ]; + }, [user]); + + return ( + + ); +} diff --git a/src/frontend/src/components/tables/part/PartParameterTable.tsx b/src/frontend/src/components/tables/part/PartParameterTable.tsx index 62141499a4..54788134b1 100644 --- a/src/frontend/src/components/tables/part/PartParameterTable.tsx +++ b/src/frontend/src/components/tables/part/PartParameterTable.tsx @@ -140,9 +140,7 @@ export function PartParameterTable({ partId }: { partId: any }) { title: t`Delete Part Parameter`, successMessage: t`Part parameter deleted`, onFormSuccess: table.refreshTable, - preFormContent: ( - {t`Are you sure you want to remove this parameter?`} - ) + preFormWarning: t`Are you sure you want to remove this parameter?` }); } }) diff --git a/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx b/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx index 7610b2f655..e9de3cf6f3 100644 --- a/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx +++ b/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx @@ -1,5 +1,4 @@ import { t } from '@lingui/macro'; -import { Text } from '@mantine/core'; import { useCallback, useMemo } from 'react'; import { ApiPaths } from '../../../enums/ApiEndpoints'; @@ -39,7 +38,7 @@ export function PartParameterTemplateTable() { { accessor: 'description', title: t`Description`, - sortbale: false + sortable: false }, { accessor: 'checkbox', @@ -78,7 +77,7 @@ export function PartParameterTemplateTable() { title: t`Delete Parameter Template`, successMessage: t`Parameter template deleted`, onFormSuccess: table.refreshTable, - preFormContent: {t`Remove parameter template`} + preFormWarning: t`Are you sure you want to remove this parameter template?` }); } }) diff --git a/src/frontend/src/components/tables/part/PartVariantTable.tsx b/src/frontend/src/components/tables/part/PartVariantTable.tsx index 169711570d..9f768a0d9d 100644 --- a/src/frontend/src/components/tables/part/PartVariantTable.tsx +++ b/src/frontend/src/components/tables/part/PartVariantTable.tsx @@ -1,7 +1,7 @@ import { PartListTable } from './PartTable'; /** - * Display variant parts for thespecified parent part + * Display variant parts for the specified parent part */ export function PartVariantTable({ partId }: { partId: string }) { return ( diff --git a/src/frontend/src/components/tables/part/RelatedPartTable.tsx b/src/frontend/src/components/tables/part/RelatedPartTable.tsx index 7ce2454c9e..1a35f82fac 100644 --- a/src/frontend/src/components/tables/part/RelatedPartTable.tsx +++ b/src/frontend/src/components/tables/part/RelatedPartTable.tsx @@ -112,9 +112,7 @@ export function RelatedPartTable({ partId }: { partId: number }): ReactNode { pk: record.pk, title: t`Delete Related Part`, successMessage: t`Related part deleted`, - preFormContent: ( - {t`Are you sure you want to remove this relationship?`} - ), + preFormWarning: t`Are you sure you want to remove this relationship?`, onFormSuccess: table.refreshTable }); } diff --git a/src/frontend/src/components/tables/purchasing/SupplierPartTable.tsx b/src/frontend/src/components/tables/purchasing/SupplierPartTable.tsx index 6dd65efc22..3ac913ef14 100644 --- a/src/frontend/src/components/tables/purchasing/SupplierPartTable.tsx +++ b/src/frontend/src/components/tables/purchasing/SupplierPartTable.tsx @@ -210,9 +210,7 @@ export function SupplierPartTable({ params }: { params: any }): ReactNode { title: t`Delete Supplier Part`, successMessage: t`Supplier part deleted`, onFormSuccess: table.refreshTable, - preFormContent: ( - {t`Are you sure you want to remove this supplier part?`} - ) + preFormWarning: t`Are you sure you want to remove this supplier part?` }); } }) diff --git a/src/frontend/src/components/tables/settings/CustomUnitsTable.tsx b/src/frontend/src/components/tables/settings/CustomUnitsTable.tsx index 64ddc27f31..e5f8bdd2a8 100644 --- a/src/frontend/src/components/tables/settings/CustomUnitsTable.tsx +++ b/src/frontend/src/components/tables/settings/CustomUnitsTable.tsx @@ -1,5 +1,4 @@ import { t } from '@lingui/macro'; -import { Text } from '@mantine/core'; import { useCallback, useMemo } from 'react'; import { ApiPaths } from '../../../enums/ApiEndpoints'; @@ -77,9 +76,7 @@ export function CustomUnitsTable() { title: t`Delete custom unit`, successMessage: t`Custom unit deleted`, onFormSuccess: table.refreshTable, - preFormContent: ( - {t`Are you sure you want to remove this custom unit?`} - ) + preFormWarning: t`Are you sure you want to remove this custom unit?` }); } }) diff --git a/src/frontend/src/components/tables/settings/GroupTable.tsx b/src/frontend/src/components/tables/settings/GroupTable.tsx index 2a862560de..5b35cd0968 100644 --- a/src/frontend/src/components/tables/settings/GroupTable.tsx +++ b/src/frontend/src/components/tables/settings/GroupTable.tsx @@ -1,5 +1,4 @@ import { t } from '@lingui/macro'; -import { Text } from '@mantine/core'; import { useCallback, useMemo } from 'react'; import { ApiPaths } from '../../../enums/ApiEndpoints'; @@ -55,9 +54,7 @@ export function GroupTable() { title: t`Delete group`, successMessage: t`Group deleted`, onFormSuccess: table.refreshTable, - preFormContent: ( - {t`Are you sure you want to delete this group?`} - ) + preFormWarning: t`Are you sure you want to delete this group?` }); } }) diff --git a/src/frontend/src/components/tables/settings/ProjectCodeTable.tsx b/src/frontend/src/components/tables/settings/ProjectCodeTable.tsx index a7044f1d4e..27fa83c2b1 100644 --- a/src/frontend/src/components/tables/settings/ProjectCodeTable.tsx +++ b/src/frontend/src/components/tables/settings/ProjectCodeTable.tsx @@ -1,5 +1,4 @@ import { t } from '@lingui/macro'; -import { Text } from '@mantine/core'; import { useCallback, useMemo } from 'react'; import { ApiPaths } from '../../../enums/ApiEndpoints'; @@ -67,9 +66,7 @@ export function ProjectCodeTable() { title: t`Delete project code`, successMessage: t`Project code deleted`, onFormSuccess: table.refreshTable, - preFormContent: ( - {t`Are you sure you want to remove this project code?`} - ) + preFormWarning: t`Are you sure you want to remove this project code?` }); } }) diff --git a/src/frontend/src/components/tables/settings/UserTable.tsx b/src/frontend/src/components/tables/settings/UserTable.tsx index e0a5be495e..5058d5c0f9 100644 --- a/src/frontend/src/components/tables/settings/UserTable.tsx +++ b/src/frontend/src/components/tables/settings/UserTable.tsx @@ -1,5 +1,4 @@ import { t } from '@lingui/macro'; -import { Text } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { useCallback, useMemo, useState } from 'react'; @@ -116,9 +115,7 @@ export function UserTable() { title: t`Delete user`, successMessage: t`User deleted`, onFormSuccess: table.refreshTable, - preFormContent: ( - {t`Are you sure you want to delete this user?`} - ) + preFormWarning: t`Are you sure you want to delete this user?` }); } }) diff --git a/src/frontend/src/forms/AttachmentForms.tsx b/src/frontend/src/forms/AttachmentForms.tsx index 3a57c6af98..deaa0b00b7 100644 --- a/src/frontend/src/forms/AttachmentForms.tsx +++ b/src/frontend/src/forms/AttachmentForms.tsx @@ -1,5 +1,4 @@ import { t } from '@lingui/macro'; -import { Text } from '@mantine/core'; import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField'; import { ApiPaths } from '../enums/ApiEndpoints'; @@ -126,8 +125,6 @@ export function deleteAttachment({ successMessage: t`Attachment deleted`, onFormSuccess: callback, fields: {}, - preFormContent: ( - {t`Are you sure you want to delete this attachment?`} - ) + preFormWarning: t`Are you sure you want to delete this attachment?` }); } diff --git a/src/frontend/src/forms/CompanyForms.tsx b/src/frontend/src/forms/CompanyForms.tsx index 0cc178cc67..344b6056b6 100644 --- a/src/frontend/src/forms/CompanyForms.tsx +++ b/src/frontend/src/forms/CompanyForms.tsx @@ -125,3 +125,34 @@ export function editCompany({ onFormSuccess: callback }); } + +export function contactFields(): ApiFormFieldSet { + return { + company: { + hidden: true + }, + name: {}, + phone: {}, + email: {}, + role: {} + }; +} + +export function addressFields(): ApiFormFieldSet { + return { + company: { + hidden: true + }, + title: {}, + primary: {}, + line1: {}, + line2: {}, + postal_code: {}, + postal_city: {}, + province: {}, + country: {}, + shipping_notes: {}, + internal_shipping_notes: {}, + link: {} + }; +} diff --git a/src/frontend/src/pages/company/CompanyDetail.tsx b/src/frontend/src/pages/company/CompanyDetail.tsx index 74db811658..38c4c27f93 100644 --- a/src/frontend/src/pages/company/CompanyDetail.tsx +++ b/src/frontend/src/pages/company/CompanyDetail.tsx @@ -27,6 +27,8 @@ import { Breadcrumb } from '../../components/nav/BreadcrumbList'; import { PageDetail } from '../../components/nav/PageDetail'; import { PanelGroup } from '../../components/nav/PanelGroup'; import { PanelType } from '../../components/nav/PanelGroup'; +import { AddressTable } from '../../components/tables/company/AddressTable'; +import { ContactTable } from '../../components/tables/company/ContactTable'; import { AttachmentTable } from '../../components/tables/general/AttachmentTable'; import { PurchaseOrderTable } from '../../components/tables/purchasing/PurchaseOrderTable'; import { ReturnOrderTable } from '../../components/tables/sales/ReturnOrderTable'; @@ -128,12 +130,14 @@ export default function CompanyDetail(props: CompanyDetailProps) { { name: 'contacts', label: t`Contacts`, - icon: + icon: , + content: company?.pk && }, { name: 'addresses', label: t`Addresses`, - icon: + icon: , + content: company?.pk && }, { name: 'attachments', diff --git a/src/frontend/src/pages/purchasing/PurchasingIndex.tsx b/src/frontend/src/pages/purchasing/PurchasingIndex.tsx index 4209d76a1e..31a7234e33 100644 --- a/src/frontend/src/pages/purchasing/PurchasingIndex.tsx +++ b/src/frontend/src/pages/purchasing/PurchasingIndex.tsx @@ -9,7 +9,7 @@ import { useMemo } from 'react'; import { PageDetail } from '../../components/nav/PageDetail'; import { PanelGroup } from '../../components/nav/PanelGroup'; -import { CompanyTable } from '../../components/tables/general/CompanyTable'; +import { CompanyTable } from '../../components/tables/company/CompanyTable'; import { PurchaseOrderTable } from '../../components/tables/purchasing/PurchaseOrderTable'; export default function PurchasingIndex() { diff --git a/src/frontend/src/pages/sales/SalesIndex.tsx b/src/frontend/src/pages/sales/SalesIndex.tsx index 0bfe2a5b74..441ba0c5bd 100644 --- a/src/frontend/src/pages/sales/SalesIndex.tsx +++ b/src/frontend/src/pages/sales/SalesIndex.tsx @@ -9,7 +9,7 @@ import { useMemo } from 'react'; import { PageDetail } from '../../components/nav/PageDetail'; import { PanelGroup } from '../../components/nav/PanelGroup'; -import { CompanyTable } from '../../components/tables/general/CompanyTable'; +import { CompanyTable } from '../../components/tables/company/CompanyTable'; import { ReturnOrderTable } from '../../components/tables/sales/ReturnOrderTable'; import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';