@fayz-ai/plugin-forms 0.10.14 → 0.11.1

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,17 @@
1
+ import React from 'react';
2
+ export interface FormFillDialogProps {
3
+ templateId: string;
4
+ personId: string;
5
+ personName?: string | null;
6
+ /** Depois de gravar — a régua reapura e a bola fecha sozinha. */
7
+ onSaved?: () => void;
8
+ onClose: () => void;
9
+ /**
10
+ * Onde o formulário mora. `inline` entrega só o miolo, para o anfitrião
11
+ * (agendamento, check-in, régua) desenhá-lo DENTRO da própria moldura em vez
12
+ * de abrir uma segunda janela por cima da primeira.
13
+ */
14
+ frame?: 'modal' | 'inline';
15
+ }
16
+ export declare function FormFillDialog({ templateId, personId, personName, onSaved, onClose, frame }: FormFillDialogProps): React.JSX.Element;
17
+ //# sourceMappingURL=FormFillDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormFillDialog.d.ts","sourceRoot":"","sources":["../../src/components/FormFillDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAyBzB,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAC3B;AAED,wBAAgB,cAAc,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,KAAe,EAAE,EAAE,mBAAmB,qBAwG1H"}
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ export interface PersonChecklistItem {
3
+ id: string;
4
+ label: string;
5
+ /** `done` risca e marca; `todo` deixa em aberto; `late` é o que venceu. */
6
+ state: 'done' | 'todo' | 'late';
7
+ /** A linha de contexto: "válido até …", "venceu em …", "nunca preenchido". */
8
+ detail?: string;
9
+ /** O verbo da linha, quando há o que fazer. */
10
+ action?: {
11
+ label: string;
12
+ onClick: () => void;
13
+ };
14
+ }
15
+ export interface PersonFormsAlertProps {
16
+ personId: string | null | undefined;
17
+ /** Abrir o formulário para preencher agora. Sem isto a faixa só informa. */
18
+ onFill?: (templateId: string) => void;
19
+ /**
20
+ * Sobe quando algo foi gravado: a lista relê. Sem isto ela continua dizendo
21
+ * "pendente" sobre o formulário que a pessoa ACABOU de preencher.
22
+ */
23
+ version?: number;
24
+ /**
25
+ * Itens de fora dos formulários — o que mais a casa quer conferir com este
26
+ * paciente antes de atender. Entram na mesma lista, com o mesmo desenho:
27
+ * uma checagem é uma checagem, venha de onde vier.
28
+ */
29
+ extras?: PersonChecklistItem[];
30
+ /**
31
+ * O que ainda falta, para quem hospeda a lista decidir o que fazer com isso
32
+ * — travar o botão de concluir, por exemplo. Vem com os rótulos, porque um
33
+ * botão travado sem dizer o motivo é pior do que um botão solto.
34
+ */
35
+ onPending?: (labels: string[]) => void;
36
+ className?: string;
37
+ }
38
+ export declare function PersonFormsAlert({ personId, onFill, version, extras, onPending, className }: PersonFormsAlertProps): React.JSX.Element | null;
39
+ //# sourceMappingURL=PersonFormsAlert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PersonFormsAlert.d.ts","sourceRoot":"","sources":["../../src/components/PersonFormsAlert.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AA4BzB,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IAC/B,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+CAA+C;IAC/C,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,CAAA;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACnC,4EAA4E;IAC5E,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IACtC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA4BD,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAW,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,qBAAqB,4BA0EtH"}
@@ -1 +1 @@
1
- {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/data/mock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAuBtD,wBAAgB,uBAAuB,IAAI,uBAAuB,CAwLjE"}
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/data/mock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAuBtD,wBAAgB,uBAAuB,IAAI,uBAAuB,CA0LjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAqItD,wBAAgB,2BAA2B,IAAI,uBAAuB,CAqVrE"}
1
+ {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAuItD,wBAAgB,2BAA2B,IAAI,uBAAuB,CAuVrE"}
package/dist/index.d.ts CHANGED
@@ -2,5 +2,8 @@ import type { PluginManifest } from '@fayz-ai/core';
2
2
  import type { CustomFormsPluginOptions } from './config';
3
3
  export type { CustomFormsPluginOptions };
4
4
  export { registerDocumentTypeProvider, type DocumentTypeOption, type DocumentTypeProvider } from './lib/document-types';
5
+ export { PersonFormsAlert, type PersonFormsAlertProps } from './components/PersonFormsAlert';
6
+ export { FormFillDialog, type FormFillDialogProps } from './components/FormFillDialog';
7
+ export { getPersonFormRequirements, isOutstanding, type PersonFormRequirement, type RequirementState, } from './lib/person-requirements';
5
8
  export declare function createCustomFormsPlugin(options?: CustomFormsPluginOptions): PluginManifest;
6
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAUnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AA4CxD,YAAY,EAAE,wBAAwB,EAAE,CAAA;AACxC,OAAO,EAAE,4BAA4B,EAAE,KAAK,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAEvH,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,cAAc,CAwL1F"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAUnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AA4CxD,YAAY,EAAE,wBAAwB,EAAE,CAAA;AACxC,OAAO,EAAE,4BAA4B,EAAE,KAAK,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAGvH,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AAG5F,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACtF,OAAO,EACL,yBAAyB,EAAE,aAAa,EACxC,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,GAClD,MAAM,2BAA2B,CAAA;AAElC,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,cAAc,CAwL1F"}
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { createOrUpdateFormTool, prefillFromPerson, FormRenderer, printDocument } from './chunk-RBLXIKWB.js';
2
- import React2, { useState, useCallback, useRef, useMemo, useEffect } from 'react';
3
- import { tl, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, useTranslation, getActiveTenantId, emitDomainEvent, useDataVersion } from '@fayz-ai/core';
4
- import { PluginSettingsPanel, registerTimelineSource, useLimitGuard, invalidateLimit, askAI, PermissionGate, PageActions } from '@fayz-ai/admin';
1
+ import { FormRenderer, createOrUpdateFormTool, prefillFromPerson, printDocument } from './chunk-RBLXIKWB.js';
2
+ import React10, { createContext, useState, useCallback, useRef, useMemo, useEffect } from 'react';
3
+ import { tl, createSafeDataProvider, registerTranslations, getSupabaseClientOptional, useTranslation, getActiveTenantId, emitDomainEvent, useDataVersion } from '@fayz-ai/core';
4
+ import { getFayzSupabaseClient, useOrganizationStore, PluginSettingsPanel, registerTimelineSource, useLimitGuard, invalidateLimit, askAI, PermissionGate, PageActions } from '@fayz-ai/admin';
5
5
  import { create } from 'zustand';
6
- import { FileText, Image, Paperclip, Plus, WandSparkles, Pencil, Archive, Search, ArrowLeft, Printer, ChevronDown, Upload, Eye } from 'lucide-react';
7
- import { Badge, RowActions, Button, PageHeaderActions, Card, DataTable, useSaveBar, Dropdown, DropdownTrigger, DropdownContent, DropdownSeparator, DropdownLabel } from '@fayz-ai/ui';
6
+ import { ClipboardList, CheckCircle2, Clock, Circle, FileText, Image, Paperclip, Plus, WandSparkles, Pencil, Archive, Search, ArrowLeft, Printer, ChevronDown, Upload, Eye } from 'lucide-react';
7
+ import { Skeleton, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, Badge, Switch, RowActions, Button, PageHeaderActions, Card, DataTable, useSaveBar, Dropdown, DropdownTrigger, DropdownContent, DropdownSeparator, DropdownLabel } from '@fayz-ai/ui';
8
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
9
 
10
10
  // src/data/tables.ts
@@ -60,6 +60,8 @@ function createMockFormsProvider() {
60
60
  metadata: input.metadata ?? {},
61
61
  isActive: true,
62
62
  isDeleted: false,
63
+ requiredOnAttendance: false,
64
+ validityDays: null,
63
65
  createdAt: now(),
64
66
  updatedAt: now()
65
67
  };
@@ -224,6 +226,8 @@ async function countTemplateUsageClientSide(db, tenantId) {
224
226
  }
225
227
  function mapTemplate(r) {
226
228
  return {
229
+ requiredOnAttendance: r.required_on_attendance ?? false,
230
+ validityDays: r.validity_days ?? null,
227
231
  id: r.id,
228
232
  tenantId: r.tenant_id,
229
233
  name: r.name,
@@ -348,7 +352,12 @@ function createSupabaseFormsProvider() {
348
352
  parent_id: existing.parentId ?? existing.id
349
353
  }).select().single();
350
354
  if (insertError) throw new Error(insertError.message);
351
- await db.from(T.templates).update({ is_current: false, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
355
+ await db.from(T.templates).update({
356
+ ...input.requiredOnAttendance !== void 0 ? { required_on_attendance: input.requiredOnAttendance } : {},
357
+ ...input.validityDays !== void 0 ? { validity_days: input.validityDays } : {},
358
+ is_current: false,
359
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
360
+ }).eq("id", id);
352
361
  return mapTemplate(newRow);
353
362
  }
354
363
  const updates = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
@@ -958,6 +967,38 @@ function TemplateListView({ store, config, onEdit, onNew }) {
958
967
  ] })
959
968
  ] })
