@fayz-ai/plugin-agenda 0.20.0 → 0.21.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.
@@ -1 +1 @@
1
- {"version":3,"file":"BookingPaymentPanel.d.ts","sourceRoot":"","sources":["../../src/components/BookingPaymentPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAkClD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC1E,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1C,gEAAgE;IAChE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,KAAK,qBA4V7H"}
1
+ {"version":3,"file":"BookingPaymentPanel.d.ts","sourceRoot":"","sources":["../../src/components/BookingPaymentPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAkClD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC1E,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1C,gEAAgE;IAChE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,KAAK,qBAqW7H"}
@@ -1 +1 @@
1
- {"version":3,"file":"SeriesDeleteChoice.d.ts","sourceRoot":"","sources":["../../src/components/SeriesDeleteChoice.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAGlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AASpD,UAAU,MAAM;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAEzD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI;;;;oBA2BtD,WAAW;EAYzC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;IAC7E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACpC,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB,qBAuBA"}
1
+ {"version":3,"file":"SeriesDeleteChoice.d.ts","sourceRoot":"","sources":["../../src/components/SeriesDeleteChoice.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAGlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AASpD,UAAU,MAAM;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAEzD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI;;;;oBA2BtD,WAAW;EAYzC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;IAC7E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACpC,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB,qBA2BA"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { createMockAgendaProvider, createSupabasePublicBookingProvider } from '.
2
2
  export { createMockAgendaProvider } from './chunk-A77PUFY5.js';
3
3
  import React21, { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } from 'react';
4
4
  import { tl, recordRailFields, RECORD_GROUP, activeFacet, connectionStatus, createConnectionStore, getActiveTenantId, createSafeDataProvider, todayLocal, getSupabaseClientOptional, registerTranslations, registerMessageDefaults, getActiveUnitId, assertWrote, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, emitDataChanged, globalCache, usePluginRuntimeOptional, useTranslation, useActiveUnitId, emitDomainEvent, getCurrentLocale, localDateKey, useDataChanged, formatCurrency, resolveDataProvider } from '@fayz-ai/core';
5
- import { createPluginContext, fayzConnectorOAuth, setScheduleBlockConfig, getScheduleBlockConfig, registerPersonActions, PluginSettingsPanel, createArchetypeLookup, dedup, usePluginPrefs, useTenantPluginSettings, SettingsGroup, ToggleRow, SelectRow, PermissionGate, PersonLink, PageActions, useRightRailStore, usePermissionOptional, ScheduleEditor, useLimitGuard, useLookupCreate, CrudFormPage, CrudDetailPage, ContactPicker, openEntity, PaymentStatusBadge as PaymentStatusBadge$1, RightRailPage, invalidateLimit } from '@fayz-ai/admin';
5
+ import { createPluginContext, fayzConnectorOAuth, UNIT_DETAIL_ZONES, setScheduleBlockConfig, getScheduleBlockConfig, registerPersonActions, PluginSettingsPanel, adminNavigateTo, createArchetypeLookup, dedup, usePluginPrefs, useTenantPluginSettings, SettingsGroup, ToggleRow, SelectRow, PermissionGate, PersonLink, PageActions, useRightRailStore, usePermissionOptional, ScheduleEditor, useLimitGuard, useLookupCreate, CrudFormPage, CrudDetailPage, ContactPicker, openEntity, PaymentStatusBadge as PaymentStatusBadge$1, RightRailPage, invalidateLimit } from '@fayz-ai/admin';
6
6
  import FullCalendar from '@fullcalendar/react';
7
7
  import dayGridPlugin from '@fullcalendar/daygrid';
8
8
  import timeGridPlugin from '@fullcalendar/timegrid';
@@ -11,7 +11,7 @@ import interactionPlugin from '@fullcalendar/interaction';
11
11
  import listPlugin from '@fullcalendar/list';
12
12
  import ptBrLocale from '@fullcalendar/core/locales/pt-br';
13
13
  import { CheckCircle, Stethoscope, RotateCcw, Check, CircleDashed, Layers, Users, Activity, RefreshCw, Clock3, CalendarPlus, Loader2, AlertCircle, X, CheckCircle2, Ban, Plus, Settings, ChevronLeft, ChevronRight, ChevronDown, CircleDollarSign, LogIn, CalendarDays, Play, UserRound, Package, Clock, Mic, MapPin, CalendarX, UserX, Search, CalendarOff, Pencil, Trash2, User, FileText, AlertTriangle, DollarSign, HandCoins, CheckSquare, Calendar, History, ExternalLink, DoorOpen, Briefcase, CircleDot, MessageCircle, Phone, AlarmClock, ArrowLeft, Link2, Receipt, ClipboardCheck, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, Sparkles } from 'lucide-react';
14
- import { toast, FilterRail, AdaptiveFilter, FilterCheckRow, TooltipProvider, ModulePage, Card, CardContent, Button, Avatar, AvatarImage, AvatarFallback, Badge, Checkbox, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, PersonAvatar, abbreviatePersonName, INLINE_FILTER_MAX, PersonMultiPicker, SegmentedControl, Switch, MiniCalendar, PageHeaderActions, ConfirmDialog, RailWorkspace, Tooltip, TooltipTrigger, TooltipContent, SubpageHeader, ModalTask, Skeleton, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, PersonPicker, Input, Tabs, TabsList, TabsTrigger, DatePicker, TimePicker, ModalFooter, ModalTaskHost, SearchCombobox, ModalTaskHeader, Dropdown, DropdownTrigger, DropdownContent, DropdownItem, cn, normalizePhone, DotBadge } from '@fayz-ai/ui';
14
+ import { Card, CardHeader, CardTitle, CardDescription, CardContent, Skeleton, Switch, Input, Button, toast, FilterRail, AdaptiveFilter, FilterCheckRow, TooltipProvider, ModulePage, Avatar, AvatarImage, AvatarFallback, Badge, Checkbox, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, PersonAvatar, abbreviatePersonName, INLINE_FILTER_MAX, PersonMultiPicker, SegmentedControl, MiniCalendar, PageHeaderActions, ConfirmDialog, RailWorkspace, Tooltip, TooltipTrigger, TooltipContent, SubpageHeader, ModalTask, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, PersonPicker, Tabs, TabsList, TabsTrigger, DatePicker, TimePicker, ModalFooter, ModalTaskHost, SearchCombobox, ModalTaskHeader, Dropdown, DropdownTrigger, DropdownContent, DropdownItem, cn, normalizePhone, DotBadge } from '@fayz-ai/ui';
15
15
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
16
16
  import { createPortal } from 'react-dom';
17
17
  import { FormFillDialog, PersonFormsAlert } from '@fayz-ai/plugin-forms';
@@ -1386,7 +1386,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
1386
1386
  /* @__PURE__ */ jsxs(
1387
1387
  "a",
1388
1388
  {
1389
- href: `#/financial/receivables/detail/${detail.invoiceId}`,
1389
+ href: detail.status === "paid" || detail.status === "cancelled" ? `#/financial/receivables/detail/${detail.invoiceId}` : `#/financial/receivables/${detail.invoiceId}/edit`,
1390
1390
  className: "inline-flex items-center gap-1.5 text-xs font-medium text-primary hover:underline ml-auto",
1391
1391
  children: [
1392
1392
  t2("agenda.payment.viewInvoice"),
@@ -3483,9 +3483,9 @@ function useSeriesDelete(booking, enabled, onDone) {
3483
3483
  }
3484
3484
  function SeriesDeleteChoice({ seriesSize, deleting, onPick, onCancel }) {
3485
3485
  const t2 = useTranslation();
3486
- const allLabel = t2("agenda.recurrence.scopeSeries").replace("{{count}}", seriesSize ? String(seriesSize) : "\u2026");
3487
- const btn = "w-full rounded px-2 py-1 text-left text-[11px] font-medium text-destructive hover:bg-destructive/10 disabled:opacity-40 transition-colors";
3488
- return /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 space-y-1", children: [
3486
+ const allLabel = t2("agenda.recurrence.scopeSeriesShort").replace("{{count}}", seriesSize ? String(seriesSize) : "\u2026");
3487
+ const btn = "flex-1 min-w-0 rounded border border-destructive/30 px-2 py-1 text-center text-[11px] font-medium text-destructive hover:bg-destructive/10 disabled:opacity-40 transition-colors";
3488
+ return /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 space-y-1.5", children: [
3489
3489
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3490
3490
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-destructive", children: t2("agenda.recurrence.scopeDeleteTitle") }),
3491
3491
  /* @__PURE__ */ jsx(
@@ -3499,8 +3499,10 @@ function SeriesDeleteChoice({ seriesSize, deleting, onPick, onCancel }) {
3499
3499
  }
3500
3500
  )
3501
3501
  ] }),
3502
- /* @__PURE__ */ jsx("button", { type: "button", className: btn, disabled: deleting !== null, onClick: () => onPick("this"), children: deleting === "this" ? t2("agenda.appointment.deleting") : t2("agenda.recurrence.scopeThis") }),
3503
- /* @__PURE__ */ jsx("button", { type: "button", className: btn, disabled: deleting !== null, onClick: () => onPick("series"), children: deleting === "series" ? t2("agenda.appointment.deleting") : allLabel })
3502
+ /* @__PURE__ */ jsxs("div", { className: "flex items-stretch gap-1.5", children: [
3503
+ /* @__PURE__ */ jsx("button", { type: "button", className: btn, disabled: deleting !== null, onClick: () => onPick("this"), children: deleting === "this" ? t2("agenda.appointment.deleting") : t2("agenda.recurrence.scopeThisShort") }),
3504
+ /* @__PURE__ */ jsx("button", { type: "button", className: btn, disabled: deleting !== null, onClick: () => onPick("series"), children: deleting === "series" ? t2("agenda.appointment.deleting") : allLabel })
3505
+ ] })
3504
3506
  ] });
3505
3507
  }
3506
3508
  function fmtTime(iso) {
@@ -10196,6 +10198,8 @@ var en = {
10196
10198
  "agenda.recurrence.scopeDeleteTitle": "Delete recurring appointment",
10197
10199
  "agenda.recurrence.scopeThis": "This appointment",
10198
10200
  "agenda.recurrence.scopeSeries": "All in the series ({{count}})",
10201
+ "agenda.recurrence.scopeThisShort": "This one",
10202
+ "agenda.recurrence.scopeSeriesShort": "All ({{count}})",
10199
10203
  "agenda.recurrence.dateStaysLocal": "The new date applies to this appointment only: every occurrence keeps its own day, and only the time becomes {{time}}."
10200
10204
  };
10201
10205
 
@@ -10503,6 +10507,8 @@ var ptBR = {
10503
10507
  "agenda.recurrence.scopeDeleteTitle": "Excluir agendamento recorrente",
10504
10508
  "agenda.recurrence.scopeThis": "Este agendamento",
10505
10509
  "agenda.recurrence.scopeSeries": "Todos da s\xE9rie ({{count}})",
10510
+ "agenda.recurrence.scopeThisShort": "S\xF3 este",
10511
+ "agenda.recurrence.scopeSeriesShort": "Todos ({{count}})",
10506
10512
  "agenda.recurrence.dateStaysLocal": "A data nova vale s\xF3 para este agendamento: cada ocorr\xEAncia da s\xE9rie mant\xE9m o dia dela, e s\xF3 o hor\xE1rio passa a ser {{time}}."
10507
10513
  };
10508
10514
 
@@ -15103,6 +15109,63 @@ GRANT EXECUTE ON FUNCTION public.agenda_series_update(uuid, text, uuid, uuid, uu
15103
15109
  COMMENT ON FUNCTION public.agenda_series_update(uuid, text, uuid, uuid, uuid, text, text, text[]) IS
15104
15110
  'Aplica a mesma mudan\xE7a a todas as ocorr\xEAncias da s\xE9rie num UPDATE s\xF3, preservando o DIA de cada uma. N\xE3o toca nas linhas de servi\xE7o \u2014 essa regra vive no cliente.';
15105
15111
  `;
15112
+ var MIGRATION_020_A_LISTA_DA_VEZ_SE_LIGA_NO_CADASTRO_DA_UNIDADE = `-- A lista da vez se liga no cadastro da unidade.
15113
+ --
15114
+ -- No V1 o interruptor "Ativar lista da vez nesta unidade" e o raio da cerca
15115
+ -- moram no cadastro da empresa (V1: companies.turn_queue_enabled,
15116
+ -- turn_queue_radius_m). Em 016 a fila nasceu sem tela de configura\xE7\xE3o: a linha
15117
+ -- surge sozinha, LIGADA, no primeiro "estou dispon\xEDvel", e o app s\xF3 pode l\xEA-la.
15118
+ --
15119
+ -- Esta RPC \xE9 a porta que faltava \u2014 a aba "Lista da vez" da unidade grava por
15120
+ -- ela. Quem configura \xE9 quem administra a filial (settings.manage NA unidade).
15121
+ -- A coordenada continua sendo a do endere\xE7o da unidade (142); n\xE3o h\xE1 segunda
15122
+ -- latitude aqui.
15123
+ --
15124
+ -- Chave que conecta: plg_agenda_turn_queues.unit_id = app.units.id
15125
+ -- (UNIQUE tenant_id, unit_id, queue_key). Idempotente.
15126
+
15127
+ CREATE OR REPLACE FUNCTION public.agenda_turn_queue_configure(
15128
+ p_unit_id uuid,
15129
+ p_enabled boolean,
15130
+ p_geofence_required boolean DEFAULT NULL,
15131
+ p_radius_meters integer DEFAULT NULL
15132
+ )
15133
+ RETURNS public.plg_agenda_turn_queues
15134
+ LANGUAGE plpgsql
15135
+ SECURITY DEFINER
15136
+ SET search_path TO ''
15137
+ AS $function$
15138
+ DECLARE
15139
+ v_tenant uuid := app.current_tenant_id();
15140
+ v_row public.plg_agenda_turn_queues;
15141
+ BEGIN
15142
+ IF v_tenant IS NULL OR auth.uid() IS NULL THEN
15143
+ RAISE EXCEPTION 'agenda_turn_queue_configure: no session or no active tenant' USING ERRCODE = '42501';
15144
+ END IF;
15145
+ IF NOT EXISTS (SELECT 1 FROM app.units u WHERE u.id = p_unit_id AND u.tenant_id = v_tenant) THEN
15146
+ RAISE EXCEPTION 'agenda_turn_queue_configure: unit does not belong to this tenant' USING ERRCODE = '42501';
15147
+ END IF;
15148
+ IF NOT app.has_unit(p_unit_id) OR NOT app.has_permission('settings.manage', p_unit_id) THEN
15149
+ RAISE EXCEPTION 'agenda_turn_queue_configure: settings.manage is required on this unit' USING ERRCODE = '42501';
15150
+ END IF;
15151
+ IF p_radius_meters IS NOT NULL AND (p_radius_meters < 10 OR p_radius_meters > 1000) THEN
15152
+ RAISE EXCEPTION 'agenda_turn_queue_configure: radius must be between 10 and 1000 meters' USING ERRCODE = '22023';
15153
+ END IF;
15154
+
15155
+ INSERT INTO public.plg_agenda_turn_queues AS q (tenant_id, unit_id, is_active, geofence_required, geofence_radius_meters)
15156
+ VALUES (v_tenant, p_unit_id, coalesce(p_enabled, false), coalesce(p_geofence_required, true), coalesce(p_radius_meters, 80))
15157
+ ON CONFLICT (tenant_id, unit_id, queue_key) DO UPDATE
15158
+ SET is_active = coalesce(p_enabled, q.is_active),
15159
+ geofence_required = coalesce(p_geofence_required, q.geofence_required),
15160
+ geofence_radius_meters = coalesce(p_radius_meters, q.geofence_radius_meters),
15161
+ updated_at = now()
15162
+ RETURNING * INTO v_row;
15163
+ RETURN v_row;
15164
+ END $function$;
15165
+
15166
+ REVOKE ALL ON FUNCTION public.agenda_turn_queue_configure(uuid, boolean, boolean, integer) FROM PUBLIC, anon;
15167
+ GRANT EXECUTE ON FUNCTION public.agenda_turn_queue_configure(uuid, boolean, boolean, integer) TO authenticated, service_role;
15168
+ `;
15106
15169
  var MIGRATIONS = [
15107
15170
  { id: "000_baseline", sql: MIGRATION_000_BASELINE },
15108
15171
  { id: "001_the_connector_definers_keep_a_pinned_path", sql: MIGRATION_001_THE_CONNECTOR_DEFINERS_KEEP_A_PINNED_PATH },
@@ -15123,7 +15186,8 @@ var MIGRATIONS = [
15123
15186
  { id: "016_a_vez_e_um_fato", sql: MIGRATION_016_A_VEZ_E_UM_FATO },
15124
15187
  { id: "017_a_equipe_do_dia_aparece", sql: MIGRATION_017_A_EQUIPE_DO_DIA_APARECE },
15125
15188
  { id: "018_o_agendamento_que_se_repete", sql: MIGRATION_018_O_AGENDAMENTO_QUE_SE_REPETE },
15126
- { id: "019_a_serie_nasce_numa_viagem_so", sql: MIGRATION_019_A_SERIE_NASCE_NUMA_VIAGEM_SO }
15189
+ { id: "019_a_serie_nasce_numa_viagem_so", sql: MIGRATION_019_A_SERIE_NASCE_NUMA_VIAGEM_SO },
15190
+ { id: "020_a_lista_da_vez_se_liga_no_cadastro_da_unidade", sql: MIGRATION_020_A_LISTA_DA_VEZ_SE_LIGA_NO_CADASTRO_DA_UNIDADE }
15127
15191
  ];
15128
15192
  var channelSequence = 0;
15129
15193
  function client() {
@@ -15606,6 +15670,103 @@ function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle })
15606
15670
  ] })
15607
15671
  ] }) });
15608
15672
  }
15673
+ function UnitTurnQueueWidget({ item }) {
15674
+ const unitId = item?.id;
15675
+ const [row, setRow] = React21.useState(null);
15676
+ const [loading, setLoading] = React21.useState(true);
15677
+ const [saving, setSaving] = React21.useState(false);
15678
+ const [radius, setRadius] = React21.useState("80");
15679
+ React21.useEffect(() => {
15680
+ if (!unitId) return;
15681
+ let alive = true;
15682
+ const supabase = getSupabaseClientOptional();
15683
+ void supabase?.from("plg_agenda_turn_queues").select("is_active, geofence_required, geofence_radius_meters").eq("unit_id", unitId).eq("queue_key", "walk_in").maybeSingle().then(({ data }) => {
15684
+ if (!alive) return;
15685
+ const next = data ?? { is_active: false, geofence_required: true, geofence_radius_meters: 80 };
15686
+ setRow(next);
15687
+ setRadius(String(next.geofence_radius_meters));
15688
+ setLoading(false);
15689
+ });
15690
+ return () => {
15691
+ alive = false;
15692
+ };
15693
+ }, [unitId]);
15694
+ const save = async (patch) => {
15695
+ if (!unitId || !row) return;
15696
+ const next = { ...row, ...patch };
15697
+ setSaving(true);
15698
+ const supabase = getSupabaseClientOptional();
15699
+ const { data, error } = await supabase.rpc("agenda_turn_queue_configure", {
15700
+ p_unit_id: unitId,
15701
+ p_enabled: next.is_active,
15702
+ p_geofence_required: next.geofence_required,
15703
+ p_radius_meters: next.geofence_radius_meters
15704
+ });
15705
+ setSaving(false);
15706
+ if (error) {
15707
+ toast.error("N\xE3o foi poss\xEDvel salvar a lista da vez", { description: error.message });
15708
+ return;
15709
+ }
15710
+ setRow({ is_active: data.is_active, geofence_required: data.geofence_required, geofence_radius_meters: data.geofence_radius_meters });
15711
+ toast.success("Lista da vez salva");
15712
+ };
15713
+ const radiusNumber = Number(radius);
15714
+ const radiusInvalid = !Number.isInteger(radiusNumber) || radiusNumber < 10 || radiusNumber > 1e3;
15715
+ const hasCoordinate = item?.latitude != null && item?.longitude != null;
15716
+ return /* @__PURE__ */ jsxs(Card, { children: [
15717
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
15718
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "Lista da vez" }),
15719
+ /* @__PURE__ */ jsx(CardDescription, { children: "Fila de atendimento por ordem de chegada. Desligada, o item some do menu e o bot\xE3o de disponibilidade some do topo." })
15720
+ ] }),
15721
+ /* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: loading || !row ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
15722
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }),
15723
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" })
15724
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
15725
+ /* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-4 rounded-md border border-input bg-background px-3 py-2.5", children: [
15726
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Ativar lista da vez nesta unidade" }),
15727
+ /* @__PURE__ */ jsx(Switch, { checked: row.is_active, disabled: saving, onCheckedChange: (v) => void save({ is_active: v }) })
15728
+ ] }),
15729
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
15730
+ /* @__PURE__ */ jsxs("div", { children: [
15731
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Localiza\xE7\xE3o da lista da vez" }),
15732
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Valida o bot\xE3o \u201Cestou dispon\xEDvel\u201D dos profissionais pela dist\xE2ncia at\xE9 a unidade." })
15733
+ ] }),
15734
+ /* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-4 rounded-md border border-input bg-background px-3 py-2.5", children: [
15735
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: "Exigir que o profissional esteja na unidade" }),
15736
+ /* @__PURE__ */ jsx(Switch, { checked: row.geofence_required, disabled: saving, onCheckedChange: (v) => void save({ geofence_required: v }) })
15737
+ ] }),
15738
+ /* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
15739
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
15740
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Coordenada" }),
15741
+ /* @__PURE__ */ jsx("p", { className: "flex h-10 items-center rounded-md border border-input bg-muted/40 px-3 text-sm tabular-nums text-muted-foreground", children: hasCoordinate ? `${Number(item.latitude).toFixed(6)}, ${Number(item.longitude).toFixed(6)}` : "Sem coordenada \u2014 preencha o endere\xE7o" }),
15742
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
15743
+ "Vem do endere\xE7o da unidade.",
15744
+ " ",
15745
+ /* @__PURE__ */ jsx("button", { type: "button", className: "underline decoration-dashed underline-offset-2", onClick: () => adminNavigateTo(`/settings/locations/${item?.id}/edit`), children: "Editar endere\xE7o" })
15746
+ ] })
15747
+ ] }),
15748
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
15749
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Raio (m)" }),
15750
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
15751
+ /* @__PURE__ */ jsx(Input, { type: "number", min: 10, max: 1e3, value: radius, onChange: (e) => setRadius(e.target.value), className: "tabular-nums" }),
15752
+ /* @__PURE__ */ jsx(
15753
+ Button,
15754
+ {
15755
+ variant: "outline",
15756
+ disabled: saving || radiusInvalid || radiusNumber === row.geofence_radius_meters,
15757
+ onClick: () => void save({ geofence_radius_meters: radiusNumber }),
15758
+ children: "Salvar"
15759
+ }
15760
+ )
15761
+ ] }),
15762
+ radiusInvalid && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: "Use um raio entre 10 e 1000 metros." })
15763
+ ] })
15764
+ ] }) }),
15765
+ row.geofence_required && !hasCoordinate && row.is_active && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: "Sem coordenada no endere\xE7o, ningu\xE9m consegue marcar \u201Cestou dispon\xEDvel\u201D nesta unidade." })
15766
+ ] })
15767
+ ] }) })
15768
+ ] });
15769
+ }
15609
15770
 
15610
15771
  // src/lib/turn-queue-plugin.ts
15611
15772
  function createAgendaTurnQueuePlugin(options = {}) {
@@ -15654,6 +15815,14 @@ function createAgendaTurnQueuePlugin(options = {}) {
15654
15815
  permission: { feature: "agenda", action: "read" },
15655
15816
  title
15656
15817
  }],
15818
+ // Lives on the add-on, not on agenda: the unit tab exists only while this
15819
+ // module is enabled in settings/modules.
15820
+ widgets: [{
15821
+ id: "agenda-unit-turn-queue",
15822
+ zone: UNIT_DETAIL_ZONES.turnQueue,
15823
+ component: UnitTurnQueueWidget,
15824
+ order: 0
15825
+ }],
15657
15826
  headerControls: [{
15658
15827
  id: "agenda-turn-queue.availability",
15659
15828
  component: HeaderControl,