From 1940ebaa366572be8f36991908bbed090b86e765 Mon Sep 17 00:00:00 2001 From: Christopher Debove Date: Thu, 13 Nov 2025 09:17:41 +0100 Subject: [PATCH 1/5] fix(inbox): make review status unbreakable --- packages/app-builder/src/components/Cases/Inbox/CasesList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx b/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx index 6dd05e7e30..21d05c95f0 100644 --- a/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx +++ b/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx @@ -112,7 +112,7 @@ export function CasesList({
{caseItem.outcome && caseItem.outcome !== 'unset' ? ( Date: Thu, 13 Nov 2025 09:23:13 +0100 Subject: [PATCH 2/5] fix(inbox): make selection checkbox have larger hitbox --- .../src/components/Cases/Inbox/CasesList.tsx | 32 +++++++++++-------- .../src/Checkbox/Checkbox.tsx | 2 +- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx b/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx index 21d05c95f0..ef159b3d48 100644 --- a/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx +++ b/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx @@ -57,13 +57,7 @@ export function CasesList({
- {(state, onSelect) => ( - - )} + {(state, onSelect) => } {t('cases:inbox.heading.status')} @@ -96,13 +90,7 @@ export function CasesList({
- {(isSelected, onSelect) => ( - - )} + {(isSelected, onSelect) => }
@@ -163,3 +151,19 @@ const TagPreview = ({ name }: { name: string }) => ( {name}
); + +type SelectionCheckboxProps = { + selectionState: boolean | 'indeterminate'; + onSelect: MouseEventHandler; +}; + +const SelectionCheckbox = ({ selectionState, onSelect }: SelectionCheckboxProps) => { + return ( +
+ +
+ ); +}; diff --git a/packages/ui-design-system/src/Checkbox/Checkbox.tsx b/packages/ui-design-system/src/Checkbox/Checkbox.tsx index 69da82f0db..aed863a0a5 100644 --- a/packages/ui-design-system/src/Checkbox/Checkbox.tsx +++ b/packages/ui-design-system/src/Checkbox/Checkbox.tsx @@ -8,7 +8,7 @@ export type { CheckedState } from '@radix-ui/react-checkbox'; const checkbox = cva( [ 'flex shrink-0 items-center justify-center rounded-sm border outline-hidden', - 'bg-grey-100 hover:bg-purple-98 enabled:radix-state-checked:border-none enabled:radix-state-checked:bg-purple-65', + 'bg-grey-100 hover:bg-purple-98 group-hover/checkbox-parent:bg-purple-98 enabled:radix-state-checked:border-none enabled:radix-state-checked:bg-purple-65', 'disabled:bg-grey-90 disabled:border-grey-80 disabled:radix-state-checked:border disabled:radix-state-checked:bg-grey-90 disabled:cursor-not-allowed', ], { From 4203492a45074342e62cef4d03b1e87291bee1b3 Mon Sep 17 00:00:00 2001 From: Christopher Debove Date: Thu, 13 Nov 2025 09:36:31 +0100 Subject: [PATCH 3/5] fix(inbox): Add date + time tooltip on row dates --- .../src/components/Cases/Inbox/CasesList.tsx | 16 +++++++++++++--- .../routes/ressources+/cases+/$inboxId.cases.tsx | 3 ++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx b/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx index ef159b3d48..07dfd1e9ea 100644 --- a/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx +++ b/packages/app-builder/src/components/Cases/Inbox/CasesList.tsx @@ -1,12 +1,12 @@ import { MultiSelect } from '@app-builder/components/MultiSelect'; import { useOrganizationTags } from '@app-builder/services/organization/organization-tags'; -import { formatDateRelative } from '@app-builder/utils/format'; +import { formatDateRelative, formatDateTimeWithoutPresets } from '@app-builder/utils/format'; import { getRoute } from '@app-builder/utils/routes'; import { fromUUIDtoSUUID } from '@app-builder/utils/short-uuid'; import { Link } from '@remix-run/react'; import { MouseEventHandler, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Checkbox, cn } from 'ui-design-system'; +import { Checkbox, cn, Tooltip } from 'ui-design-system'; import { Icon } from 'ui-icons'; import { CaseStatusBadge } from '../CaseStatus'; import { AssignedContributors } from './AssignedContributors'; @@ -112,7 +112,17 @@ export function CasesList({ '-' )} -
{formatDateRelative(caseItem.createdAt, { language })}
+
+ + + +
{caseItem.tags.map((tagItem) => { const tag = orgTags.find((tag) => tag.id === tagItem.tagId); diff --git a/packages/app-builder/src/routes/ressources+/cases+/$inboxId.cases.tsx b/packages/app-builder/src/routes/ressources+/cases+/$inboxId.cases.tsx index 41528fa335..00d2431102 100644 --- a/packages/app-builder/src/routes/ressources+/cases+/$inboxId.cases.tsx +++ b/packages/app-builder/src/routes/ressources+/cases+/$inboxId.cases.tsx @@ -22,12 +22,13 @@ export const loader = createServerFn( throw badRequest('Invalid query'); } const filterInboxIds = inboxId === MY_INBOX_ID ? undefined : [inboxId]; + const assigneeIdFilter = parsedQuery.data.assignee ? { assigneeId: parsedQuery.data.assignee } : {}; const cases = await caseRepository.listCases({ ...parsedQuery.data, ...parsedPagination.data, inboxIds: filterInboxIds, - ...(filterInboxIds === undefined ? { assigneeId: user.actorIdentity.userId } : {}), + ...(filterInboxIds === undefined ? { assigneeId: user.actorIdentity.userId } : assigneeIdFilter), }); return data({ data: cases }); From 72fbb4c08886ee4e992f48afbca82a2bd0a45db0 Mon Sep 17 00:00:00 2001 From: Christopher Debove Date: Thu, 13 Nov 2025 10:38:28 +0100 Subject: [PATCH 4/5] fix(inbox): default status filter to not closed cases --- .../ressources+/cases+/$caseId+/decisions.tsx | 2 +- .../routes/ressources+/cases+/$inboxId.cases.tsx | 3 +++ .../app-builder/src/utils/input-validation.ts | 15 ++++++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/app-builder/src/routes/ressources+/cases+/$caseId+/decisions.tsx b/packages/app-builder/src/routes/ressources+/cases+/$caseId+/decisions.tsx index 9e8572338c..30d94fdf29 100644 --- a/packages/app-builder/src/routes/ressources+/cases+/$caseId+/decisions.tsx +++ b/packages/app-builder/src/routes/ressources+/cases+/$caseId+/decisions.tsx @@ -7,7 +7,7 @@ import { z } from 'zod/v4'; const paginationSchema = z.object({ limit: z.coerce.number().optional(), - cursorId: z.union([z.string(), z.coerce.number()]).optional(), + cursorId: z.string().optional(), }); export async function loader({ request, params }: LoaderFunctionArgs) { diff --git a/packages/app-builder/src/routes/ressources+/cases+/$inboxId.cases.tsx b/packages/app-builder/src/routes/ressources+/cases+/$inboxId.cases.tsx index 00d2431102..f13a9f2863 100644 --- a/packages/app-builder/src/routes/ressources+/cases+/$inboxId.cases.tsx +++ b/packages/app-builder/src/routes/ressources+/cases+/$inboxId.cases.tsx @@ -3,6 +3,7 @@ import { MY_INBOX_ID } from '@app-builder/constants/inboxes'; import { createServerFn, data } from '@app-builder/core/requests'; import { authMiddleware } from '@app-builder/middlewares/auth-middleware'; import { handleRedirectMiddleware } from '@app-builder/middlewares/handle-redirect-middleware'; +import { caseStatuses } from '@app-builder/models/cases'; import { filtersSchema } from '@app-builder/queries/cases/get-cases'; import { badRequest } from '@app-builder/utils/http/http-responses'; import { parseQuerySafe } from '@app-builder/utils/input-validation'; @@ -23,10 +24,12 @@ export const loader = createServerFn( } const filterInboxIds = inboxId === MY_INBOX_ID ? undefined : [inboxId]; const assigneeIdFilter = parsedQuery.data.assignee ? { assigneeId: parsedQuery.data.assignee } : {}; + const statusesFilter = parsedQuery.data.statuses ?? caseStatuses.filter((status) => status !== 'closed'); const cases = await caseRepository.listCases({ ...parsedQuery.data, ...parsedPagination.data, + statuses: statusesFilter, inboxIds: filterInboxIds, ...(filterInboxIds === undefined ? { assigneeId: user.actorIdentity.userId } : assigneeIdFilter), }); diff --git a/packages/app-builder/src/utils/input-validation.ts b/packages/app-builder/src/utils/input-validation.ts index 9705e52187..3e55e0a4b3 100644 --- a/packages/app-builder/src/utils/input-validation.ts +++ b/packages/app-builder/src/utils/input-validation.ts @@ -95,14 +95,19 @@ export async function parseParams(params: Params, schema: ZodType(request: Request, schema: ZodType) { +type ParseQuerySafeResult = + | z.ZodSafeParseSuccess + | (z.ZodSafeParseError & { searchParams: Record }); + +export async function parseQuerySafe( + request: Request, + schema: T, +): Promise>> { const searchParams = inputFromUrl(request); const result = await schema.safeParseAsync(searchParams); + if (!result.success) { - return { - ...result, - searchParams, - }; + return { ...result, searchParams }; } return result; } From b7f4823541e6c63a5c29fd77dfefb4648f7e45eb Mon Sep 17 00:00:00 2001 From: Christopher Debove Date: Thu, 13 Nov 2025 10:38:28 +0100 Subject: [PATCH 5/5] fix(inbox): change breadcrumb to be selected inbox name --- .../_builder+/cases+/inboxes.$inboxId.tsx | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/app-builder/src/routes/_builder+/cases+/inboxes.$inboxId.tsx b/packages/app-builder/src/routes/_builder+/cases+/inboxes.$inboxId.tsx index fc951d3018..294a44fce7 100644 --- a/packages/app-builder/src/routes/_builder+/cases+/inboxes.$inboxId.tsx +++ b/packages/app-builder/src/routes/_builder+/cases+/inboxes.$inboxId.tsx @@ -8,6 +8,7 @@ import { DEFAULT_CASE_PAGINATION_SIZE } from '@app-builder/repositories/CaseRepo import { getRoute } from '@app-builder/utils/routes'; import { fromSUUIDtoUUID, fromUUIDtoSUUID } from '@app-builder/utils/short-uuid'; import { useLoaderData } from '@remix-run/react'; +import { SerializeFrom } from '@remix-run/server-runtime/dist/single-fetch'; import { Namespace } from 'i18next'; import QueryString from 'qs'; import { useTranslation } from 'react-i18next'; @@ -27,11 +28,14 @@ export const handle = { ); }, - ({ isLast }: BreadCrumbProps) => { - const { t } = useTranslation(['navigation']); + ({ isLast, data }: BreadCrumbProps>) => { + const { t } = useTranslation(['navigation', 'cases']); + const currentInboxName = data.currentInbox?.name ?? t('cases:inbox.my-inbox.link'); + const currentInboxId = data.currentInbox ? fromUUIDtoSUUID(data.currentInbox.id) : MY_INBOX_ID; + return ( - - {t('navigation:case_manager.cases')} + + {currentInboxName} ); }, @@ -47,10 +51,11 @@ const pageQueryStringSchema = z.object({ export const loader = createServerFn([authMiddleware], async function casesInboxesLoader({ request, params, context }) { const { inbox: inboxRepository } = context.authInfo; const inboxes = await inboxRepository.listInboxesWithCaseCount(); - const inboxId = params['inboxId']; + const inboxIdParam = params['inboxId']; - invariant(inboxId, 'inboxId is required'); + invariant(inboxIdParam, 'inboxId is required'); + const inboxId = inboxIdParam === MY_INBOX_ID ? inboxIdParam : fromSUUIDtoUUID(inboxIdParam); let inboxUsersIds: string[] = []; let currentInbox = inboxes.find((inbox) => inbox.id === inboxId); if (currentInbox) { @@ -62,7 +67,8 @@ export const loader = createServerFn([authMiddleware], async function casesInbox const parsedSearchParams = pageQueryStringSchema.parse(Object.fromEntries(searchParams)); return { - inboxId: inboxId === MY_INBOX_ID ? inboxId : fromSUUIDtoUUID(inboxId), + inboxId, + currentInbox, inboxes, inboxUsersIds, query: parsedSearchParams.q,