@fayz-ai/plugin-forms 0.15.13 → 0.15.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,98 @@
1
+ import { alertOf, answerAlerts, yesNoAnswer } from './chunk-YOFFKQD4.js';
2
+ import './chunk-66C3L4RE.js';
3
+ import { Button } from '@fayz-ai/ui';
4
+ import React from 'react';
5
+ import { AlertTriangle, ChevronUp, ChevronDown } from 'lucide-react';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ var RANK = { danger: 3, warning: 2, info: 1 };
9
+ var TONE = {
10
+ danger: { wrap: "border-destructive/40 bg-destructive/5", chip: "bg-destructive text-destructive-foreground", dot: "bg-destructive" },
11
+ warning: { wrap: "border-warning/50 bg-warning-soft", chip: "bg-warning text-warning-foreground", dot: "bg-warning" },
12
+ info: { wrap: "border-info/40 bg-info-soft", chip: "bg-info text-white", dot: "bg-info" }
13
+ };
14
+ function PersonAnswerAlerts({ personId, provider }) {
15
+ const [alerts, setAlerts] = React.useState([]);
16
+ const [open, setOpen] = React.useState(false);
17
+ React.useEffect(() => {
18
+ if (!personId || !provider.getLatestAnswers) return;
19
+ let alive = true;
20
+ (async () => {
21
+ try {
22
+ const { data: templates } = await provider.getTemplates({ pageSize: 200 });
23
+ const withAlerts = templates.filter((t) => t.isActive && t.schema.fields.some((f) => alertOf(f)));
24
+ if (!withAlerts.length) return;
25
+ const answers = await provider.getLatestAnswers(personId, withAlerts.map((t) => t.id));
26
+ const out = [];
27
+ for (const a of answers) {
28
+ const t = withAlerts.find((x) => x.id === a.templateId);
29
+ if (!t) continue;
30
+ for (const f of t.schema.fields) {
31
+ const hit = answerAlerts(f, a.data[f.id]);
32
+ if (!hit) continue;
33
+ out.push({
34
+ key: `${t.id}:${f.id}`,
35
+ label: hit.message || f.label,
36
+ severity: hit.severity ?? "danger",
37
+ answer: yesNoAnswer(a.data[f.id]),
38
+ templateName: t.name
39
+ });
40
+ }
41
+ }
42
+ out.sort((x, y) => RANK[y.severity] - RANK[x.severity]);
43
+ if (alive) setAlerts(out);
44
+ } catch {
45
+ }
46
+ })();
47
+ return () => {
48
+ alive = false;
49
+ };
50
+ }, [personId, provider]);
51
+ if (!alerts.length) return null;
52
+ const tone = TONE[alerts[0].severity];
53
+ return /* @__PURE__ */ jsxs("div", { className: `overflow-hidden rounded-lg border-2 ${tone.wrap}`, children: [
54
+ /* @__PURE__ */ jsxs(
55
+ Button,
56
+ {
57
+ variant: "bare",
58
+ size: "bare",
59
+ type: "button",
60
+ onClick: () => setOpen((o) => !o),
61
+ "aria-expanded": open,
62
+ className: "font-normal whitespace-normal justify-start flex w-full items-center gap-2 px-3 py-2 text-left",
63
+ children: [
64
+ /* @__PURE__ */ jsx("span", { className: `flex h-7 w-7 shrink-0 items-center justify-center rounded-full ${tone.chip}`, children: /* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4" }) }),
65
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
66
+ /* @__PURE__ */ jsxs("span", { className: "block text-sm font-semibold leading-tight", children: [
67
+ alerts.length,
68
+ " alerta",
69
+ alerts.length > 1 ? "s" : "",
70
+ " no hist\xF3rico do cliente"
71
+ ] }),
72
+ /* @__PURE__ */ jsxs("span", { className: "block truncate text-xs text-muted-foreground", children: [
73
+ alerts.slice(0, 3).map((a) => a.label).join(" \xB7 "),
74
+ alerts.length > 3 ? "\u2026" : ""
75
+ ] })
76
+ ] }),
77
+ open ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })
78
+ ]
79
+ }
80
+ ),
81
+ open && /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border/40 border-t px-3", children: alerts.map((a) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2 py-2", children: [
82
+ /* @__PURE__ */ jsx("span", { className: `mt-1.5 h-2 w-2 shrink-0 rounded-full ${TONE[a.severity].dot}` }),
83
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
84
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-tight", children: a.label }),
85
+ /* @__PURE__ */ jsxs("p", { className: "mt-0.5 text-[11px] leading-tight text-muted-foreground", children: [
86
+ "Respondeu ",
87
+ /* @__PURE__ */ jsx("strong", { className: "uppercase", children: a.answer === "sim" ? "Sim" : "N\xE3o" }),
88
+ " \xB7 ",
89
+ a.templateName
90
+ ] })
91
+ ] })
92
+ ] }, a.key)) })
93
+ ] });
94
+ }
95
+
96
+ export { PersonAnswerAlerts };
97
+ //# sourceMappingURL=PersonAnswerAlerts-MDBBY63K.js.map
98
+ //# sourceMappingURL=PersonAnswerAlerts-MDBBY63K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/PersonAnswerAlerts.tsx"],"names":[],"mappings":"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","file":"PersonAnswerAlerts-MDBBY63K.js","sourcesContent":["import { Button } from '@fayz-ai/ui'\nimport React from 'react'\nimport { AlertTriangle, ChevronDown, ChevronUp } from 'lucide-react'\nimport type { FormsDataProvider } from '@fayz-ai/forms/data/provider'\nimport type { AnswerAlertConfig } from '@fayz-ai/forms/domain/template'\nimport { alertOf, answerAlerts, yesNoAnswer } from './fields/fields'\n\ninterface AlertItem {\n key: string\n label: string\n severity: NonNullable<AnswerAlertConfig['severity']>\n answer: 'sim' | 'nao'\n templateName: string\n}\n\nconst RANK = { danger: 3, warning: 2, info: 1 } as const\nconst TONE = {\n danger: { wrap: 'border-destructive/40 bg-destructive/5', chip: 'bg-destructive text-destructive-foreground', dot: 'bg-destructive' },\n warning: { wrap: 'border-warning/50 bg-warning-soft', chip: 'bg-warning text-warning-foreground', dot: 'bg-warning' },\n info: { wrap: 'border-info/40 bg-info-soft', chip: 'bg-info text-white', dot: 'bg-info' },\n} as const\n\n/**\n * V1 ClientAnamnesisAlerts: answers the clinic flagged (pregnant, anticoagulants, keloids…) stay\n * on the client's record from the latest answer of each form. Starts collapsed — the header is\n * the signal; the open list pushed the record off-screen in V1.\n */\nexport function PersonAnswerAlerts({ personId, provider }: { personId?: string; provider: FormsDataProvider }) {\n const [alerts, setAlerts] = React.useState<AlertItem[]>([])\n const [open, setOpen] = React.useState(false)\n\n React.useEffect(() => {\n if (!personId || !provider.getLatestAnswers) return\n let alive = true\n ;(async () => {\n try {\n const { data: templates } = await provider.getTemplates({ pageSize: 200 })\n const withAlerts = templates.filter((t) => t.isActive && t.schema.fields.some((f) => alertOf(f)))\n if (!withAlerts.length) return\n const answers = await provider.getLatestAnswers!(personId, withAlerts.map((t) => t.id))\n const out: AlertItem[] = []\n for (const a of answers) {\n const t = withAlerts.find((x) => x.id === a.templateId)\n if (!t) continue\n for (const f of t.schema.fields) {\n const hit = answerAlerts(f, a.data[f.id])\n if (!hit) continue\n out.push({\n key: `${t.id}:${f.id}`, label: hit.message || f.label, severity: hit.severity ?? 'danger',\n answer: yesNoAnswer(a.data[f.id]) as 'sim' | 'nao', templateName: t.name,\n })\n }\n }\n out.sort((x, y) => RANK[y.severity] - RANK[x.severity])\n if (alive) setAlerts(out)\n } catch { /* the record opens without the banner rather than not opening */ }\n })()\n return () => { alive = false }\n }, [personId, provider])\n\n if (!alerts.length) return null\n const tone = TONE[alerts[0].severity]\n return (\n <div className={`overflow-hidden rounded-lg border-2 ${tone.wrap}`}>\n <Button variant=\"bare\" size=\"bare\" type=\"button\" onClick={() => setOpen((o) => !o)} aria-expanded={open}\n className=\"font-normal whitespace-normal justify-start flex w-full items-center gap-2 px-3 py-2 text-left\">\n <span className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-full ${tone.chip}`}>\n <AlertTriangle className=\"h-4 w-4\" />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block text-sm font-semibold leading-tight\">\n {alerts.length} alerta{alerts.length > 1 ? 's' : ''} no histórico do cliente\n </span>\n <span className=\"block truncate text-xs text-muted-foreground\">\n {alerts.slice(0, 3).map((a) => a.label).join(' · ')}{alerts.length > 3 ? '…' : ''}\n </span>\n </span>\n {open ? <ChevronUp className=\"h-4 w-4 text-muted-foreground\" /> : <ChevronDown className=\"h-4 w-4 text-muted-foreground\" />}\n </Button>\n {open && (\n <ul className=\"divide-y divide-border/40 border-t px-3\">\n {alerts.map((a) => (\n <li key={a.key} className=\"flex items-start gap-2 py-2\">\n <span className={`mt-1.5 h-2 w-2 shrink-0 rounded-full ${TONE[a.severity].dot}`} />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium leading-tight\">{a.label}</p>\n <p className=\"mt-0.5 text-[11px] leading-tight text-muted-foreground\">\n Respondeu <strong className=\"uppercase\">{a.answer === 'sim' ? 'Sim' : 'Não'}</strong> · {a.templateName}\n </p>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n"]}