Skip to content

Commit 42e4d60

Browse files
feat(inbox): filter cases by assignee MAR-1244 (#1136)
1 parent 4984aaa commit 42e4d60

8 files changed

Lines changed: 48 additions & 2 deletions

File tree

packages/app-builder/src/components/Cases/Filters/CasesFiltersContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const casesFiltersSchema = z.object({
3131
.enum(['true', 'false'])
3232
.transform((val) => val === 'true')
3333
.optional(),
34+
assignee: z.uuid().optional(),
3435
});
3536

3637
export type CasesFilters = z.infer<typeof casesFiltersSchema>;
@@ -49,6 +50,7 @@ export type CasesFiltersForm = {
4950
name?: string;
5051
includeSnoozed?: boolean;
5152
excludeAssigned?: boolean;
53+
assignee?: string;
5254
};
5355

5456
const emptyCasesFilters: CasesFiltersForm = {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useOrganizationUsers } from '@app-builder/services/organization/organization-users';
2+
import { useFormContext } from 'react-hook-form';
3+
import * as R from 'remeda';
4+
import { Avatar } from 'ui-design-system';
5+
import { CasesFiltersForm, useCasesFiltersContext } from '../CasesFiltersContext';
6+
7+
export function CasesAssigneeFilter() {
8+
const { submitCasesFilters } = useCasesFiltersContext();
9+
const { setValue } = useFormContext<CasesFiltersForm>();
10+
const { orgUsers } = useOrganizationUsers();
11+
12+
const handleUserSelect = (userId: string) => {
13+
setValue('assignee', userId);
14+
submitCasesFilters();
15+
};
16+
17+
return (
18+
<div className="flex flex-col gap-2 p-v2-sm">
19+
{orgUsers.map((user) => (
20+
<div
21+
key={user.userId}
22+
className="inline-flex gap-v2-sm w-full p-v2-sm hover:bg-purple-98 cursor-pointer"
23+
onClick={() => handleUserSelect(user.userId)}
24+
>
25+
<Avatar size="xs" firstName={user.firstName} lastName={user.lastName} />
26+
<span>{`${R.capitalize(user.firstName)} ${R.capitalize(user.lastName)}`}</span>
27+
</div>
28+
))}
29+
</div>
30+
);
31+
}

packages/app-builder/src/components/Cases/Filters/FilterDetail/FilterDetail.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { match } from 'ts-pattern';
22

33
import { type CasesFilterName } from '../filters';
4+
import { CasesAssigneeFilter } from './CasesAssigneeFilter';
45
import { CasesDateRangeFilter } from './CasesDateRangeFilter';
56
import { CasesExcludeAssignedFilter } from './CasesExcludeAssignedFilter';
67
import { CasesSnoozedFilter } from './CasesSnoozedFilter';
@@ -12,4 +13,5 @@ export const FilterDetail = ({ filterName }: { filterName: CasesFilterName }) =>
1213
.with('statuses', () => <ClosedCasesFilter />)
1314
.with('includeSnoozed', () => <CasesSnoozedFilter />)
1415
.with('excludeAssigned', () => <CasesExcludeAssignedFilter />)
16+
.with('assignee', () => <CasesAssigneeFilter />)
1517
.exhaustive();

packages/app-builder/src/components/Cases/Filters/filters.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const casesFilterNames = [
77
'statuses',
88
'includeSnoozed',
99
'excludeAssigned',
10+
'assignee',
1011
] as const;
1112

1213
export type CasesFilterName = (typeof casesFilterNames)[number];
@@ -21,6 +22,7 @@ export const getFilterIcon = (filterName: CasesFilterName) =>
2122
.with('statuses', () => 'checked')
2223
.with('includeSnoozed', () => 'snooze')
2324
.with('excludeAssigned', () => 'person')
25+
.with('assignee', () => 'person')
2426
.exhaustive();
2527

2628
export const getFilterTKey = (filterName: CasesFilterName) =>
@@ -29,4 +31,5 @@ export const getFilterTKey = (filterName: CasesFilterName) =>
2931
.with('statuses', () => 'cases:filter.closed_only.label')
3032
.with('includeSnoozed', () => 'cases:filter.include_snoozed.label')
3133
.with('excludeAssigned', () => 'cases:filter.exclude_assigned.label')
34+
.with('assignee', () => 'cases:filter.assignee.label')
3235
.exhaustive();

packages/app-builder/src/locales/ar/cases.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@
182182
"escalate-button.label": "تصاعد",
183183
"escalate-case.modal.callout": "عند تصعيد هذه الحالة ، لن يتم تخصيصها لها ، وسوف تفقد الوصول إذا لم تكن عضوًا في صندوق الوارد المستهدف. <br/> سيتم إعادة توجيهك إلى قائمة الحالات في صندوق الوارد الحالي.",
184184
"escalate-case.modal.submit-button.label": "تصاعد",
185+
"filter.assignee.label": "مكلّف",
185186
"filter.closed_only.label": "المغلقة فقط",
186187
"filter.exclude_assigned.label": "غير المكلّفين فقط",
187188
"filter.include_snoozed.label": "تضمين المؤجّلين",

packages/app-builder/src/locales/en/cases.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@
182182
"escalate-button.label": "Escalate",
183183
"escalate-case.modal.callout": "Upon escalating this case, you will no longer be assigned to it, and you will lose access if you are not a member of the target inbox.<br/>You will be redirected to the list of cases in the current inbox.",
184184
"escalate-case.modal.submit-button.label": "Escalate",
185+
"filter.assignee.label": "Assignee",
185186
"filter.closed_only.label": "Closed only",
186187
"filter.exclude_assigned.label": "Unassigned only",
187188
"filter.include_snoozed.label": "Include snoozed",

packages/app-builder/src/locales/fr/cases.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@
182182
"escalate-button.label": "Escalader",
183183
"escalate-case.modal.callout": "En procédant à l’escalade de ce cas, il ne vous sera plus affecté et vous n’y aurez plus accès si vous n'êtes pas membre de la boîte de réception cible.<br/>Vous serez redirigé vers la boîte de réception actuelle.",
184184
"escalate-case.modal.submit-button.label": "Escalader",
185+
"filter.assignee.label": "Assigné",
185186
"filter.closed_only.label": "Investigations résolues",
186187
"filter.exclude_assigned.label": "Non assignés",
187188
"filter.include_snoozed.label": "Voir les rappels",

packages/app-builder/src/routes/_builder+/cases+/inboxes.$inboxId.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export const buildQueryParams = (
6060
fromNow: filters.dateRange.fromNow,
6161
}
6262
: {},
63+
assignee: filters.assignee,
6364
offsetId,
6465
...(limit && { limit }),
6566
...(order && { order }),
@@ -105,7 +106,9 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
105106
statuses:
106107
(parsedQuery.data.statuses as CaseStatus[]) ??
107108
caseStatuses.filter((status) => status !== 'closed'),
108-
...(!inboxId && { assigneeId: user.actorIdentity.userId }),
109+
...(!inboxId
110+
? { assigneeId: user.actorIdentity.userId }
111+
: { assigneeId: parsedQuery.data.assignee }),
109112
};
110113

111114
try {
@@ -253,7 +256,9 @@ export default function Cases() {
253256
</CaseRightPanel.Trigger>
254257
</div>
255258
</div>
256-
<CasesFiltersBar excludedFilters={!inboxId ? ['excludeAssigned'] : undefined} />
259+
<CasesFiltersBar
260+
excludedFilters={!inboxId ? ['excludeAssigned', 'assignee'] : undefined}
261+
/>
257262
<CasesList
258263
key={inboxId}
259264
cases={cases}

0 commit comments

Comments
 (0)