@fayz-ai/plugin-agenda 0.8.0 → 0.8.3

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.
Files changed (117) hide show
  1. package/dist/{chunk-LFUELUIT.cjs → chunk-3KWNHV5A.cjs} +60 -2
  2. package/dist/chunk-3KWNHV5A.cjs.map +1 -0
  3. package/dist/{chunk-DRZF6CV2.js → chunk-YQO6B4G3.js} +60 -3
  4. package/dist/chunk-YQO6B4G3.js.map +1 -0
  5. package/dist/components/AgendaGeneralSettings.d.ts.map +1 -1
  6. package/dist/components/AppointmentModal.d.ts.map +1 -1
  7. package/dist/components/CalendarToolbar.d.ts.map +1 -1
  8. package/dist/data/supabase.d.ts +5 -1
  9. package/dist/data/supabase.d.ts.map +1 -1
  10. package/dist/index.cjs +1071 -766
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +914 -609
  14. package/dist/index.js.map +1 -1
  15. package/dist/integrations/google-calendar/components/CalendarColorDot.d.ts +6 -0
  16. package/dist/integrations/google-calendar/components/CalendarColorDot.d.ts.map +1 -0
  17. package/dist/integrations/google-calendar/components/ChannelMappingList.d.ts +9 -0
  18. package/dist/integrations/google-calendar/components/ChannelMappingList.d.ts.map +1 -0
  19. package/dist/integrations/google-calendar/components/ImportEventsWizard.d.ts +12 -0
  20. package/dist/integrations/google-calendar/components/ImportEventsWizard.d.ts.map +1 -0
  21. package/dist/integrations/google-calendar/components/useAgendaLookups.d.ts +26 -0
  22. package/dist/integrations/google-calendar/components/useAgendaLookups.d.ts.map +1 -0
  23. package/dist/integrations/google-calendar/connectorDef.d.ts.map +1 -1
  24. package/dist/integrations/google-calendar/data/supabase.d.ts +33 -1
  25. package/dist/integrations/google-calendar/data/supabase.d.ts.map +1 -1
  26. package/dist/integrations/google-calendar/data/tables.d.ts +6 -0
  27. package/dist/integrations/google-calendar/data/tables.d.ts.map +1 -0
  28. package/dist/integrations/google-calendar/index.d.ts +1 -1
  29. package/dist/integrations/google-calendar/index.d.ts.map +1 -1
  30. package/dist/integrations/google-calendar/mapping.d.ts +56 -4
  31. package/dist/integrations/google-calendar/mapping.d.ts.map +1 -1
  32. package/dist/integrations/google-calendar/types.d.ts +101 -0
  33. package/dist/integrations/google-calendar/types.d.ts.map +1 -1
  34. package/dist/locales/en.d.ts.map +1 -1
  35. package/dist/locales/pt-BR.d.ts.map +1 -1
  36. package/dist/migrations/index.d.ts +11 -0
  37. package/dist/migrations/index.d.ts.map +1 -1
  38. package/dist/public/BookingWidget.d.ts.map +1 -1
  39. package/dist/public/context.d.ts +3 -1
  40. package/dist/public/context.d.ts.map +1 -1
  41. package/dist/public/createPublicBookingPlugin.d.ts +7 -1
  42. package/dist/public/createPublicBookingPlugin.d.ts.map +1 -1
  43. package/dist/public/data.d.ts +6 -0
  44. package/dist/public/data.d.ts.map +1 -1
  45. package/dist/public/data.supabase.d.ts.map +1 -1
  46. package/dist/public/format.d.ts +22 -1
  47. package/dist/public/format.d.ts.map +1 -1
  48. package/dist/public/hooks.d.ts.map +1 -1
  49. package/dist/public/index.cjs +31 -63
  50. package/dist/public/index.cjs.map +1 -1
  51. package/dist/public/index.d.ts +1 -1
  52. package/dist/public/index.d.ts.map +1 -1
  53. package/dist/public/index.js +27 -61
  54. package/dist/public/index.js.map +1 -1
  55. package/dist/public/steps/ContactStep.d.ts +4 -2
  56. package/dist/public/steps/ContactStep.d.ts.map +1 -1
  57. package/dist/public/types.d.ts +16 -0
  58. package/dist/public/types.d.ts.map +1 -1
  59. package/dist/views/CalendarView.d.ts.map +1 -1
  60. package/package.json +6 -5
  61. package/src/components/AgendaGeneralSettings.tsx +38 -16
  62. package/src/components/AppointmentModal.tsx +50 -108
  63. package/src/components/CalendarToolbar.tsx +10 -7
  64. package/src/data/supabase.ts +67 -13
  65. package/src/index.ts +56 -8
  66. package/src/integrations/google-calendar/components/CalendarColorDot.tsx +23 -0
  67. package/src/integrations/google-calendar/components/ChannelMappingList.tsx +329 -0
  68. package/src/integrations/google-calendar/components/ImportEventsWizard.tsx +215 -0
  69. package/src/integrations/google-calendar/components/useAgendaLookups.ts +100 -0
  70. package/src/integrations/google-calendar/connectorDef.tsx +123 -40
  71. package/src/integrations/google-calendar/data/supabase.ts +109 -13
  72. package/src/integrations/google-calendar/data/tables.ts +12 -0
  73. package/src/integrations/google-calendar/index.ts +10 -1
  74. package/src/integrations/google-calendar/integration.test.ts +126 -1
  75. package/src/integrations/google-calendar/mapping.ts +149 -9
  76. package/src/integrations/google-calendar/migrations/001_google_calendar.sql +9 -6
  77. package/src/integrations/google-calendar/migrations/002_smart_sync.sql +517 -0
  78. package/src/integrations/google-calendar/migrations/003_plg_rename.sql +273 -0
  79. package/src/integrations/google-calendar/types.ts +111 -0
  80. package/src/locales/en.ts +1 -0
  81. package/src/locales/pt-BR.ts +1 -0
  82. package/src/migrations/003_public_client_kind.sql +175 -0
  83. package/src/migrations/004_public_booking_assignee.sql +176 -0
  84. package/src/migrations/005_agent_rpcs.sql +168 -0
  85. package/src/migrations/006_agent_rpc_localtime.sql +181 -0
  86. package/src/migrations/007_schedule_exception_precedence.sql +290 -0
  87. package/src/migrations/008_agent_rpc_exception_precedence.sql +172 -0
  88. package/src/migrations/009_public_booking_location.sql +162 -0
  89. package/src/migrations/010_public_service_curation.sql +31 -0
  90. package/src/migrations/011_default_assignee.sql +167 -0
  91. package/src/migrations/012_drop_legacy_booking_overload.sql +29 -0
  92. package/src/migrations/013_normalize_v_appointments.sql +161 -0
  93. package/src/migrations/index.ts +1746 -1
  94. package/src/public/BookingWidget.tsx +10 -1
  95. package/src/public/context.tsx +3 -0
  96. package/src/public/createPublicBookingPlugin.ts +14 -1
  97. package/src/public/data.supabase.ts +3 -0
  98. package/src/public/data.ts +6 -0
  99. package/src/public/format.ts +36 -1
  100. package/src/public/hooks.ts +7 -2
  101. package/src/public/index.ts +1 -0
  102. package/src/public/steps/ContactStep.tsx +10 -5
  103. package/src/public/types.ts +21 -0
  104. package/src/views/CalendarView.tsx +15 -1
  105. package/dist/chunk-3LJKZQK4.js +0 -12
  106. package/dist/chunk-3LJKZQK4.js.map +0 -1
  107. package/dist/chunk-DRZF6CV2.js.map +0 -1
  108. package/dist/chunk-LFUELUIT.cjs.map +0 -1
  109. package/dist/chunk-SYDBDI3Q.cjs +0 -15
  110. package/dist/chunk-SYDBDI3Q.cjs.map +0 -1
  111. package/dist/components/SearchCombobox.d.ts +0 -38
  112. package/dist/components/SearchCombobox.d.ts.map +0 -1
  113. package/dist/tenant-222EHYUC.cjs +0 -16
  114. package/dist/tenant-222EHYUC.cjs.map +0 -1
  115. package/dist/tenant-LAUWVUYB.js +0 -3
  116. package/dist/tenant-LAUWVUYB.js.map +0 -1
  117. package/src/components/SearchCombobox.tsx +0 -216
