Skip to content

Commit 47b5ca4

Browse files
committed
👌 - fix: pr fixes
1 parent 2898fad commit 47b5ca4

File tree

7 files changed

+210
-65
lines changed

7 files changed

+210
-65
lines changed

frontend/src/components/ProcessingStatusBadge/ProcessingStatusBadge.tsx

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Badge, field2Title } from "@maykin-ui/admin-ui";
1+
import { Badge, Outline, field2Title } from "@maykin-ui/admin-ui";
22
import React from "react";
33

44
import { ProcessingStatus } from "../../lib/api/processingStatus";
@@ -11,24 +11,44 @@ import {
1111

1212
type ProcessingStatusBadgeProps = {
1313
processingStatus: ProcessingStatus;
14-
plannedDestructionDate: string | null;
14+
plannedDestructionDate?: string | null;
1515
};
1616

1717
export const ProcessingStatusBadge: React.FC<ProcessingStatusBadgeProps> = ({
1818
processingStatus,
1919
plannedDestructionDate,
2020
}) => {
21+
const getLevel = () => {
22+
if (processingStatus === "new" && plannedDestructionDate) {
23+
return "warning";
24+
}
25+
return PROCESSING_STATUS_LEVEL_MAPPING[processingStatus];
26+
};
27+
28+
const getStatusIcon = () => {
29+
if (processingStatus === "new" && plannedDestructionDate) {
30+
return <Outline.ClockIcon />;
31+
}
32+
return PROCESSING_STATUS_ICON_MAPPING[processingStatus];
33+
};
34+
2135
const getStatusText = () => {
2236
if (processingStatus === "new" && plannedDestructionDate) {
23-
return `Vernietigd ${timeAgo(plannedDestructionDate ?? "", { shortFormat: true })}`;
37+
const isPlannedDestructionDateInPast =
38+
new Date(plannedDestructionDate) < new Date();
39+
if (isPlannedDestructionDateInPast) {
40+
return `Wordt vernietigd`;
41+
}
42+
return `Wordt vernietigd ${timeAgo(plannedDestructionDate, { shortFormat: true })}`;
2443
}
2544
return field2Title(PROCESSING_STATUS_MAPPING[processingStatus], {
2645
unHyphen: false,
2746
});
2847
};
48+
2949
return (
30-
<Badge level={PROCESSING_STATUS_LEVEL_MAPPING[processingStatus]}>
31-
{PROCESSING_STATUS_ICON_MAPPING[processingStatus]}
50+
<Badge level={getLevel()}>
51+
{getStatusIcon()}
3252
{getStatusText()}
3353
</Badge>
3454
);

frontend/src/fixtures/destructionListItem.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ export const FIXTURE_DESTRUCTION_LIST_ITEM_DELETED: DestructionListItem = {
1919
extraZaakData: null,
2020
zaak: null,
2121
processingStatus: "succeeded",
22-
plannedDestructionDate: null,
22+
plannedDestructionDate: "2026-01-01T00:00:00Z",
2323
};
2424
export const FIXTURE_DESTRUCTION_LIST_ITEM_FAILED: DestructionListItem = {
2525
pk: 3,
2626
status: "suggested",
2727
extraZaakData: null,
2828
zaak: zaakFactory(),
2929
processingStatus: "failed",
30-
plannedDestructionDate: null,
30+
plannedDestructionDate: "2026-01-01T00:00:00Z",
3131
};
3232

3333
export const destructionListItemFactory = createObjectFactory(

frontend/src/lib/format/date.test.ts

+62-5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe("timeAgo()", () => {
1919
beforeEach(mockDate);
2020
afterEach(unMockDate);
2121

22+
// Year tests (past and future)
2223
test("timeAgo() handles year ", () => {
2324
const yearAgo = new Date("2022-09-15:00:00");
2425
expect(timeAgo(yearAgo)).toBe("1 jaar geleden");
@@ -27,8 +28,17 @@ describe("timeAgo()", () => {
2728
const yearsAgo = new Date("2021-09-15:00:00");
2829
expect(timeAgo(yearsAgo)).toBe("2 jaren geleden");
2930
expect(timeAgo(yearsAgo, { shortFormat: true })).toBe("2j");
31+
32+
const yearAhead = new Date("2024-09-15:00:00");
33+
expect(timeAgo(yearAhead)).toBe("over 1 jaar");
34+
expect(timeAgo(yearAhead, { shortFormat: true })).toBe("over 1j");
35+
36+
const yearsAhead = new Date("2025-09-15:00:00");
37+
expect(timeAgo(yearsAhead)).toBe("over 2 jaren");
38+
expect(timeAgo(yearsAhead, { shortFormat: true })).toBe("over 2j");
3039
});
3140

41+
// Month tests (past and future)
3242
test("timeAgo() handles month ", () => {
3343
const monthAgo = new Date("2023-08-15:00:00");
3444
expect(timeAgo(monthAgo)).toBe("1 maand geleden");
@@ -37,8 +47,17 @@ describe("timeAgo()", () => {
3747
const monthsAgo = new Date("2023-07-15:00:00");
3848
expect(timeAgo(monthsAgo)).toBe("2 maanden geleden");
3949
expect(timeAgo(monthsAgo, { shortFormat: true })).toBe("2ma");
50+
51+
const monthAhead = new Date("2023-10-15:00:00");
52+
expect(timeAgo(monthAhead)).toBe("over 1 maand");
53+
expect(timeAgo(monthAhead, { shortFormat: true })).toBe("over 1ma");
54+
55+
const monthsAhead = new Date("2023-11-15:00:00");
56+
expect(timeAgo(monthsAhead)).toBe("over 2 maanden");
57+
expect(timeAgo(monthsAhead, { shortFormat: true })).toBe("over 2ma");
4058
});
4159

60+
// Day tests (past and future)
4261
test("timeAgo() handles day ", () => {
4362
const dayAgo = new Date("2023-09-14:00:00");
4463
expect(timeAgo(dayAgo)).toBe("1 dag geleden");
@@ -47,8 +66,17 @@ describe("timeAgo()", () => {
4766
const daysAgo = new Date("2023-09-13:00:00");
4867
expect(timeAgo(daysAgo)).toBe("2 dagen geleden");
4968
expect(timeAgo(daysAgo, { shortFormat: true })).toBe("2d");
69+
70+
const dayAhead = new Date("2023-09-16:00:00");
71+
expect(timeAgo(dayAhead)).toBe("over 1 dag");
72+
expect(timeAgo(dayAhead, { shortFormat: true })).toBe("over 1d");
73+
74+
const daysAhead = new Date("2023-09-17:00:00");
75+
expect(timeAgo(daysAhead)).toBe("over 2 dagen");
76+
expect(timeAgo(daysAhead, { shortFormat: true })).toBe("over 2d");
5077
});
5178

79+
// Hour tests (past and future)
5280
test("timeAgo() handles hour ", () => {
5381
const hourAgo = new Date("2023-09-14:23:00");
5482
expect(timeAgo(hourAgo)).toBe("1 uur geleden");
@@ -57,8 +85,17 @@ describe("timeAgo()", () => {
5785
const hoursAgo = new Date("2023-09-14:22:00");
5886
expect(timeAgo(hoursAgo)).toBe("2 uur geleden");
5987
expect(timeAgo(hoursAgo, { shortFormat: true })).toBe("2u");
88+
89+
const hourAhead = new Date("2023-09-15:01:00");
90+
expect(timeAgo(hourAhead)).toBe("over 1 uur");
91+
expect(timeAgo(hourAhead, { shortFormat: true })).toBe("over 1u");
92+
93+
const hoursAhead = new Date("2023-09-15:02:00");
94+
expect(timeAgo(hoursAhead)).toBe("over 2 uur");
95+
expect(timeAgo(hoursAhead, { shortFormat: true })).toBe("over 2u");
6096
});
6197

98+
// Minute tests (past and future)
6299
test("timeAgo() handles minute ", () => {
63100
const minuteAgo = new Date("2023-09-14:23:59");
64101
expect(timeAgo(minuteAgo)).toBe("1 minuut geleden");
@@ -67,8 +104,17 @@ describe("timeAgo()", () => {
67104
const minutesAgo = new Date("2023-09-14:23:58");
68105
expect(timeAgo(minutesAgo)).toBe("2 minuten geleden");
69106
expect(timeAgo(minutesAgo, { shortFormat: true })).toBe("2m");
107+
108+
const minuteAhead = new Date("2023-09-15:00:01");
109+
expect(timeAgo(minuteAhead)).toBe("over 1 minuut");
110+
expect(timeAgo(minuteAhead, { shortFormat: true })).toBe("over 1m");
111+
112+
const minutesAhead = new Date("2023-09-15:00:02");
113+
expect(timeAgo(minutesAhead)).toBe("over 2 minuten");
114+
expect(timeAgo(minutesAhead, { shortFormat: true })).toBe("over 2m");
70115
});
71116

117+
// Less than a minute tests (past and future)
72118
test("timeAgo() handles less than a minute ", () => {
73119
const secondAgo = new Date("2023-09-14:23:59:59");
74120
expect(timeAgo(secondAgo)).toBe("Nu");
@@ -77,14 +123,17 @@ describe("timeAgo()", () => {
77123
const secondsAgo = new Date("2023-09-14:23:59:59");
78124
expect(timeAgo(secondsAgo)).toBe("Nu");
79125
expect(timeAgo(secondsAgo, { shortFormat: true })).toBe("0m");
80-
});
81126

82-
test("timeAgo() interprets future data as now ", () => {
83-
const yearFromNow = new Date("2024-09-15:00:00");
84-
expect(timeAgo(yearFromNow)).toBe("Nu");
85-
expect(timeAgo(yearFromNow, { shortFormat: true })).toBe("0m");
127+
const secondAhead = new Date("2023-09-15:00:00:01");
128+
expect(timeAgo(secondAhead)).toBe("zo meteen");
129+
expect(timeAgo(secondAhead, { shortFormat: true })).toBe("0m");
130+
131+
const secondsAhead = new Date("2023-09-15:00:00:02");
132+
expect(timeAgo(secondsAhead)).toBe("zo meteen");
133+
expect(timeAgo(secondsAhead, { shortFormat: true })).toBe("0m");
86134
});
87135

136+
// Combined scenarios (past and future)
88137
test("timeAgo() handles combined scenario ", () => {
89138
const yearAgo = new Date("2022-08-14:23:59:59");
90139
expect(timeAgo(yearAgo)).toBe("1 jaar geleden");
@@ -93,6 +142,14 @@ describe("timeAgo()", () => {
93142
const monthsAgo = new Date("2023-07-13:22:58:58");
94143
expect(timeAgo(monthsAgo)).toBe("2 maanden geleden");
95144
expect(timeAgo(monthsAgo, { shortFormat: true })).toBe("2ma");
145+
146+
const yearAhead = new Date("2025-08-14:23:59:59");
147+
expect(timeAgo(yearAhead)).toBe("over 1 jaar");
148+
expect(timeAgo(yearAhead, { shortFormat: true })).toBe("over 1j");
149+
150+
const monthsAhead = new Date("2023-11-13:22:58:58");
151+
expect(timeAgo(monthsAhead)).toBe("over 1 maand");
152+
expect(timeAgo(monthsAhead, { shortFormat: true })).toBe("over 1ma");
96153
});
97154
});
98155

frontend/src/lib/format/date.ts

+19-10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface TimeAgoOptions {
2525
/**
2626
* Calculate how long ago or how long until a given date and return a human-readable string in Dutch.
2727
* The date can be provided as a Date object or an ISO 8601 string.
28+
* Note that this function does currently not show dates like "1 jaar 1 maand 1 dag geleden", but would rather show "1 jaar geleden"
2829
* TODO: Consider using a specialized library.
2930
*
3031
* @param dateInput - The date to calculate the time difference from. It can be a Date object or an ISO 8601 string.
@@ -48,24 +49,28 @@ export function timeAgo(
4849
let seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
4950
const { shortFormat = false } = options;
5051

52+
// Define the intervals in seconds for various time units
5153
const intervals = [
5254
{ label: "jaar", plural: "jaren", shortFormat: "j", seconds: 31536000 },
53-
{ label: "maand", plural: "maanden", shortFormat: "mnd", seconds: 2592000 },
55+
{ label: "maand", plural: "maanden", shortFormat: "ma", seconds: 2592000 },
5456
{ label: "week", plural: "weken", shortFormat: "w", seconds: 604800 },
5557
{ label: "dag", plural: "dagen", shortFormat: "d", seconds: 86400 },
56-
{ label: "uur", plural: "uren", shortFormat: "u", seconds: 3600 },
58+
{ label: "uur", plural: "uur", shortFormat: "u", seconds: 3600 },
5759
{ label: "minuut", plural: "minuten", shortFormat: "m", seconds: 60 },
5860
{ label: "seconde", plural: "seconden", shortFormat: "s", seconds: 1 },
5961
];
6062

6163
let result = "";
6264
let isFuture = false;
6365

64-
// If the time difference is positive, the date is in the past
65-
// If the time difference is negative, the date is in the future
6666
if (seconds < 0) {
6767
isFuture = true;
68-
seconds = Math.abs(seconds); // Work with positive time difference for calculation
68+
seconds = Math.abs(seconds);
69+
}
70+
71+
// Special case for "Nu" or "zo meteen"
72+
if (seconds < 60) {
73+
return shortFormat ? "0m" : isFuture ? "zo meteen" : "Nu";
6974
}
7075

7176
// Iterate over the intervals to determine the appropriate time unit
@@ -79,19 +84,23 @@ export function timeAgo(
7984
? interval.label
8085
: interval.plural;
8186

87+
// Check if it's future or past
8288
if (isFuture) {
8389
result = `over ${intervalCount}${shortFormat ? "" : " "}${label}`;
8490
} else {
85-
result = `${intervalCount}${shortFormat ? "" : " "}${label}`;
91+
// Special case to not include "geleden" for the short format
92+
if (shortFormat) {
93+
result = `${intervalCount}${shortFormat ? "" : " "}${label}`;
94+
} else {
95+
result = `${intervalCount}${shortFormat ? "" : " "}${label} geleden`;
96+
}
8697
}
8798
break;
8899
}
89100
}
90101

91-
// Return the result or default to "just now" or "0m" for short format
92-
return (
93-
result.trim() || (shortFormat ? "0m" : isFuture ? "zo meteen" : "zojuist")
94-
);
102+
// Return the formatted time difference
103+
return result.trim();
95104
}
96105

97106
/**

frontend/src/pages/destructionlist/detail/DestructionListDetail.stories.tsx

+62-1
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,10 @@ export const DeleteDestructionList: Story = {
414414
delay: 10,
415415
},
416416
);
417-
await waitFor(async () => await expect(submit).not.toBeDisabled());
417+
await waitFor(async () => {
418+
const isDisabled = submit.getAttribute("disabled");
419+
expect(isDisabled).toBe("");
420+
});
418421
},
419422
};
420423

@@ -450,3 +453,61 @@ export const DeleteFailedDestructionList: Story = {
450453
},
451454
},
452455
};
456+
457+
const FIXTURE_CANCEL_PLANNED_DESTRUCTION: DestructionListDetailContext = {
458+
storageKey: "storybook-storage-key",
459+
460+
destructionList: destructionListFactory({
461+
status: "ready_to_delete",
462+
processingStatus: "new",
463+
plannedDestructionDate: "2026-01-01T00:00:00Z",
464+
}),
465+
destructionListItems: paginatedDestructionListItemsFactory(),
466+
logItems: auditLogFactory(),
467+
468+
zaakSelection: {},
469+
selectableZaken: paginatedZakenFactory(),
470+
471+
archivists: usersFactory(),
472+
reviewers: usersFactory(),
473+
user: usersFactory()[0],
474+
475+
review: null,
476+
reviewItems: null,
477+
478+
selectieLijstKlasseChoicesMap: null,
479+
};
480+
481+
export const CancelPlannedDestruction: Story = {
482+
parameters: {
483+
reactRouterDecorator: {
484+
route: {
485+
loader: async () => FIXTURE_CANCEL_PLANNED_DESTRUCTION,
486+
},
487+
},
488+
},
489+
play: async (context) => {
490+
const canvas = within(context.canvasElement);
491+
const vernietigingStarten = await canvas.findByText<HTMLButtonElement>(
492+
"Vernietigen starten",
493+
);
494+
expect(vernietigingStarten).toBeDisabled();
495+
await clickButton({
496+
...context,
497+
parameters: {
498+
...context.parameters,
499+
name: "Vernietigen annuleren",
500+
},
501+
});
502+
await userEvent.click(document.activeElement as HTMLInputElement, {
503+
delay: 10,
504+
});
505+
userEvent.type(document.activeElement as HTMLInputElement, "Test Comment", {
506+
delay: 10,
507+
});
508+
const submit = await canvas.findByText<HTMLButtonElement>(
509+
"Vernietigen annuleren",
510+
);
511+
expect(submit).not.toBeDisabled();
512+
},
513+
};

0 commit comments

Comments
 (0)