960
969
  },
970
+ {
971
+ id: "required",
972
+ header: "Exigido",
973
+ // A PORTA da migration 001. Sem ela as duas colunas novas ficam no banco
974
+ // e o aviso de formulário pendente nunca acende para ninguém — código
975
+ // pronto sem consumidor é o mesmo que código ausente.
976
+ cell: ({ row }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
977
+ /* @__PURE__ */ jsx(
978
+ Switch,
979
+ {
980
+ size: "sm",
981
+ checked: row.original.requiredOnAttendance,
982
+ onCheckedChange: (on) => {
983
+ void store.getState().updateTemplate(row.original.id, {
984
+ requiredOnAttendance: on,
985
+ // Seis meses é o que o V1 usa na anamnese, e é o padrão que faz
986
+ // a validade existir sem pedir mais uma decisão na hora de
987
+ // ligar. Zero validade é o mesmo que "vale para sempre", que
988
+ // deixa uma anamnese de 2019 contando como preenchida.
989
+ validityDays: on ? row.original.validityDays ?? 180 : null
990
+ });
991
+ },
992
+ "aria-label": `Exigir ${row.original.name} de todo cliente`
993
+ }
994
+ ),
995
+ row.original.requiredOnAttendance && /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-muted-foreground", children: [
996
+ "vale ",
997
+ row.original.validityDays ?? "\u221E",
998
+ " dias"
999
+ ] })
1000
+ ] })
1001
+ },
961
1002
  {
962
1003
  id: "usage",
963
1004
  header: t("customForms.usageColumn"),
@@ -1063,7 +1104,7 @@ function CustomFormsSettingsTab({ config, provider, store }) {
1063
1104
  window.location.hash = `#${ROUTE_BASE}/forms`;
1064
1105
  store.getState().fetchTemplates();
1065
1106
  }, [store]);
