-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathFeedback.tsx
More file actions
93 lines (83 loc) · 3.12 KB
/
Feedback.tsx
File metadata and controls
93 lines (83 loc) · 3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { type ReactNode, useEffect, useRef } from "react";
import { hasTranslation, t, tx } from "@/i18n/translate";
import type { Role, ValidationResult } from "@/types/generated/openapi";
import type { AlertType, FeedbackId } from "@/types/ui";
import { cn } from "@/utils/classnames";
import { dottedCode } from "@/utils/ValidationResults";
import { AlertIcon } from "../Icon/AlertIcon";
import cls from "./Feedback.module.css";
interface FeedbackItem {
title: string;
codes: string[];
content?: ReactNode;
actions?: ReactNode;
}
interface FeedbackProps {
id: FeedbackId;
type: AlertType;
data: ValidationResult[];
userRole: Role;
shouldFocus?: boolean;
}
export function Feedback({ id, type, data, userRole, shouldFocus = true }: FeedbackProps) {
const feedbackHeader = useRef<HTMLHeadingElement | null>(null);
// NOTE: administrator roles are always mapped to coordinator here
const role = userRole === "administrator" ? "coordinator" : userRole;
const feedbackList: FeedbackItem[] = [];
for (const { code, context } of data) {
const title = t(`feedback.${code}.${role}.title`, { ...context });
const contentPath = `feedback.${code}.${role}.content`;
const actionsPath = `feedback.${code}.${role}.actions`;
const content = hasTranslation(contentPath) ? tx(contentPath, undefined, { ...context }) : undefined;
const actions = hasTranslation(actionsPath) ? tx(actionsPath, undefined, { ...context }) : undefined;
const identical = feedbackList.find(
(item) => item.title === title && item.content === content && item.actions === actions,
);
if (identical) {
identical.codes.push(dottedCode(code));
} else {
feedbackList.push({
title,
codes: [dottedCode(code)],
content,
actions,
});
}
}
const actionsGrouped = role === "typist" && feedbackList.every((item) => item.actions === undefined);
const defaultActions = tx(`feedback.${role}_actions`);
useEffect(() => {
if (shouldFocus) {
feedbackHeader.current?.focus();
}
}, [shouldFocus]);
return (
<article id={id} className={cn(cls.feedback, cls[type])}>
{feedbackList.map((feedback, index) => (
<div key={`feedback-${feedback.codes.join("-")}`} className={cls.item}>
<header>
<AlertIcon type={type} />
<h3 tabIndex={-1} ref={index === 0 ? feedbackHeader : undefined} className="feedback-header">
{feedback.title}
</h3>
<span>{feedback.codes.join(", ")}</span>
</header>
{feedback.content && <div className={cls.content}>{feedback.content}</div>}
{!actionsGrouped && <div className={cls.actions}>{feedback.actions ?? defaultActions}</div>}
</div>
))}
{actionsGrouped && (
<div className={cls.actions}>
{feedbackList.length > 1 && (
<h3>
{t("feedback.multiple_action_title", {
type: type === "error" ? t("feedback.errors") : t("feedback.warnings"),
})}
</h3>
)}
{tx(`feedback.${role}_actions`)}
</div>
)}
</article>
);
}