diff --git a/packages/frontend/src/pages/Inbox/Inbox.tsx b/packages/frontend/src/pages/Inbox/Inbox.tsx
index c14698ad..f7d29fbd 100644
--- a/packages/frontend/src/pages/Inbox/Inbox.tsx
+++ b/packages/frontend/src/pages/Inbox/Inbox.tsx
@@ -112,7 +112,11 @@ export const Inbox = ({ viewType }: InboxProps) => {
{dataSourceSuccess && !filteredItems.length && {t(`inbox.heading.title.${viewType}`, { count: 0 })}
}
- b[0].localeCompare(a[0])} />
+ (groups[bKey]?.orderIndex ?? 0) - (groups[aKey]?.orderIndex ?? 0)}
+ />
>
);
diff --git a/packages/frontend/src/pages/Inbox/useGroupedDialogs.spec.tsx b/packages/frontend/src/pages/Inbox/useGroupedDialogs.spec.tsx
new file mode 100644
index 00000000..f6ba8826
--- /dev/null
+++ b/packages/frontend/src/pages/Inbox/useGroupedDialogs.spec.tsx
@@ -0,0 +1,250 @@
+import { renderHook } from '@testing-library/react';
+import type { DialogStatus, SystemLabel } from 'bff-types-generated';
+import { useTranslation } from 'react-i18next';
+import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest';
+import type { InboxViewType } from '../../api/useDialogs.tsx';
+import { useFormat } from '../../i18n/useDateFnsLocale.tsx';
+import useGroupedDialogs from './useGroupedDialogs.tsx';
+
+vi.mock('react-i18next', () => ({
+ useTranslation: vi.fn(),
+}));
+
+vi.mock('../../i18n/useDateFnsLocale.tsx', () => ({
+ useFormat: vi.fn(),
+}));
+
+const mockData = [
+ {
+ id: '01953842-438a-7232-b9c0-d90bd89e1072',
+ party: 'urn:altinn:person:identifier-no:14886498226',
+ title: 'Dialog laget med Scenario Builder 2025-2-24 14:52',
+ summary: 'Denne teksten representerer et sammendrag av dialogen, som er opprettet av serviceOwner i dialogporten.',
+ sender: {
+ name: 'Digitaliseringsdirektoratet',
+ isCompany: true,
+ imageURL: '',
+ },
+ receiver: {
+ name: 'Hjelpelinje Ordinær',
+ isCompany: false,
+ },
+ guiAttachmentCount: 0,
+ createdAt: '2025-02-24T14:00:21.642Z',
+ updatedAt: '2025-02-24T14:02:58.540Z',
+ status: 'NEW' as DialogStatus,
+ isSeenByEndUser: true,
+ label: 'DEFAULT' as SystemLabel,
+ org: 'digdir',
+ seenByLabel: 'Sett av deg',
+ seenByOthersCount: 0,
+ viewType: 'inbox' as InboxViewType,
+ },
+ {
+ id: '0195383e-0d9a-73b1-b96b-73c49e31097d',
+ party: 'urn:altinn:person:identifier-no:14886498226',
+ title: 'Dialog laget med Scenario Builder 2025-2-24 14:52',
+ summary: 'Denne teksten representerer et sammendrag av dialogen, som er opprettet av serviceOwner i dialogporten.',
+ sender: {
+ name: 'Digitaliseringsdirektoratet',
+ isCompany: true,
+ imageURL: '',
+ },
+ receiver: {
+ name: 'Hjelpelinje Ordinær',
+ isCompany: false,
+ },
+ guiAttachmentCount: 1,
+ createdAt: '2025-02-24T13:55:45.689Z',
+ updatedAt: '2025-02-24T13:55:45.689Z',
+ status: 'NEW' as DialogStatus,
+ isSeenByEndUser: true,
+ label: 'DEFAULT' as SystemLabel,
+ org: 'digdir',
+ seenByLabel: 'Sett av deg',
+ seenByOthersCount: 0,
+ viewType: 'inbox' as InboxViewType,
+ },
+ {
+ id: '0195383b-6068-726a-b632-1ff3cc836e9a',
+ party: 'urn:altinn:person:identifier-no:14886498226',
+ title: 'Dialog laget med Scenario Builder 2025-2-24 14:52',
+ summary: 'Denne teksten representerer et sammendrag av dialogen, som er opprettet av serviceOwner i dialogporten.',
+ sender: {
+ name: 'Digitaliseringsdirektoratet',
+ isCompany: true,
+ imageURL: '',
+ },
+ receiver: {
+ name: 'Hjelpelinje Ordinær',
+ isCompany: false,
+ },
+ guiAttachmentCount: 1,
+ createdAt: '2025-02-24T13:52:50.280Z',
+ updatedAt: '2025-02-24T13:52:50.280Z',
+ status: 'NEW' as DialogStatus,
+ isSeenByEndUser: true,
+ label: 'DEFAULT' as SystemLabel,
+ org: 'digdir',
+ seenByLabel: 'Sett av deg',
+ seenByOthersCount: 0,
+ viewType: 'inbox' as InboxViewType,
+ },
+ {
+ id: '01937766-ff24-7305-9361-d14743d2db30',
+ party: 'urn:altinn:person:identifier-no:14886498226',
+ title: 'Test melding8',
+ summary: 'Et sammendrag her. Maks 200 tegn, ingen HTML-støtte. Påkrevd. Vises i liste.',
+ sender: {
+ name: 'digitaliseringsdirektoratet',
+ isCompany: true,
+ imageURL: '',
+ },
+ receiver: {
+ name: 'Hjelpelinje Ordinær',
+ isCompany: false,
+ },
+ guiAttachmentCount: 1,
+ createdAt: '2024-11-29T10:10:58.980Z',
+ updatedAt: '2024-11-29T10:10:58.980Z',
+ status: 'NEW' as DialogStatus,
+ isSeenByEndUser: true,
+ label: 'DEFAULT' as SystemLabel,
+ org: 'digitaliseringsdirektoratet',
+ seenByLabel: 'Sett av deg',
+ seenByOthersCount: 0,
+ viewType: 'inbox' as InboxViewType,
+ },
+ {
+ id: '01936e30-ac82-70f3-96ba-eb3ccd31586a',
+ party: 'urn:altinn:person:identifier-no:14886498226',
+ title: 'Test melding3',
+ summary: 'Et sammendrag her. Maks 200 tegn, ingen HTML-støtte. Påkrevd. Vises i liste.',
+ sender: {
+ name: 'digitaliseringsdirektoratet',
+ isCompany: true,
+ imageURL: '',
+ },
+ receiver: {
+ name: 'Hjelpelinje Ordinær',
+ isCompany: false,
+ },
+ guiAttachmentCount: 1,
+ createdAt: '2024-11-27T15:15:03.934Z',
+ updatedAt: '2024-11-27T15:15:03.934Z',
+ status: 'NEW' as DialogStatus,
+ isSeenByEndUser: true,
+ label: 'DEFAULT' as SystemLabel,
+ org: 'digitaliseringsdirektoratet',
+ seenByLabel: 'Sett av deg',
+ seenByOthersCount: 0,
+ viewType: 'inbox' as InboxViewType,
+ },
+ {
+ id: 'f67d9101-5149-6377-95db-6146e6b2e196',
+ party: 'urn:altinn:person:identifier-no:14886498226',
+ title: 'Tjeneste for rapportering av et eller annet fra SKE',
+ summary: 'Et sammendrag her. Maks 200 tegn, ingen riktekst-støtte. Påkrevd. Vises i liste.',
+ sender: {
+ name: 'Skatteetaten',
+ isCompany: true,
+ imageURL: 'https://altinncdn.no/orgs/skd/skd.png',
+ },
+ receiver: {
+ name: 'Hjelpelinje Ordinær',
+ isCompany: false,
+ },
+ guiAttachmentCount: 2,
+ createdAt: '2024-08-23T06:39:38.321Z',
+ updatedAt: '2024-08-23T06:39:38.321Z',
+ status: 'NEW' as DialogStatus,
+ isSeenByEndUser: true,
+ label: 'DEFAULT' as SystemLabel,
+ org: 'skd',
+ seenByLabel: 'Sett av deg',
+ seenByOthersCount: 0,
+ viewType: 'inbox' as InboxViewType,
+ },
+ {
+ id: 'f57d9101-e17a-8b76-94ba-c6d63f56b96f',
+ party: 'urn:altinn:person:identifier-no:14886498226',
+ title: 'Tjeneste for rapportering av et eller annet fra SKE',
+ summary: 'Et sammendrag her. Maks 200 tegn, ingen riktekst-støtte. Påkrevd. Vises i liste.',
+ sender: {
+ name: 'Skatteetaten',
+ isCompany: true,
+ imageURL: 'https://altinncdn.no/orgs/skd/skd.png',
+ },
+ receiver: {
+ name: 'Hjelpelinje Ordinær',
+ isCompany: false,
+ },
+ guiAttachmentCount: 2,
+ createdAt: '2024-08-23T06:38:45.473Z',
+ updatedAt: '2024-08-23T06:38:45.473Z',
+ status: 'NEW' as DialogStatus,
+ isSeenByEndUser: true,
+ label: 'DEFAULT' as SystemLabel,
+ org: 'skd',
+ seenByLabel: 'Sett av deg',
+ seenByOthersCount: 0,
+ viewType: 'inbox' as InboxViewType,
+ },
+];
+
+describe('useGroupedDialogs', () => {
+ const t = vi.fn((key) => key);
+ const format = vi.fn((date) => date.toString());
+
+ beforeEach(() => {
+ (useTranslation as Mock).mockReturnValue({ t });
+ (useFormat as Mock).mockReturnValue(format);
+ });
+
+ it('should return loading items when isLoading is true', () => {
+ const { result } = renderHook(() =>
+ useGroupedDialogs({
+ items: [],
+ displaySearchResults: false,
+ viewType: 'inbox',
+ isLoading: true,
+ }),
+ );
+
+ expect(result.current.mappedGroupedDialogs).toHaveLength(5);
+ expect(result.current.groups).toEqual({ loading: { title: 'word.loading' } });
+ });
+
+ it('should return grouped dialogs when isLoading is false', () => {
+ const { result } = renderHook(() =>
+ useGroupedDialogs({
+ items: mockData,
+ displaySearchResults: true,
+ viewType: 'inbox',
+ isLoading: false,
+ }),
+ );
+
+ expect(result.current.mappedGroupedDialogs).toHaveLength(7);
+ expect(result.current.groups).toEqual({
+ inbox: { title: 'inbox.heading.search_results.inbox', orderIndex: null },
+ });
+ });
+
+ it('should generat groups orderIndex correctly', () => {
+ const { result } = renderHook(() =>
+ useGroupedDialogs({
+ items: mockData,
+ displaySearchResults: false,
+ viewType: 'inbox',
+ isLoading: false,
+ }),
+ );
+
+ const groups = result.current.groups;
+ const uniqueOrderIndexes = [...new Set(Object.values(groups).map((item) => item.orderIndex?.toString()))];
+
+ expect(uniqueOrderIndexes).toHaveLength(2);
+ expect(uniqueOrderIndexes.toString()).toBe('2025,2024');
+ });
+});
diff --git a/packages/frontend/src/pages/Inbox/useGroupedDialogs.tsx b/packages/frontend/src/pages/Inbox/useGroupedDialogs.tsx
index 3feaa496..8dfef06b 100644
--- a/packages/frontend/src/pages/Inbox/useGroupedDialogs.tsx
+++ b/packages/frontend/src/pages/Inbox/useGroupedDialogs.tsx
@@ -16,16 +16,21 @@ interface GroupedItem {
id: string | number;
label: string;
items: InboxItemInput[];
+ orderIndex: number | null;
+}
+
+interface DialogListGroupPropsSort extends DialogListGroupProps {
+ orderIndex?: number | null;
}
interface UseGroupedDialogsOutput {
mappedGroupedDialogs: DialogListItemProps[];
- groups?: Record;
+ groups: Record;
}
interface UseGroupedDialogsProps {
items: InboxItemInput[];
- filters: FilterState;
+ filters?: FilterState;
displaySearchResults: boolean;
viewType: InboxViewType;
isLoading: boolean;
@@ -132,15 +137,25 @@ const useGroupedDialogs = ({
};
}
+ const getOrderIndex = (createdAt: Date, isDateKey: boolean) => {
+ if (!isDateKey) return null;
+
+ if (allWithinSameYear) {
+ return createdAt.getMonth();
+ }
+ return createdAt.getFullYear();
+ };
+
const groupedItems = items.reduce((acc, item, _, list) => {
const createdAt = new Date(item.createdAt);
-
const groupKey = displaySearchResults
? item.viewType
: allWithinSameYear
? format(createdAt, 'LLLL')
: format(createdAt, 'yyyy');
+ const isDateKey = !displaySearchResults;
+
const label = displaySearchResults
? t(`inbox.heading.search_results.${groupKey}`, {
count: list.filter((i) => i.viewType === groupKey).length,
@@ -148,16 +163,21 @@ const useGroupedDialogs = ({
: groupKey;
const existingGroup = acc.find((group) => group.id === groupKey);
+
if (existingGroup) {
existingGroup.items.push(item);
} else {
- acc.push({ id: groupKey, label, items: [item] });
+ const orderIndex = getOrderIndex(createdAt, isDateKey);
+
+ acc.push({ id: groupKey, label, items: [item], orderIndex });
}
return acc;
}, []);
- const groups = Object.fromEntries(groupedItems.map(({ id, label }) => [id, { title: label }]));
+ const groups = Object.fromEntries(
+ groupedItems.map(({ id, label, orderIndex }) => [id, { title: label, orderIndex }]),
+ );
const mappedGroupedDialogs = groupedItems.flatMap(({ id, items }) =>
items.map((item) => formatDialogItem(item, id.toString())),