@@ -1,216 +0,0 @@
1
- import React, { useState, useEffect, useRef, useCallback } from 'react'
2
- import { createPortal } from 'react-dom'
3
- import { Search, Plus } from 'lucide-react'
4
-
5
- // ---------------------------------------------------------------------------
6
- // Reusable search combobox with keyboard navigation + optional create
7
- // ---------------------------------------------------------------------------
8
-
9
- export interface ComboboxOption {
10
- id: string
11
- label: string
12
- subtitle?: string
13
- price?: number
14
- data?: Record<string, unknown>
15
- }
16
-
17
- interface SearchComboboxProps {
18
- value: string
19
- onChange: (value: string) => void
20
- onSelect: (option: ComboboxOption) => void
21
- options: ComboboxOption[]
22
- placeholder?: string
23
- autoFocus?: boolean
24
- onBlurEmpty?: () => void
25
- renderRight?: (option: ComboboxOption) => React.ReactNode
26
- /** Show a "+ Create ..." option when search has no exact match */
27
- allowCreate?: boolean
28
- /** Called when user clicks the create option. Receives the current search text. */
29
- onCreateNew?: (searchText: string) => void
30
- /** Label for the create option (default: 'Create') */
31
- createLabel?: string
32
- className?: string
33
- /** Minimal style — no border/bg, just underline on focus. For inline use. */
34
- minimal?: boolean
35
- /** Inline label shown when collapsed. Click reveals the search input. */
36
- inlineLabel?: string
37
- /** Icon for the inline label: 'search' (default) or 'plus' (for "add" actions) */
38
- inlineIcon?: 'search' | 'plus'
39
- /** Show a loading skeleton in the dropdown while results are being fetched */
40
- loading?: boolean
41
- /** Called when the input gains focus */
42
- onFocus?: () => void
43
- }
44
-
45
- export function SearchCombobox({
46
- value, onChange, onSelect, options, placeholder, autoFocus, onBlurEmpty,
47
- renderRight, allowCreate, onCreateNew, createLabel = 'Create', className, minimal, inlineLabel, inlineIcon = 'search',
48
- loading, onFocus: onFocusProp,
49
- }: SearchComboboxProps) {
50
- const [open, setOpen] = useState(false)
51
- const [activeIndex, setActiveIndex] = useState(-1)
52
- const [expanded, setExpanded] = useState(!inlineLabel)
53
- const [dropRect, setDropRect] = useState<DOMRect | null>(null)
54
- const inputRef = useRef<HTMLInputElement>(null)
55
- const wrapperRef = useRef<HTMLDivElement>(null)
56
- const listRef = useRef<HTMLDivElement>(null)
57
-
58
- // When value is set externally (e.g. edit mode), auto-expand; collapse when cleared
59
- useEffect(() => {
60
- if (value && inlineLabel) setExpanded(true)
61
- if (!value && inlineLabel && !open) setExpanded(false)
62
- }, [value, open])
63
-
64
- function updateRect() {
65
- if (wrapperRef.current) setDropRect(wrapperRef.current.getBoundingClientRect())
66
- }
67
-
68
- // Show create option only when dropdown is open, has text, and no exact match
69
- const showCreate = open && allowCreate && onCreateNew && value.trim().length > 0 &&
70
- !options.some((o) => o.label.toLowerCase() === value.trim().toLowerCase())
71
-
72
- const totalItems = options.length + (showCreate ? 1 : 0)
73
-
74
- useEffect(() => { setActiveIndex(-1) }, [options])
75
- useEffect(() => {
76
- if ((options.length > 0 || showCreate || loading) && document.activeElement === inputRef.current) setOpen(true)
77
- }, [options, showCreate, loading])
78
-
79
- const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
80
- if (!open || totalItems === 0) {
81
- if (e.key === 'ArrowDown' && totalItems > 0) {
82
- e.preventDefault()
83
- setOpen(true)
84
- setActiveIndex(0)
85
- }
86
- return
87
- }
88
-
89
- switch (e.key) {
90
- case 'ArrowDown':
91
- e.preventDefault()
92
- setActiveIndex((prev) => (prev + 1) % totalItems)
93
- break
94
- case 'ArrowUp':
95
- e.preventDefault()
96
- setActiveIndex((prev) => (prev <= 0 ? totalItems - 1 : prev - 1))
97
- break
98
- case 'Enter':
99
- e.preventDefault()
100
- if (activeIndex >= 0 && activeIndex < options.length) {
101
- onSelect(options[activeIndex])
102
- setOpen(false)
103
- setActiveIndex(-1)
104
- } else if (showCreate && activeIndex === options.length) {
105
- onCreateNew?.(value.trim())
106
- setOpen(false)
107
- setActiveIndex(-1)
108
- }
109
- break
110
- case 'Escape':
111
- e.preventDefault()
112
- setOpen(false)
113
- setActiveIndex(-1)
114
- break
115
- }
116
- }, [open, options, totalItems, activeIndex, onSelect, showCreate, onCreateNew, value])
117
-
118
- useEffect(() => {
119
- if (activeIndex < 0 || !listRef.current) return
120
- const items = listRef.current.children
121
- if (items[activeIndex]) (items[activeIndex] as HTMLElement).scrollIntoView({ block: 'nearest' })
122
- }, [activeIndex])
123
-
124
- function handleBlur() {
125
- setTimeout(() => {
126
- setOpen(false)
127
- setActiveIndex(-1)
128
- if (!value && onBlurEmpty) onBlurEmpty()
129
- if (!value && inlineLabel) setExpanded(false)
130
- }, 200)
131
- }
132
-
133
- const hasDropdown = open && (options.length > 0 || showCreate || loading)
134
-
135
- // Collapsed inline label — click to expand
136
- if (!expanded && inlineLabel) {
137
- const isPlus = inlineIcon === 'plus'
138
- return (
139
- <button type="button" onClick={() => { setExpanded(true); setTimeout(() => inputRef.current?.focus(), 50) }}
140
- className={`flex items-center gap-1.5 w-full text-left text-sm py-1.5 pl-2 transition-colors ${
141
- isPlus ? 'text-primary hover:text-primary/80' : 'text-muted-foreground hover:text-foreground'
142
- } ${className ?? ''}`}>
143
- {isPlus && <Plus className="h-3.5 w-3.5 shrink-0" />}
144
- {inlineLabel}
145
- </button>
146
- )
147
- }
148
-
149
- return (
150
- <div ref={wrapperRef} className={`relative ${className ?? ''}`}>
151
- {!minimal && (
152
- <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground pointer-events-none" />
153
- )}
154
- <input
155
- ref={inputRef}
156
- type="text"
157
- value={value}
158
- onChange={(e) => { onChange(e.target.value); updateRect(); setOpen(true) }}
159
- onFocus={() => { updateRect(); onFocusProp?.(); if (options.length > 0 || showCreate || loading) setOpen(true) }}
160
- onBlur={handleBlur}
161
- onKeyDown={handleKeyDown}
162
- placeholder={placeholder}
163
- autoFocus={autoFocus}
164
- className={minimal
165
- ? 'w-full bg-transparent pl-2 pr-3 py-1.5 text-sm placeholder:text-muted-foreground focus:outline-none border-b border-transparent hover:border-border focus:border-foreground transition-colors'
166
- : 'w-full rounded-lg border bg-background pl-8 pr-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring'
167
- }
168
- role="combobox"
169
- aria-expanded={hasDropdown}
170
- aria-autocomplete="list"
171
- aria-activedescendant={activeIndex >= 0 ? `combobox-opt-${activeIndex}` : undefined}
172
- />
173
- {hasDropdown && dropRect && createPortal(
174
- <div ref={listRef} role="listbox" data-modal-passthrough
175
- style={{ position: 'fixed', left: dropRect.left, top: dropRect.bottom + 4, width: dropRect.width, zIndex: 9999, pointerEvents: 'auto' }}
176
- className="fayz-glass-surface max-h-52 overflow-y-auto rounded-lg border bg-popover shadow-lg py-0.5 animate-in fade-in zoom-in-95 duration-100">
177
- {loading && options.length === 0 && (
178
- <div className="flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground">
179
- <svg className="h-3.5 w-3.5 animate-spin shrink-0" viewBox="0 0 16 16" fill="none">
180
- <circle cx="8" cy="8" r="6.5" stroke="currentColor" strokeWidth="1.5" opacity="0.25" />
181
- <path d="M14.5 8a6.5 6.5 0 00-6.5-6.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
182
- </svg>
183
- <span>Buscando…</span>
184
- </div>
185
- )}
186
- {options.map((opt, i) => (
187
- <div key={opt.id} id={`combobox-opt-${i}`} role="option" aria-selected={i === activeIndex}
188
- onMouseDown={(e) => { e.preventDefault(); onSelect(opt); setOpen(false) }}
189
- onMouseEnter={() => setActiveIndex(i)}
190
- className={`flex w-full items-center gap-2 px-3 py-2 text-sm cursor-pointer transition-colors ${
191
- i === activeIndex ? 'bg-primary/10 text-primary' : 'hover:bg-muted/50'
192
- }`}>
193
- <div className="flex-1 min-w-0">
194
- <p className="truncate font-medium">{opt.label}</p>
195
- {opt.subtitle && <p className="truncate text-[11px] text-muted-foreground">{opt.subtitle}</p>}
196
- </div>
197
- {renderRight && <div className="shrink-0">{renderRight(opt)}</div>}
198
- </div>
199
- ))}
200
- {showCreate && (
201
- <div id={`combobox-opt-${options.length}`} role="option" aria-selected={activeIndex === options.length}
202
- onMouseDown={(e) => { e.preventDefault(); onCreateNew?.(value.trim()); setOpen(false) }}
203
- onMouseEnter={() => setActiveIndex(options.length)}
204
- className={`flex w-full items-center gap-2 px-3 py-2 text-sm cursor-pointer transition-colors border-t ${
205
- activeIndex === options.length ? 'bg-primary/10 text-primary' : 'hover:bg-muted/50'
206
- }`}>
207
- <Plus className="h-3.5 w-3.5 shrink-0" />
208
- <span>{createLabel} &ldquo;<span className="font-medium">{value.trim()}</span>&rdquo;</span>
209
- </div>
210
- )}
211
- </div>,
212
- document.body,
213
- )}
214
- </div>
215
- )
216
- }