1066
- React2.useEffect(() => {
1107
+ React10.useEffect(() => {
1067
1108
  const sync = () => setBuilderTemplateId(readBuilderIdFromHash());
1068
1109
  window.addEventListener("hashchange", sync);
1069
1110
  return () => window.removeEventListener("hashchange", sync);
@@ -1094,11 +1135,11 @@ function BuilderWrapper({
1094
1135
  config,
1095
1136
  onBack
1096
1137
  }) {
1097
- const FormBuilderLazy = React2.lazy(
1138
+ const FormBuilderLazy = React10.lazy(
1098
1139
  () => import('./FormBuilder-YHJHF6HZ.js').then((m) => ({ default: m.FormBuilder }))
1099
1140
  );
1100
1141
  return /* @__PURE__ */ jsx(
1101
- React2.Suspense,
1142
+ React10.Suspense,
1102
1143
  {
1103
1144
  fallback: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx("div", { className: "h-6 w-6 border-2 border-primary border-t-transparent animate-spin rounded-full" }) }),
1104
1145
  children: /* @__PURE__ */ jsx(
@@ -1334,7 +1375,7 @@ function DocumentFormDialog({
1334
1375
  provider.getTemplateById(existingDocument.templateId).then(setTemplate);
1335
1376
  }
1336
1377
  }, [existingDocument, template, provider]);
1337
- const prefilled = React2.useRef(!!initialTemplate || !!existingDocument);
1378
+ const prefilled = React10.useRef(!!initialTemplate || !!existingDocument);
1338
1379
  useEffect(() => {
1339
1380
  if (prefilled.current || !template || existingDocument) return;
1340
1381
  prefilled.current = true;
@@ -1366,7 +1407,7 @@ function DocumentFormDialog({
1366
1407
  },
1367
1408
  [template, existingDocument, data, personId, personName, store, onSaved, guardDocuments]
1368
1409
  );
1369
- const snapshot = React2.useRef(null);
1410
+ const snapshot = React10.useRef(null);
1370
1411
  useEffect(() => {
1371
1412
  if (template && snapshot.current === null) {
1372
1413
  snapshot.current = JSON.stringify(existingDocument?.data ?? {});
@@ -1616,7 +1657,7 @@ function AddDocumentDropdown({ personId, onSelect }) {
1616
1657
  )
1617
1658
  ] }),
1618
1659
  /* @__PURE__ */ jsxs("div", { ref: listRef, className: "max-h-[min(60vh,20rem)] overflow-y-auto overscroll-contain p-1", children: [
1619
- groups.map((group, gi) => /* @__PURE__ */ jsxs(React2.Fragment, { children: [
1660
+ groups.map((group, gi) => /* @__PURE__ */ jsxs(React10.Fragment, { children: [
1620
1661
  gi > 0 && /* @__PURE__ */ jsx(DropdownSeparator, {}),
1621
1662
  /* @__PURE__ */ jsx(DropdownLabel, { className: "text-[10px] uppercase tracking-wider", children: group.label }),
1622
1663
  group.items.map((opt) => {
@@ -1924,6 +1965,191 @@ function buildFormsAnalyticsCards() {
1924
1965
  }
1925
1966
  ];
1926
1967
  }
1968
+ async function getPersonFormRequirements(tenantId, personId) {
1969
+ const client = getFayzSupabaseClient();
1970
+ const { data, error } = await client.rpc("forms_person_requirements", {
1971
+ p_tenant_id: tenantId,
1972
+ p_person_id: personId
1973
+ });
1974
+ if (error) throw error;
1975
+ return (data ?? []).map((r) => ({
1976
+ templateId: r.template_id,
1977
+ templateName: r.template_name,
1978
+ category: r.category,
1979
+ validityDays: r.validity_days ?? null,
1980
+ lastFilledAt: r.last_filled_at ?? null,
1981
+ validUntil: r.valid_until ?? null,
1982
+ state: r.state
1983
+ }));
1984
+ }
1985
+ var isOutstanding = (r) => r.state !== "valid";
1986
+ var DATE = (iso) => (/* @__PURE__ */ new Date(iso + "T00:00:00")).toLocaleDateString("pt-BR");
1987
+ function Linha({ item }) {
1988
+ const Icone = item.state === "done" ? CheckCircle2 : item.state === "late" ? Clock : Circle;
1989
+ const cor = item.state === "done" ? "text-success" : item.state === "late" ? "text-warning" : "text-muted-foreground";
1990
+ return /* @__PURE__ */ jsxs("li", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 px-3 py-1.5 text-xs", children: [
1991
+ /* @__PURE__ */ jsx(Icone, { className: `h-3.5 w-3.5 shrink-0 ${cor}`, "aria-hidden": true }),
1992
+ /* @__PURE__ */ jsx("span", { className: item.state === "done" ? "text-foreground" : "font-medium text-foreground", children: item.label }),
1993
+ item.detail && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
1994
+ "\u2014 ",
1995
+ item.detail
1996
+ ] }),
1997
+ item.action && // O VERBO NÃO PODE PARECER DESLIGADO. Em `text-primary` sobre o âmbar
1998
+ // da faixa ele saía lavado e, quando quebrava a linha, parecia um
1999
+ // rótulo cinza no canto — a recepção lia "pendente" e não via que
2000
+ // havia o que clicar.
2001
+ /* @__PURE__ */ jsx(
2002
+ "button",
2003
+ {
2004
+ type: "button",
2005
+ onClick: item.action.onClick,
2006
+ className: "ml-auto shrink-0 rounded-button bg-foreground px-2 py-0.5 text-[11px] font-semibold text-background hover:opacity-90",
2007
+ children: item.action.label
2008
+ }
2009
+ )
2010
+ ] });
2011
+ }
2012
+ function PersonFormsAlert({ personId, onFill, version = 0, extras, onPending, className }) {
2013
+ const tenantId = useOrganizationStore((s) => s.currentOrg?.id);
2014
+ const [rows, setRows] = React10.useState(null);
2015
+ React10.useEffect(() => {
2016
+ if (!personId || !tenantId) {
2017
+ setRows([]);
2018
+ return;
2019
+ }
2020
+ let cancelled = false;
2021
+ setRows(null);
2022
+ getPersonFormRequirements(tenantId, personId).then((r) => {
2023
+ if (!cancelled) setRows(r);
2024
+ }).catch(() => {
2025
+ if (!cancelled) setRows([]);
2026
+ });
2027
+ return () => {
2028
+ cancelled = true;
2029
+ };
2030
+ }, [personId, tenantId, version]);
2031
+ const itens = React10.useMemo(() => [
2032
+ ...(rows ?? []).map((r) => {
2033
+ const pendente = isOutstanding(r);
2034
+ return {
2035
+ id: r.templateId,
2036
+ label: r.templateName,
2037
+ state: !pendente ? "done" : r.state === "expired" ? "late" : "todo",
2038
+ detail: !pendente ? r.validUntil ? `em dia, v\xE1lido at\xE9 ${DATE(r.validUntil)}` : "em dia" : r.state === "expired" && r.validUntil ? `venceu em ${DATE(r.validUntil)}` : "nunca preenchido",
2039
+ action: pendente && onFill ? { label: "Preencher agora", onClick: () => onFill(r.templateId) } : void 0
2040
+ };
2041
+ }),
2042
+ ...extras ?? []
2043
+ ], [rows, extras, onFill]);
2044
+ const peso = (i) => i.state === "late" ? 0 : i.state === "todo" ? 1 : 2;
2045
+ itens.sort((a, b) => peso(a) - peso(b));
2046
+ const pendentes = itens.filter((i) => i.state !== "done");
2047
+ const faltando = pendentes.length;
2048
+ const chave = pendentes.map((i) => i.label).join("|");
2049
+ React10.useEffect(() => {
2050
+ onPending?.(chave ? chave.split("|") : []);
2051
+ }, [chave]);
2052
+ if (rows === null) return /* @__PURE__ */ jsx(Skeleton, { className: `h-12 w-full ${className ?? ""}` });
2053
+ if (itens.length === 0) return null;
2054
+ return /* @__PURE__ */ jsxs("div", { className: `overflow-hidden rounded-lg border ${faltando ? "border-warning/40 bg-warning/5" : "border-success/30 bg-success/5"} ${className ?? ""}`, children: [
2055
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 border-b border-inherit px-3 py-1.5 text-[11px] font-medium text-foreground", children: [
2056
+ /* @__PURE__ */ jsx(ClipboardList, { className: `h-3.5 w-3.5 ${faltando ? "text-warning" : "text-success"}`, "aria-hidden": true }),
2057
+ faltando === 0 ? "Checagem do paciente \u2014 tudo em dia" : faltando === 1 ? "1 item pendente com o paciente" : `${faltando} itens pendentes com o paciente`
2058
+ ] }),
2059
+ /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border/60", children: itens.map((i) => /* @__PURE__ */ jsx(Linha, { item: i }, i.id)) })
2060
+ ] });
2061
+ }
2062
+ var Ctx = createContext(null);
2063
+ function CustomFormsProvider({
2064
+ value,
2065
+ children
2066
+ }) {
2067
+ return React10.createElement(Ctx.Provider, { value }, children);
2068
+ }
2069
+ function FormFillDialog({ templateId, personId, personName, onSaved, onClose, frame = "modal" }) {
2070
+ const provider = React10.useMemo(() => createSafeDataProvider(
2071
+ () => createSupabaseFormsProvider(),
2072
+ () => createMockFormsProvider()
2073
+ ), []);
2074
+ const store = React10.useMemo(() => createCustomFormsStore(provider), [provider]);
2075
+ const config = React10.useMemo(() => resolveConfig(), []);
2076
+ const [template, setTemplate] = React10.useState(null);
2077
+ const [erro, setErro] = React10.useState(null);
2078
+ const [data, setData] = React10.useState({});
2079
+ const [salvando, setSalvando] = React10.useState(false);
2080
+ async function salvar() {
2081
+ if (!template) return;
2082
+ setSalvando(true);
2083
+ try {
2084
+ await store.getState().createDocument({
2085
+ templateId: template.id,
2086
+ personId,
2087
+ title: personName ? `${template.name} \u2014 ${personName}` : template.name,
2088
+ data,
2089
+ status: "completed"
2090
+ });
2091
+ onSaved?.();
2092
+ onClose();
2093
+ } catch (e) {
2094
+ setErro(e instanceof Error ? e.message : "N\xE3o foi poss\xEDvel salvar o formul\xE1rio.");
2095
+ } finally {
2096
+ setSalvando(false);
2097
+ }
2098
+ }
2099
+ React10.useEffect(() => {
2100
+ let vivo = true;
2101
+ (async () => {
2102
+ try {
2103
+ const t = await store.getState().fetchTemplateById(templateId);
2104
+ if (vivo) setTemplate(t);
2105
+ } catch (e) {
2106
+ if (vivo) setErro(e instanceof Error ? e.message : "N\xE3o foi poss\xEDvel abrir o formul\xE1rio.");
2107
+ }
2108
+ })();
2109
+ return () => {
2110
+ vivo = false;
2111
+ };
2112
+ }, [templateId, store]);
2113
+ const corpo = /* @__PURE__ */ jsxs(Fragment, { children: [
2114
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-5 py-4", children: erro ? /* @__PURE__ */ jsx("p", { className: "rounded-lg bg-destructive/10 px-3 py-2 text-xs text-destructive", children: erro }) : !template ? (
2115
+ /* Esqueleto com a FORMA do formulário: três campos e um rótulo. */
2116
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", "aria-busy": "true", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
2117
+ /* @__PURE__ */ jsx("div", { className: "h-3 w-24 animate-pulse rounded bg-muted/70" }),
2118
+ /* @__PURE__ */ jsx("div", { className: "h-9 w-full animate-pulse rounded-lg bg-muted" })
2119
+ ] }, i)) })
2120
+ ) : /* @__PURE__ */ jsx(CustomFormsProvider, { value: { config, provider, store }, children: /* @__PURE__ */ jsx(FormRenderer, { schema: template.schema, data, onChange: setData }) }) }),
2121
+ /* @__PURE__ */ jsx("div", { className: "border-t fayz-glass-divider px-5 py-2.5 pb-[calc(0.625rem+env(safe-area-inset-bottom))] sm:pb-2.5 shrink-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
2122
+ /* @__PURE__ */ jsx(
2123
+ "button",
2124
+ {
2125
+ type: "button",
2126
+ onClick: onClose,
2127
+ className: "rounded-button px-2 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground",
2128
+ children: "Agora n\xE3o"
2129
+ }
2130
+ ),
2131
+ /* @__PURE__ */ jsx(
2132
+ "button",
2133
+ {
2134
+ type: "button",
2135
+ onClick: salvar,
2136
+ disabled: !template || salvando,
2137
+ className: "rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40",
2138
+ children: salvando ? "Salvando\u2026" : "Concluir"
2139
+ }
2140
+ )
2141
+ ] }) })
2142
+ ] });
2143
+ if (frame === "inline") return corpo;
2144
+ return /* @__PURE__ */ jsx(Modal, { open: true, onOpenChange: (o) => {
2145
+ if (!o) onClose();
2146
+ }, children: /* @__PURE__ */ jsxs(ModalContent, { size: "2xl", noPadding: true, className: "gap-0", children: [
2147
+ /* @__PURE__ */ jsx(ModalHeader, { className: "border-b px-5 py-3.5", children: /* @__PURE__ */ jsx(ModalTitle, { className: "truncate text-base", children: template?.name ?? "Formul\xE1rio" }) }),
2148
+ /* @__PURE__ */ jsx(ModalBody, { className: "p-0", children: corpo })
2149
+ ] }) });
2150
+ }
2151
+
2152
+ // src/index.ts
1927
2153
  function registerBuiltInProviders(formsProvider) {
1928
2154
  registerDocumentTypeProvider({
1929
2155
  id: "custom_forms:templates",
@@ -1998,14 +2224,14 @@ function createCustomFormsPlugin(options) {
1998
2224
  }
1999
2225
  ];
2000
2226
  const registries = [...formRegistries, ...options?.settingsRegistries ?? []];
2001
- const SettingsComponent = () => React2.createElement(PluginSettingsPanel, {
2227
+ const SettingsComponent = () => React10.createElement(PluginSettingsPanel, {
2002
2228
  title: config.labels.settingsLabel,
2003
2229
  customTabs: [
2004
2230
  {
2005
2231
  id: "forms",
2006
2232
  label: config.labels.pageTitle,
2007
2233
  icon: "FileText",
2008
- content: React2.createElement(CustomFormsSettingsTab, { config, provider, store })
2234
+ content: React10.createElement(CustomFormsSettingsTab, { config, provider, store })
2009
2235
  }
2010
2236
  ],
2011
2237
  registries,
@@ -2013,7 +2239,7 @@ function createCustomFormsPlugin(options) {
2013
2239
  hostPluginId: "custom_forms"
2014
2240
  });
2015
2241
  SettingsComponent.displayName = "CustomFormsSettingsPanel";
2016
- const PersonDocumentsWidget2 = (props) => React2.createElement(PersonDocumentsWidget, { ...props, config, provider, store });
2242
+ const PersonDocumentsWidget2 = (props) => React10.createElement(PersonDocumentsWidget, { ...props, config, provider, store });
2017
2243
  PersonDocumentsWidget2.displayName = "PersonDocumentsWidget";
2018
2244
  return {
2019
2245
  id: "custom_forms",
@@ -2119,6 +2345,6 @@ function createCustomFormsPlugin(options) {
2119
2345
  };
2120
2346
  }
2121
2347
 
2122
- export { createCustomFormsPlugin, registerDocumentTypeProvider };
2348
+ export { FormFillDialog, PersonFormsAlert, createCustomFormsPlugin, getPersonFormRequirements, isOutstanding, registerDocumentTypeProvider };
2123
2349
  //# sourceMappingURL=index.js.map
2124
2350
  //# sourceMappingURL=index.js.map