From 4e454f4c997eb50bb00ddd4959bbcdae19267a21 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 24 Oct 2025 15:30:15 +1100 Subject: [PATCH] [UI] User column (#10661) * Refactor user display columns * More refactoring --- src/frontend/src/tables/ColumnRenderers.tsx | 92 ++++++++++++++++--- .../src/tables/build/BuildOrderTable.tsx | 16 ++-- .../src/tables/part/PartParameterTable.tsx | 22 ++--- .../src/tables/settings/ApiTokenTable.tsx | 22 ++--- .../tables/stock/StockItemTestResultTable.tsx | 19 ++-- 5 files changed, 109 insertions(+), 62 deletions(-) diff --git a/src/frontend/src/tables/ColumnRenderers.tsx b/src/frontend/src/tables/ColumnRenderers.tsx index ce27f54432..73ccd8ef04 100644 --- a/src/frontend/src/tables/ColumnRenderers.tsx +++ b/src/frontend/src/tables/ColumnRenderers.tsx @@ -2,7 +2,15 @@ * Common rendering functions for table column data. */ import { t } from '@lingui/core/macro'; -import { Anchor, Center, Group, Skeleton, Text, Tooltip } from '@mantine/core'; +import { + Anchor, + Badge, + Center, + Group, + Skeleton, + Text, + Tooltip +} from '@mantine/core'; import { IconBell, IconExclamationCircle, @@ -16,9 +24,10 @@ import type { ModelType } from '@lib/enums/ModelType'; import { resolveItem } from '@lib/functions/Conversion'; import { cancelEvent } from '@lib/functions/Events'; import type { TableColumn, TableColumnProps } from '@lib/types/Tables'; +import type { ReactNode } from 'react'; import { Thumbnail } from '../components/images/Thumbnail'; import { TableStatusRenderer } from '../components/render/StatusRenderer'; -import { RenderOwner, RenderUser } from '../components/render/User'; +import { RenderOwner } from '../components/render/User'; import { formatCurrency, formatDate, @@ -380,28 +389,81 @@ export function StatusColumn(props: StatusColumnProps): TableColumn { }; } -export function CreatedByColumn(props: TableColumnProps): TableColumn { +export function UserColumn(props: TableColumnProps): TableColumn { return { - accessor: 'created_by', - title: t`Created By`, + accessor: 'user', + title: t`User`, sortable: true, switchable: true, - render: (record: any) => - record.created_by && RenderUser({ instance: record.created_by }), + render: (record: any) => { + const instance = resolveItem(record, props.accessor ?? 'user_detail'); + if (instance) { + const extra: ReactNode[] = [ + + {instance.first_name} {instance.last_name} + + ]; + + if (instance.is_active === false) { + extra.push( + + {t`Inactive`} + + ); + } + + return ( + + ); + } else { + return '-'; + } + }, + ...props + }; +} + +export function CreatedByColumn(props: TableColumnProps): TableColumn { + return UserColumn({ + accessor: 'created_by', + ordering: 'created_by', + title: t`Created By`, + ...props + }); +} + +export function OwnerColumn(props: TableColumnProps): TableColumn { + return { + accessor: 'owner_detail', + ordering: 'owner', + title: t`Owner`, + sortable: true, + switchable: true, + render: (record: any) => { + const instance = resolveItem(record, props.accessor ?? 'owner_detail'); + + if (instance) { + return ; + } else { + return '-'; + } + }, ...props }; } export function ResponsibleColumn(props: TableColumnProps): TableColumn { - return { - accessor: 'responsible', - sortable: true, - switchable: true, - render: (record: any) => - record.responsible && - RenderOwner({ instance: record.responsible_detail }), + return OwnerColumn({ + accessor: 'responsible_detail', + ordering: 'responsible', + title: t`Responsible`, ...props - }; + }); } export function DateColumn(props: TableColumnProps): TableColumn { diff --git a/src/frontend/src/tables/build/BuildOrderTable.tsx b/src/frontend/src/tables/build/BuildOrderTable.tsx index afc265a98f..4a3fcc7ccc 100644 --- a/src/frontend/src/tables/build/BuildOrderTable.tsx +++ b/src/frontend/src/tables/build/BuildOrderTable.tsx @@ -8,7 +8,6 @@ import { ModelType } from '@lib/enums/ModelType'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import type { TableFilter } from '@lib/types/Filters'; -import { RenderUser } from '../../components/render/User'; import { useBuildOrderFields } from '../../forms/BuildForms'; import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useTable } from '../../hooks/UseTable'; @@ -25,7 +24,8 @@ import { ResponsibleColumn, StartDateColumn, StatusColumn, - TargetDateColumn + TargetDateColumn, + UserColumn } from '../ColumnRenderers'; import { AssignedToMeFilter, @@ -137,13 +137,11 @@ export function BuildOrderTable({ title: t`Completion Date`, sortable: true }), - { - accessor: 'issued_by', - sortable: true, - render: (record: any) => ( - - ) - }, + UserColumn({ + accessor: 'issued_by_detail', + ordering: 'issued_by', + title: t`Issued By` + }), ResponsibleColumn({}) ]; }, [parentBuildId, globalSettings]); diff --git a/src/frontend/src/tables/part/PartParameterTable.tsx b/src/frontend/src/tables/part/PartParameterTable.tsx index ad58c66d9b..d7119e4324 100644 --- a/src/frontend/src/tables/part/PartParameterTable.tsx +++ b/src/frontend/src/tables/part/PartParameterTable.tsx @@ -16,7 +16,6 @@ import { apiUrl } from '@lib/functions/Api'; import type { TableFilter } from '@lib/types/Filters'; import type { ApiFormFieldSet } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; -import { RenderUser } from '../../components/render/User'; import { formatDecimal } from '../../defaults/formatters'; import { usePartParameterFields } from '../../forms/PartForms'; import { @@ -30,7 +29,8 @@ import { DateColumn, DescriptionColumn, NoteColumn, - PartColumn + PartColumn, + UserColumn } from '../ColumnRenderers'; import { IncludeVariantsFilter, UserFilter } from '../Filter'; import { InvenTreeTable } from '../InvenTreeTable'; @@ -122,19 +122,11 @@ export function PartParameterTable({ sortable: true, switchable: true }), - { - accessor: 'updated_by', - title: t`Updated By`, - sortable: true, - switchable: true, - render: (record: any) => { - return record.updated_by_detail ? ( - - ) : ( - '-' - ); - } - } + UserColumn({ + accessor: 'updated_by_detail', + ordering: 'updated_by', + title: t`Updated By` + }) ]; }, [partId]); diff --git a/src/frontend/src/tables/settings/ApiTokenTable.tsx b/src/frontend/src/tables/settings/ApiTokenTable.tsx index b3a00467cd..ccb953a4ce 100644 --- a/src/frontend/src/tables/settings/ApiTokenTable.tsx +++ b/src/frontend/src/tables/settings/ApiTokenTable.tsx @@ -12,11 +12,10 @@ import { useCallback, useMemo, useState } from 'react'; import { api } from '../../App'; import { CopyButton } from '../../components/buttons/CopyButton'; import { StylishText } from '../../components/items/StylishText'; -import { RenderUser } from '../../components/render/User'; import { showApiErrorMessage } from '../../functions/notifications'; import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useTable } from '../../hooks/UseTable'; -import { BooleanColumn } from '../ColumnRenderers'; +import { BooleanColumn, UserColumn } from '../ColumnRenderers'; import { UserFilter } from '../Filter'; import { InvenTreeTable } from '../InvenTreeTable'; @@ -100,18 +99,13 @@ export function ApiTokenTable({ } ]; if (!only_myself) { - cols.push({ - accessor: 'user', - title: t`User`, - sortable: true, - render: (record: any) => { - if (record.user_detail) { - return ; - } else { - return record.user; - } - } - }); + cols.push( + UserColumn({ + accessor: 'user_detail', + ordering: 'user', + title: t`User` + }) + ); } return cols; }, [only_myself]); diff --git a/src/frontend/src/tables/stock/StockItemTestResultTable.tsx b/src/frontend/src/tables/stock/StockItemTestResultTable.tsx index 5b8962b5fe..fea1ddedcd 100644 --- a/src/frontend/src/tables/stock/StockItemTestResultTable.tsx +++ b/src/frontend/src/tables/stock/StockItemTestResultTable.tsx @@ -25,7 +25,6 @@ import type { TableFilter } from '@lib/types/Filters'; import type { ApiFormFieldSet } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; import { AttachmentLink } from '../../components/items/AttachmentLink'; -import { RenderUser } from '../../components/render/User'; import { useApi } from '../../contexts/ApiContext'; import { formatDate } from '../../defaults/formatters'; import { useTestResultFields } from '../../forms/StockForms'; @@ -37,7 +36,12 @@ import { import { useTable } from '../../hooks/UseTable'; import { useGlobalSettingsState } from '../../states/SettingsStates'; import { useUserState } from '../../states/UserState'; -import { DateColumn, DescriptionColumn, NoteColumn } from '../ColumnRenderers'; +import { + DateColumn, + DescriptionColumn, + NoteColumn, + UserColumn +} from '../ColumnRenderers'; import { InvenTreeTable } from '../InvenTreeTable'; import RowExpansionIcon from '../RowExpansionIcon'; @@ -211,13 +215,10 @@ export default function StockItemTestResultTable({ }, NoteColumn({}), DateColumn({}), - { - accessor: 'user', - title: t`User`, - sortable: false, - render: (record: any) => - record.user_detail && - }, + UserColumn({ + accessor: 'user_detail', + ordering: 'user' + }), { accessor: 'test_station', sortable: true,