@fayz-ai/plugin-agenda 0.8.1 → 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.
- package/dist/{chunk-LFUELUIT.cjs → chunk-3KWNHV5A.cjs} +60 -2
- package/dist/chunk-3KWNHV5A.cjs.map +1 -0
- package/dist/{chunk-DRZF6CV2.js → chunk-YQO6B4G3.js} +60 -3
- package/dist/chunk-YQO6B4G3.js.map +1 -0
- package/dist/components/AgendaGeneralSettings.d.ts.map +1 -1
- package/dist/components/AppointmentModal.d.ts.map +1 -1
- package/dist/components/CalendarToolbar.d.ts.map +1 -1
- package/dist/data/supabase.d.ts +5 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/index.cjs +1071 -766
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +914 -609
- package/dist/index.js.map +1 -1
- package/dist/integrations/google-calendar/components/CalendarColorDot.d.ts +6 -0
- package/dist/integrations/google-calendar/components/CalendarColorDot.d.ts.map +1 -0
- package/dist/integrations/google-calendar/components/ChannelMappingList.d.ts +9 -0
- package/dist/integrations/google-calendar/components/ChannelMappingList.d.ts.map +1 -0
- package/dist/integrations/google-calendar/components/ImportEventsWizard.d.ts +12 -0
- package/dist/integrations/google-calendar/components/ImportEventsWizard.d.ts.map +1 -0
- package/dist/integrations/google-calendar/components/useAgendaLookups.d.ts +26 -0
- package/dist/integrations/google-calendar/components/useAgendaLookups.d.ts.map +1 -0
- package/dist/integrations/google-calendar/connectorDef.d.ts.map +1 -1
- package/dist/integrations/google-calendar/data/supabase.d.ts +33 -1
- package/dist/integrations/google-calendar/data/supabase.d.ts.map +1 -1
- package/dist/integrations/google-calendar/data/tables.d.ts +6 -0
- package/dist/integrations/google-calendar/data/tables.d.ts.map +1 -0
- package/dist/integrations/google-calendar/index.d.ts +1 -1
- package/dist/integrations/google-calendar/index.d.ts.map +1 -1
- package/dist/integrations/google-calendar/mapping.d.ts +56 -4
- package/dist/integrations/google-calendar/mapping.d.ts.map +1 -1
- package/dist/integrations/google-calendar/types.d.ts +101 -0
- package/dist/integrations/google-calendar/types.d.ts.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +11 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/public/BookingWidget.d.ts.map +1 -1
- package/dist/public/context.d.ts +3 -1
- package/dist/public/context.d.ts.map +1 -1
- package/dist/public/createPublicBookingPlugin.d.ts +7 -1
- package/dist/public/createPublicBookingPlugin.d.ts.map +1 -1
- package/dist/public/data.d.ts +6 -0
- package/dist/public/data.d.ts.map +1 -1
- package/dist/public/data.supabase.d.ts.map +1 -1
- package/dist/public/format.d.ts +22 -1
- package/dist/public/format.d.ts.map +1 -1
- package/dist/public/hooks.d.ts.map +1 -1
- package/dist/public/index.cjs +31 -63
- package/dist/public/index.cjs.map +1 -1
- package/dist/public/index.d.ts +1 -1
- package/dist/public/index.d.ts.map +1 -1
- package/dist/public/index.js +27 -61
- package/dist/public/index.js.map +1 -1
- package/dist/public/steps/ContactStep.d.ts +4 -2
- package/dist/public/steps/ContactStep.d.ts.map +1 -1
- package/dist/public/types.d.ts +16 -0
- package/dist/public/types.d.ts.map +1 -1
- package/dist/views/CalendarView.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/components/AgendaGeneralSettings.tsx +38 -16
- package/src/components/AppointmentModal.tsx +50 -108
- package/src/components/CalendarToolbar.tsx +10 -7
- package/src/data/supabase.ts +67 -13
- package/src/index.ts +56 -8
- package/src/integrations/google-calendar/components/CalendarColorDot.tsx +23 -0
- package/src/integrations/google-calendar/components/ChannelMappingList.tsx +329 -0
- package/src/integrations/google-calendar/components/ImportEventsWizard.tsx +215 -0
- package/src/integrations/google-calendar/components/useAgendaLookups.ts +100 -0
- package/src/integrations/google-calendar/connectorDef.tsx +123 -40
- package/src/integrations/google-calendar/data/supabase.ts +109 -13
- package/src/integrations/google-calendar/data/tables.ts +12 -0
- package/src/integrations/google-calendar/index.ts +10 -1
- package/src/integrations/google-calendar/integration.test.ts +126 -1
- package/src/integrations/google-calendar/mapping.ts +149 -9
- package/src/integrations/google-calendar/migrations/001_google_calendar.sql +9 -6
- package/src/integrations/google-calendar/migrations/002_smart_sync.sql +517 -0
- package/src/integrations/google-calendar/migrations/003_plg_rename.sql +273 -0
- package/src/integrations/google-calendar/types.ts +111 -0
- package/src/locales/en.ts +1 -0
- package/src/locales/pt-BR.ts +1 -0
- package/src/migrations/003_public_client_kind.sql +175 -0
- package/src/migrations/004_public_booking_assignee.sql +176 -0
- package/src/migrations/005_agent_rpcs.sql +168 -0
- package/src/migrations/006_agent_rpc_localtime.sql +181 -0
- package/src/migrations/007_schedule_exception_precedence.sql +290 -0
- package/src/migrations/008_agent_rpc_exception_precedence.sql +172 -0
- package/src/migrations/009_public_booking_location.sql +162 -0
- package/src/migrations/010_public_service_curation.sql +31 -0
- package/src/migrations/011_default_assignee.sql +167 -0
- package/src/migrations/012_drop_legacy_booking_overload.sql +29 -0
- package/src/migrations/013_normalize_v_appointments.sql +161 -0
- package/src/migrations/index.ts +1746 -1
- package/src/public/BookingWidget.tsx +10 -1
- package/src/public/context.tsx +3 -0
- package/src/public/createPublicBookingPlugin.ts +14 -1
- package/src/public/data.supabase.ts +3 -0
- package/src/public/data.ts +6 -0
- package/src/public/format.ts +36 -1
- package/src/public/hooks.ts +7 -2
- package/src/public/index.ts +1 -0
- package/src/public/steps/ContactStep.tsx +10 -5
- package/src/public/types.ts +21 -0
- package/src/views/CalendarView.tsx +15 -1
- package/dist/chunk-3LJKZQK4.js +0 -12
- package/dist/chunk-3LJKZQK4.js.map +0 -1
- package/dist/chunk-DRZF6CV2.js.map +0 -1
- package/dist/chunk-LFUELUIT.cjs.map +0 -1
- package/dist/chunk-SYDBDI3Q.cjs +0 -15
- package/dist/chunk-SYDBDI3Q.cjs.map +0 -1
- package/dist/components/SearchCombobox.d.ts +0 -38
- package/dist/components/SearchCombobox.d.ts.map +0 -1
- package/dist/tenant-222EHYUC.cjs +0 -16
- package/dist/tenant-222EHYUC.cjs.map +0 -1
- package/dist/tenant-LAUWVUYB.js +0 -3
- package/dist/tenant-LAUWVUYB.js.map +0 -1
- package/src/components/SearchCombobox.tsx +0 -216
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CountryDef } from '@fayz-ai/core';
|
|
2
|
-
import type { PublicBookingLabels, ResolvedPayment } from '../types';
|
|
2
|
+
import type { PhoneVerificationMode, PublicBookingLabels, ResolvedPayment } from '../types';
|
|
3
3
|
export interface ContactDraft {
|
|
4
4
|
name: string;
|
|
5
5
|
phone: string;
|
|
@@ -11,6 +11,8 @@ interface ContactStepProps {
|
|
|
11
11
|
contact: ContactDraft;
|
|
12
12
|
onContactChange: (contact: ContactDraft) => void;
|
|
13
13
|
contactStep: 'phone' | 'code' | 'details';
|
|
14
|
+
/** 'none' → no code is sent, so the copy drops every OTP promise. */
|
|
15
|
+
phoneVerification: PhoneVerificationMode;
|
|
14
16
|
country: CountryDef;
|
|
15
17
|
onCountryChange: (iso2: string) => void;
|
|
16
18
|
code: string;
|
|
@@ -25,6 +27,6 @@ interface ContactStepProps {
|
|
|
25
27
|
error: Error | null;
|
|
26
28
|
}
|
|
27
29
|
/** Contact step — phone verification first (Quaddro-style), then details. */
|
|
28
|
-
export declare function ContactStep({ labels, contact, onContactChange, contactStep, country, onCountryChange, code, onCodeChange, codeError, onSendCode, onConfirmCode, onBackToPhone, onSubmitDetails, payment, submitting, error, }: ContactStepProps): import("react").JSX.Element;
|
|
30
|
+
export declare function ContactStep({ labels, contact, onContactChange, contactStep, phoneVerification, country, onCountryChange, code, onCodeChange, codeError, onSendCode, onConfirmCode, onBackToPhone, onSubmitDetails, payment, submitting, error, }: ContactStepProps): import("react").JSX.Element;
|
|
29
31
|
export {};
|
|
30
32
|
//# sourceMappingURL=ContactStep.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContactStep.d.ts","sourceRoot":"","sources":["../../../src/public/steps/ContactStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAClF,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"ContactStep.d.ts","sourceRoot":"","sources":["../../../src/public/steps/ContactStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAClF,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE3F,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,mBAAmB,CAAA;IAC3B,OAAO,EAAE,YAAY,CAAA;IACrB,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAA;IAChD,WAAW,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;IACzC,qEAAqE;IACrE,iBAAiB,EAAE,qBAAqB,CAAA;IACxC,OAAO,EAAE,UAAU,CAAA;IACnB,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,aAAa,EAAE,MAAM,IAAI,CAAA;IACzB,aAAa,EAAE,MAAM,IAAI,CAAA;IACzB,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,OAAO,EAAE,eAAe,GAAG,IAAI,CAAA;IAC/B,UAAU,EAAE,OAAO,CAAA;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,EAC1F,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EACvE,eAAe,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,GAC5C,EAAE,gBAAgB,+BAkJlB"}
|
package/dist/public/types.d.ts
CHANGED
|
@@ -40,6 +40,18 @@ export interface ResolvedPayment {
|
|
|
40
40
|
reservationFeePercent: number;
|
|
41
41
|
method: 'pix';
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* How the customer's phone number is verified before the details sub-step.
|
|
45
|
+
*
|
|
46
|
+
* • 'otp' — show the code modal (POC: the code is the literal '0000'; no
|
|
47
|
+
* message is actually sent). Use only where a real sender exists.
|
|
48
|
+
* • 'none' — trust the typed number: the flow goes straight to the details
|
|
49
|
+
* sub-step and the identity is emitted as-is.
|
|
50
|
+
*
|
|
51
|
+
* Default is 'none': until a real WhatsApp sender is wired, an OTP nobody can
|
|
52
|
+
* receive is a dead end for real customers.
|
|
53
|
+
*/
|
|
54
|
+
export type PhoneVerificationMode = 'otp' | 'none';
|
|
43
55
|
/** One icon+text bullet on the overview sidebar. */
|
|
44
56
|
export interface BookingHighlight {
|
|
45
57
|
icon?: ComponentType<{
|
|
@@ -157,6 +169,10 @@ export interface PublicBookingLabels {
|
|
|
157
169
|
phoneLabel: string;
|
|
158
170
|
phoneHint: string;
|
|
159
171
|
sendCodeCta: string;
|
|
172
|
+
/** Phone hint shown when phoneVerification is 'none' (no code is sent). */
|
|
173
|
+
phoneHintNoVerification: string;
|
|
174
|
+
/** Phone CTA shown when phoneVerification is 'none'. */
|
|
175
|
+
phoneContinueCta: string;
|
|
160
176
|
codeTitle: string;
|
|
161
177
|
codeBody: string;
|
|
162
178
|
codeInvalid: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/public/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/public/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CalendarView.d.ts","sourceRoot":"","sources":["../../src/views/CalendarView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAwRhF,wBAAgB,YAAY,
|
|
1
|
+
{"version":3,"file":"CalendarView.d.ts","sourceRoot":"","sources":["../../src/views/CalendarView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAwRhF,wBAAgB,YAAY,sBAmyB3B"}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"fayz": {
|
|
4
4
|
"status": "beta"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.8.
|
|
6
|
+
"version": "0.8.3",
|
|
7
7
|
"description": "Fayz SDK — plugin-agenda plugin",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "./dist/index.cjs",
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
"@tanstack/react-table": "^8.20.0",
|
|
44
44
|
"@fullcalendar/resource-timegrid": "^6.1.0",
|
|
45
45
|
"@fullcalendar/list": "^6.1.0",
|
|
46
|
-
"@fayz-ai/core": "^0.8.
|
|
47
|
-
"@fayz-ai/ui": "^0.8.
|
|
48
|
-
"@fayz-ai/saas": "^0.8.
|
|
49
|
-
"@fayz-ai/plugin-financial": "^0.8.
|
|
46
|
+
"@fayz-ai/core": "^0.8.3",
|
|
47
|
+
"@fayz-ai/ui": "^0.8.3",
|
|
48
|
+
"@fayz-ai/saas": "^0.8.3",
|
|
49
|
+
"@fayz-ai/plugin-financial": "^0.8.3"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/react": "^18.3.0",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { SettingsGroup, ToggleRow, SelectRow } from '@fayz-ai/saas'
|
|
2
|
+
import { SettingsGroup, ToggleRow, SelectRow, useTenantPluginSettings } from '@fayz-ai/saas'
|
|
3
3
|
import { useTranslation } from '@fayz-ai/core'
|
|
4
4
|
import { useAgendaSettings } from '../hooks/useAgendaSettings'
|
|
5
5
|
|
|
@@ -8,30 +8,52 @@ const HOUR_OPTIONS = Array.from({ length: 18 }, (_, i) => {
|
|
|
8
8
|
return { value: `${h}:00`, label: `${h}:00` }
|
|
9
9
|
})
|
|
10
10
|
|
|
11
|
+
// Per-tenant toggle defaults = the values that used to be hardcoded here.
|
|
12
|
+
// NOTE: these are persisted + re-hydrated per tenant; actually consuming them to
|
|
13
|
+
// change agenda behaviour is a follow-up (see useTenantPluginSettings docs).
|
|
14
|
+
const AGENDA_TOGGLE_DEFAULTS = {
|
|
15
|
+
conflictDetection: true,
|
|
16
|
+
dragDrop: true,
|
|
17
|
+
autoCreateOrder: true,
|
|
18
|
+
enableLocation: false,
|
|
19
|
+
enableWorkingHours: true,
|
|
20
|
+
blockOutside: false,
|
|
21
|
+
enableConfirmations: true,
|
|
22
|
+
autoConfirm: false,
|
|
23
|
+
reminder: true,
|
|
24
|
+
showCancelled: false,
|
|
25
|
+
showNoShow: true,
|
|
26
|
+
compact: false,
|
|
27
|
+
newBookingNotif: true,
|
|
28
|
+
cancelAlerts: true,
|
|
29
|
+
noShowTracking: false,
|
|
30
|
+
}
|
|
31
|
+
|
|
11
32
|
export function AgendaGeneralSettings() {
|
|
12
33
|
const t = useTranslation()
|
|
13
34
|
const { startTime, endTime, setStartTime, setEndTime } = useAgendaSettings()
|
|
35
|
+
const s = useTenantPluginSettings('agenda', AGENDA_TOGGLE_DEFAULTS)
|
|
14
36
|
return (
|
|
15
37
|
<div className="space-y-4">
|
|
16
38
|
<SettingsGroup title={t('agenda.settings.scheduling')} description={t('agenda.settings.schedulingDesc')}>
|
|
17
|
-
<ToggleRow label={t('agenda.settings.conflictDetection')} description={t('agenda.settings.conflictDetectionDesc')} checked={
|
|
18
|
-
<ToggleRow label={t('agenda.settings.dragDrop')} description={t('agenda.settings.dragDropDesc')} checked={
|
|
19
|
-
<ToggleRow label={t('agenda.settings.autoCreateOrder')} description={t('agenda.settings.autoCreateOrderDesc')} checked={
|
|
39
|
+
<ToggleRow label={t('agenda.settings.conflictDetection')} description={t('agenda.settings.conflictDetectionDesc')} checked={s.get('conflictDetection')} onChange={(v) => s.set('conflictDetection', v)} />
|
|
40
|
+
<ToggleRow label={t('agenda.settings.dragDrop')} description={t('agenda.settings.dragDropDesc')} checked={s.get('dragDrop')} onChange={(v) => s.set('dragDrop', v)} />
|
|
41
|
+
<ToggleRow label={t('agenda.settings.autoCreateOrder')} description={t('agenda.settings.autoCreateOrderDesc')} checked={s.get('autoCreateOrder')} onChange={(v) => s.set('autoCreateOrder', v)} />
|
|
20
42
|
</SettingsGroup>
|
|
21
43
|
|
|
22
44
|
<SettingsGroup title={t('agenda.settings.locations')} description={t('agenda.settings.locationsDesc')}>
|
|
23
|
-
<ToggleRow label={t('agenda.settings.enableLocation')} description={t('agenda.settings.enableLocationDesc')} checked={
|
|
45
|
+
<ToggleRow label={t('agenda.settings.enableLocation')} description={t('agenda.settings.enableLocationDesc')} checked={s.get('enableLocation')} onChange={(v) => s.set('enableLocation', v)} />
|
|
24
46
|
</SettingsGroup>
|
|
25
47
|
|
|
26
48
|
<SettingsGroup title={t('agenda.settings.workingHours')} description={t('agenda.settings.workingHoursDesc')}>
|
|
27
|
-
<ToggleRow label={t('agenda.settings.enableWorkingHours')} description={t('agenda.settings.enableWorkingHoursDesc')} checked={
|
|
28
|
-
<ToggleRow label={t('agenda.settings.blockOutside')} description={t('agenda.settings.blockOutsideDesc')} checked={
|
|
49
|
+
<ToggleRow label={t('agenda.settings.enableWorkingHours')} description={t('agenda.settings.enableWorkingHoursDesc')} checked={s.get('enableWorkingHours')} onChange={(v) => s.set('enableWorkingHours', v)} />
|
|
50
|
+
<ToggleRow label={t('agenda.settings.blockOutside')} description={t('agenda.settings.blockOutsideDesc')} checked={s.get('blockOutside')} onChange={(v) => s.set('blockOutside', v)} />
|
|
29
51
|
</SettingsGroup>
|
|
30
52
|
|
|
31
53
|
<SettingsGroup title={t('agenda.settings.confirmations')} description={t('agenda.settings.confirmationsDesc')}>
|
|
32
|
-
<ToggleRow label={t('agenda.settings.enableConfirmations')} description={t('agenda.settings.enableConfirmationsDesc')} checked={
|
|
33
|
-
<ToggleRow label={t('agenda.settings.autoConfirm')} description={t('agenda.settings.autoConfirmDesc')} checked={
|
|
34
|
-
<ToggleRow label={t('agenda.settings.reminder')} description={t('agenda.settings.reminderDesc')} checked={
|
|
54
|
+
<ToggleRow label={t('agenda.settings.enableConfirmations')} description={t('agenda.settings.enableConfirmationsDesc')} checked={s.get('enableConfirmations')} onChange={(v) => s.set('enableConfirmations', v)} />
|
|
55
|
+
<ToggleRow label={t('agenda.settings.autoConfirm')} description={t('agenda.settings.autoConfirmDesc')} checked={s.get('autoConfirm')} onChange={(v) => s.set('autoConfirm', v)} />
|
|
56
|
+
<ToggleRow label={t('agenda.settings.reminder')} description={t('agenda.settings.reminderDesc')} checked={s.get('reminder')} onChange={(v) => s.set('reminder', v)} />
|
|
35
57
|
</SettingsGroup>
|
|
36
58
|
|
|
37
59
|
<SettingsGroup title={t('agenda.settings.calendarDisplay')} description={t('agenda.settings.calendarDisplayDesc')}>
|
|
@@ -49,15 +71,15 @@ export function AgendaGeneralSettings() {
|
|
|
49
71
|
options={HOUR_OPTIONS}
|
|
50
72
|
onChange={setEndTime}
|
|
51
73
|
/>
|
|
52
|
-
<ToggleRow label={t('agenda.settings.showCancelled')} description={t('agenda.settings.showCancelledDesc')} checked={
|
|
53
|
-
<ToggleRow label={t('agenda.settings.showNoShow')} description={t('agenda.settings.showNoShowDesc')} checked={
|
|
54
|
-
<ToggleRow label={t('agenda.settings.compact')} description={t('agenda.settings.compactDesc')} checked={
|
|
74
|
+
<ToggleRow label={t('agenda.settings.showCancelled')} description={t('agenda.settings.showCancelledDesc')} checked={s.get('showCancelled')} onChange={(v) => s.set('showCancelled', v)} />
|
|
75
|
+
<ToggleRow label={t('agenda.settings.showNoShow')} description={t('agenda.settings.showNoShowDesc')} checked={s.get('showNoShow')} onChange={(v) => s.set('showNoShow', v)} />
|
|
76
|
+
<ToggleRow label={t('agenda.settings.compact')} description={t('agenda.settings.compactDesc')} checked={s.get('compact')} onChange={(v) => s.set('compact', v)} />
|
|
55
77
|
</SettingsGroup>
|
|
56
78
|
|
|
57
79
|
<SettingsGroup title={t('agenda.settings.notifications')} description={t('agenda.settings.notificationsDesc')}>
|
|
58
|
-
<ToggleRow label={t('agenda.settings.newBookingNotif')} description={t('agenda.settings.newBookingNotifDesc')} checked={
|
|
59
|
-
<ToggleRow label={t('agenda.settings.cancelAlerts')} description={t('agenda.settings.cancelAlertsDesc')} checked={
|
|
60
|
-
<ToggleRow label={t('agenda.settings.noShowTracking')} description={t('agenda.settings.noShowTrackingDesc')} checked={
|
|
80
|
+
<ToggleRow label={t('agenda.settings.newBookingNotif')} description={t('agenda.settings.newBookingNotifDesc')} checked={s.get('newBookingNotif')} onChange={(v) => s.set('newBookingNotif', v)} />
|
|
81
|
+
<ToggleRow label={t('agenda.settings.cancelAlerts')} description={t('agenda.settings.cancelAlertsDesc')} checked={s.get('cancelAlerts')} onChange={(v) => s.set('cancelAlerts', v)} />
|
|
82
|
+
<ToggleRow label={t('agenda.settings.noShowTracking')} description={t('agenda.settings.noShowTrackingDesc')} checked={s.get('noShowTracking')} onChange={(v) => s.set('noShowTracking', v)} />
|
|
61
83
|
</SettingsGroup>
|
|
62
84
|
</div>
|
|
63
85
|
)
|
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
} from '@fayz-ai/ui'
|
|
11
11
|
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@fayz-ai/ui'
|
|
12
12
|
import { cn } from '@fayz-ai/ui'
|
|
13
|
-
import { SearchCombobox, type ComboboxOption } from '
|
|
14
|
-
import {
|
|
13
|
+
import { SearchCombobox, type ComboboxOption } from '@fayz-ai/ui'
|
|
14
|
+
import { ContactPicker, type ContactPickerValue } from '@fayz-ai/saas'
|
|
15
15
|
import { BookingPaymentPanel } from './BookingPaymentPanel'
|
|
16
16
|
import { CrudDetailPage } from '@fayz-ai/saas'
|
|
17
17
|
import { CrudFormPage } from '@fayz-ai/saas'
|
|
18
|
+
import { useLimitGuard, invalidateLimit } from '@fayz-ai/saas'
|
|
18
19
|
import { Button } from '@fayz-ai/ui'
|
|
19
20
|
import { useAgendaConfig, useAgendaProvider, useAgendaStore } from '../AgendaContext'
|
|
20
21
|
import { useTranslation } from '@fayz-ai/core'
|
|
@@ -280,6 +281,7 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, e
|
|
|
280
281
|
const provider = useAgendaProvider()
|
|
281
282
|
const initialSnapshot = useRef<string>('')
|
|
282
283
|
const createBooking = useAgendaStore((s) => s.createBooking)
|
|
284
|
+
const guardBookings = useLimitGuard('bookings_month')
|
|
283
285
|
const updateBooking = useAgendaStore((s) => s.updateBooking)
|
|
284
286
|
const deleteBooking = useAgendaStore((s) => s.deleteBooking)
|
|
285
287
|
const fetchBookings = useAgendaStore((s) => s.fetchBookings)
|
|
@@ -317,13 +319,7 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, e
|
|
|
317
319
|
const [showNotes, setShowNotes] = useState(false)
|
|
318
320
|
const [saving, setSaving] = useState(false)
|
|
319
321
|
const [conflict, setConflict] = useState(false)
|
|
320
|
-
|
|
321
|
-
const [newClientName, setNewClientName] = useState('')
|
|
322
|
-
const [newClientPhone, setNewClientPhone] = useState('')
|
|
323
|
-
const [newClientEmail, setNewClientEmail] = useState('')
|
|
324
|
-
const [creatingClient, setCreatingClient] = useState(false)
|
|
325
|
-
const [clientOptions, setClientOptions] = useState<ComboboxOption[]>([])
|
|
326
|
-
const [clientSearching, setClientSearching] = useState(false)
|
|
322
|
+
// Client search/quick-create state now lives inside <ContactPicker>.
|
|
327
323
|
const [serviceOptions, setServiceOptions] = useState<ComboboxOption[]>([])
|
|
328
324
|
const [initialServiceOptions, setInitialServiceOptions] = useState<ComboboxOption[]>([])
|
|
329
325
|
const [editingDate, setEditingDate] = useState(false)
|
|
@@ -353,7 +349,6 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, e
|
|
|
353
349
|
setProfessionalId(prefill?.professionalId ?? ''); setLocationId(prefill?.locationId ?? ''); setServices([])
|
|
354
350
|
setServiceSearch(''); setNotes(''); setStatus('scheduled'); setShowNotes(false)
|
|
355
351
|
setConflict(false); setLoading(mode === 'edit')
|
|
356
|
-
setQuickCreate(false); setNewClientName(''); setNewClientPhone(''); setNewClientEmail(''); setCreatingClient(false)
|
|
357
352
|
setBookingType(prefill?.kind ?? defaultBookingType); setTitle('')
|
|
358
353
|
setEditingDate(false); setEditingTime(false); setConfirmingDelete(false); setDeleting(false)
|
|
359
354
|
setEditOrderId(null); setEditOrderTotal(0); setEditPaymentStatus('none'); setPaymentStatusLoading(false)
|
|
@@ -449,20 +444,7 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, e
|
|
|
449
444
|
}).catch(() => setInitialServiceOptions([]))
|
|
450
445
|
}, [open])
|
|
451
446
|
|
|
452
|
-
// Search effects
|
|
453
|
-
useEffect(() => {
|
|
454
|
-
if (!config.contactLookup || clientSearch.length < 1 || clientId) { setClientOptions([]); setClientSearching(false); return }
|
|
455
|
-
setClientSearching(true)
|
|
456
|
-
const timer = setTimeout(async () => {
|
|
457
|
-
try {
|
|
458
|
-
const results = await config.contactLookup!.search(clientSearch)
|
|
459
|
-
setClientOptions(results.map((r: any) => ({ id: r.id, label: r.label, subtitle: r.subtitle, price: r.price, data: r.data })))
|
|
460
|
-
} catch { setClientOptions([]) }
|
|
461
|
-
setClientSearching(false)
|
|
462
|
-
}, 250)
|
|
463
|
-
return () => clearTimeout(timer)
|
|
464
|
-
}, [clientSearch, clientId])
|
|
465
|
-
|
|
447
|
+
// Search effects (client search now lives inside <ContactPicker>)
|
|
466
448
|
useEffect(() => {
|
|
467
449
|
if (!config.serviceLookup || serviceSearch.length < 1) { setServiceOptions([]); return }
|
|
468
450
|
const timer = setTimeout(async () => {
|
|
@@ -501,10 +483,6 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, e
|
|
|
501
483
|
return () => clearTimeout(timer)
|
|
502
484
|
}, [professionalId, date, startTime, endTime, bookingId])
|
|
503
485
|
|
|
504
|
-
function selectClient(opt: ComboboxOption) {
|
|
505
|
-
setClientId(opt.id); setClientSearch(opt.label); setClientOptions([])
|
|
506
|
-
setClientPhone((opt.data?.phone as string) ?? '')
|
|
507
|
-
}
|
|
508
486
|
function addService(opt: ComboboxOption) {
|
|
509
487
|
const duration = (opt.data?.duration_minutes as number) || 60
|
|
510
488
|
setServices((p) => [...p, { serviceId: opt.id, name: opt.label, durationMinutes: duration, price: opt.price ?? 0 }])
|
|
@@ -515,6 +493,9 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, e
|
|
|
515
493
|
async function handleSubmit(e: React.FormEvent) {
|
|
516
494
|
e.preventDefault()
|
|
517
495
|
if (!canSubmit) return
|
|
496
|
+
// Plan quantity guard (client-side, before the store call): opens the global
|
|
497
|
+
// UpgradeModal and aborts when the monthly booking cap is reached. CREATE only.
|
|
498
|
+
if (mode === 'create' && (await guardBookings()) === 'blocked') return
|
|
518
499
|
setSaving(true)
|
|
519
500
|
try {
|
|
520
501
|
const startsAt = new Date(`${date}T${startTime}:00`).toISOString()
|
|
@@ -522,6 +503,7 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, e
|
|
|
522
503
|
const newBooking = await createBooking({ kind: bookingType, clientId, professionalId, locationId: locationId || undefined, startsAt, notes: notes || undefined,
|
|
523
504
|
title: activeType.fields.title ? title.trim() : undefined,
|
|
524
505
|
services: services.map((s: any) => ({ serviceId: s.serviceId, name: s.name, durationMinutes: s.durationMinutes, price: s.price })) })
|
|
506
|
+
invalidateLimit('bookings_month')
|
|
525
507
|
onClose()
|
|
526
508
|
onCreated?.(newBooking.id, externalSource)
|
|
527
509
|
} else if (bookingId) {
|
|
@@ -547,8 +529,17 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, e
|
|
|
547
529
|
async function handleDelete() {
|
|
548
530
|
if (!bookingId) return
|
|
549
531
|
setDeleting(true)
|
|
550
|
-
try {
|
|
551
|
-
|
|
532
|
+
try {
|
|
533
|
+
await deleteBooking(bookingId)
|
|
534
|
+
onClose()
|
|
535
|
+
} catch {
|
|
536
|
+
// deleteBooking surfaces its own toast. Return the footer to its normal
|
|
537
|
+
// state (out of the confirm step) so a failure is clear feedback rather
|
|
538
|
+
// than a modal that silently stays stuck on "Confirm delete".
|
|
539
|
+
setConfirmingDelete(false)
|
|
540
|
+
} finally {
|
|
541
|
+
setDeleting(false)
|
|
542
|
+
}
|
|
552
543
|
}
|
|
553
544
|
|
|
554
545
|
const isPaidBooking = editPaymentStatus === 'paid'
|
|
@@ -802,84 +793,35 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, e
|
|
|
802
793
|
<div className="flex items-center gap-3 py-1">
|
|
803
794
|
<User className="h-4 w-4 text-muted-foreground shrink-0" />
|
|
804
795
|
<div className="flex-1">
|
|
805
|
-
{
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
placeholder={t('agenda.appointment.name')} className="flex-1 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2.5 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-ring" autoFocus />
|
|
835
|
-
<button type="button" onClick={() => setQuickCreate(false)}
|
|
836
|
-
className="p-1 text-muted-foreground hover:text-foreground shrink-0"><X className="h-3.5 w-3.5" /></button>
|
|
837
|
-
</div>
|
|
838
|
-
<div className="grid grid-cols-2 gap-2">
|
|
839
|
-
<input type="tel" value={newClientPhone} onChange={(e) => setNewClientPhone(e.target.value)}
|
|
840
|
-
placeholder={t('agenda.appointment.phone')} className="rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2.5 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-ring" />
|
|
841
|
-
<input type="email" value={newClientEmail} onChange={(e) => setNewClientEmail(e.target.value)}
|
|
842
|
-
placeholder={t('agenda.appointment.emailOptional')} className="rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2.5 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-ring" />
|
|
843
|
-
</div>
|
|
844
|
-
<button type="button" disabled={creatingClient || !newClientName.trim()}
|
|
845
|
-
onClick={async () => {
|
|
846
|
-
setCreatingClient(true)
|
|
847
|
-
try {
|
|
848
|
-
const { getSupabaseClientOptional } = await import('@fayz-ai/core')
|
|
849
|
-
const { getAgendaTenantId } = await import('../lib/tenant')
|
|
850
|
-
const supabase = getSupabaseClientOptional() as any
|
|
851
|
-
const tenantId = getAgendaTenantId()
|
|
852
|
-
if (!supabase || !tenantId) throw new Error('Not initialized')
|
|
853
|
-
|
|
854
|
-
// 1. Insert into public.people (archetype base table)
|
|
855
|
-
const { data: personRow, error: personError } = await supabase.from('people').insert({
|
|
856
|
-
tenant_id: tenantId, kind: config.clientKind, name: newClientName.trim(),
|
|
857
|
-
phone: newClientPhone.trim() || null, email: newClientEmail.trim() || null,
|
|
858
|
-
}).select('id, name').single()
|
|
859
|
-
if (personError) throw personError
|
|
860
|
-
|
|
861
|
-
// 2. Insert into public.clients (project extension table)
|
|
862
|
-
const { error: clientError } = await supabase.from('clients').insert({
|
|
863
|
-
person_id: personRow.id, tenant_id: tenantId,
|
|
864
|
-
})
|
|
865
|
-
if (clientError) {
|
|
866
|
-
// Rollback person if extension insert fails
|
|
867
|
-
await supabase.from('people').delete().eq('id', personRow.id)
|
|
868
|
-
throw clientError
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
setClientId(personRow.id); setClientSearch(personRow.name); setQuickCreate(false)
|
|
872
|
-
} catch (err: any) {
|
|
873
|
-
const { toast } = await import('@fayz-ai/ui')
|
|
874
|
-
toast.error(t('agenda.appointment.createClientFailed'), { description: err?.message })
|
|
875
|
-
}
|
|
876
|
-
setCreatingClient(false)
|
|
877
|
-
}}
|
|
878
|
-
className="w-full rounded-md bg-primary border border-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset disabled:opacity-40 transition-colors">
|
|
879
|
-
{creatingClient ? t('agenda.appointment.creating') : t('agenda.appointment.create')}
|
|
880
|
-
</button>
|
|
881
|
-
</div>
|
|
882
|
-
)}
|
|
796
|
+
{/* Shared find-or-create contact flow (@fayz-ai/saas). The
|
|
797
|
+
quick-create that used to live inline here now writes the
|
|
798
|
+
SAME people(+clients) rows for every plugin — see
|
|
799
|
+
`createPerson`. `clientKind`/`clientExtensionTable` keep the
|
|
800
|
+
vertical's discriminator and extension table.
|
|
801
|
+
`variant="inline"` keeps this modal's grammar: an icon
|
|
802
|
+
column and borderless controls (like Scheduled / Add
|
|
803
|
+
notes). Same component and same flow as the conversations
|
|
804
|
+
composer — only the input's chrome differs. */}
|
|
805
|
+
<ContactPicker
|
|
806
|
+
value={clientId || clientSearch ? { id: clientId || undefined, name: clientSearch, phone: clientPhone } : null}
|
|
807
|
+
onChange={(contact) => {
|
|
808
|
+
setClientId(contact?.id ?? '')
|
|
809
|
+
setClientSearch(contact?.name ?? '')
|
|
810
|
+
setClientPhone(contact?.phone ?? '')
|
|
811
|
+
}}
|
|
812
|
+
kind={config.clientKind}
|
|
813
|
+
extensionTable="clients"
|
|
814
|
+
lookup={config.contactLookup}
|
|
815
|
+
clearable={!isPaidBooking}
|
|
816
|
+
variant="inline"
|
|
817
|
+
placeholder={t('agenda.appointment.searchClient')}
|
|
818
|
+
createLabel={t('agenda.appointment.newClient')}
|
|
819
|
+
hint={
|
|
820
|
+
<p className="text-[11px] text-warning">
|
|
821
|
+
{t('agenda.appointment.selectClientHint')}
|
|
822
|
+
</p>
|
|
823
|
+
}
|
|
824
|
+
/>
|
|
883
825
|
</div>
|
|
884
826
|
</div>
|
|
885
827
|
)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { ChevronLeft, ChevronRight, Plus, Calendar } from 'lucide-react'
|
|
3
3
|
import { SegmentedControl } from '@fayz-ai/ui'
|
|
4
|
+
import { PermissionGate } from '@fayz-ai/saas'
|
|
4
5
|
import { useAgendaConfig } from '../AgendaContext'
|
|
5
6
|
import { useTranslation } from '@fayz-ai/core'
|
|
6
7
|
|
|
@@ -70,13 +71,15 @@ export function CalendarToolbar({
|
|
|
70
71
|
aria-label="Calendar view"
|
|
71
72
|
/>
|
|
72
73
|
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
<PermissionGate feature="appointments" action="create">
|
|
75
|
+
<button
|
|
76
|
+
onClick={onNewAppointment}
|
|
77
|
+
className="flex items-center gap-2 rounded-lg bg-primary border border-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors"
|
|
78
|
+
>
|
|
79
|
+
<Plus className="h-4 w-4" />
|
|
80
|
+
<span className="hidden sm:inline">{config.labels.newAppointment}</span>
|
|
81
|
+
</button>
|
|
82
|
+
</PermissionGate>
|
|
80
83
|
</div>
|
|
81
84
|
</div>
|
|
82
85
|
)
|
package/src/data/supabase.ts
CHANGED
|
@@ -151,8 +151,14 @@ async function hydrateCanonicalBookings(core: any, bookings: any[]): Promise<Cal
|
|
|
151
151
|
locationId: booking.location_id,
|
|
152
152
|
metadata: booking.metadata ?? {},
|
|
153
153
|
clientId: booking.party_id,
|
|
154
|
-
// Simple events (no party) fall back to the title stored in metadata
|
|
155
|
-
|
|
154
|
+
// Simple events (no party) fall back to the title stored in metadata;
|
|
155
|
+
// partyless blocks/tasks (e.g. imported Google events) get their notes or
|
|
156
|
+
// a kind label so the calendar never renders an unnamed chip.
|
|
157
|
+
clientName: client?.name
|
|
158
|
+
?? (booking.metadata?.title as string | undefined)
|
|
159
|
+
?? (booking.kind === 'appointment'
|
|
160
|
+
? null
|
|
161
|
+
: booking.notes || (booking.kind === 'task' ? 'Tarefa' : 'Bloqueio')),
|
|
156
162
|
clientPhone: client?.phone ?? null,
|
|
157
163
|
clientEmail: client?.email ?? null,
|
|
158
164
|
clientAvatarUrl: client?.avatar_url ?? null,
|
|
@@ -206,7 +212,13 @@ async function getCanonicalBookingById(core: any, id: string): Promise<CalendarB
|
|
|
206
212
|
// Supabase Agenda Provider
|
|
207
213
|
// ---------------------------------------------------------------------------
|
|
208
214
|
|
|
209
|
-
export
|
|
215
|
+
export interface SupabaseAgendaProviderOptions {
|
|
216
|
+
/** people.kind used for calendar resources/professionals (default 'staff' — schools use 'teacher'). */
|
|
217
|
+
professionalKind?: string
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function createSupabaseAgendaProvider(providerOptions?: SupabaseAgendaProviderOptions): AgendaDataProvider {
|
|
221
|
+
const professionalKind = providerOptions?.professionalKind ?? 'staff'
|
|
210
222
|
return {
|
|
211
223
|
// v_appointments is in public schema (PostgREST default)
|
|
212
224
|
async getBookings(query: BookingQuery): Promise<CalendarBooking[]> {
|
|
@@ -233,7 +245,37 @@ export function createSupabaseAgendaProvider(): AgendaDataProvider {
|
|
|
233
245
|
const { core, pub } = getClients()
|
|
234
246
|
const { data, error } = await pub.from('v_appointments').select('*').eq('id', id).maybeSingle()
|
|
235
247
|
if (error) throw error
|
|
236
|
-
if (data)
|
|
248
|
+
if (data) {
|
|
249
|
+
const booking = mapBooking(data as Record<string, unknown>)
|
|
250
|
+
// v_appointments is a FLAT read model — it exposes no line items. The
|
|
251
|
+
// calendar list path merges in the canonical hydration (which carries
|
|
252
|
+
// `services` from order_items), but this single-record lookup used by the
|
|
253
|
+
// edit modal previously returned the bare view row. That dropped
|
|
254
|
+
// `services`, so a booking whose type has `requiresServices` reopened with
|
|
255
|
+
// an empty service list and kept the "Update" button permanently disabled
|
|
256
|
+
// (canSubmit stayed false). Hydrate the line items here to stay consistent
|
|
257
|
+
// with getBookings.
|
|
258
|
+
if (booking.orderId) {
|
|
259
|
+
const { data: items } = await core.from('order_items')
|
|
260
|
+
.select('id, service_id, name, duration_minutes, unit_price, assignee_id, sort_order')
|
|
261
|
+
.eq('order_id', booking.orderId)
|
|
262
|
+
.order('sort_order', { ascending: true })
|
|
263
|
+
if (items && items.length) {
|
|
264
|
+
const services = items.map((item: any) => ({
|
|
265
|
+
id: item.id,
|
|
266
|
+
serviceId: item.service_id,
|
|
267
|
+
name: item.name,
|
|
268
|
+
durationMinutes: item.duration_minutes,
|
|
269
|
+
price: item.unit_price,
|
|
270
|
+
assigneeId: item.assignee_id,
|
|
271
|
+
}))
|
|
272
|
+
booking.services = services
|
|
273
|
+
const itemDuration = services.reduce((sum: number, s: any) => sum + (s.durationMinutes ?? 0), 0)
|
|
274
|
+
if (itemDuration) booking.totalDurationMinutes = itemDuration
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return booking
|
|
278
|
+
}
|
|
237
279
|
return getCanonicalBookingById(core, id)
|
|
238
280
|
},
|
|
239
281
|
|
|
@@ -264,7 +306,7 @@ export function createSupabaseAgendaProvider(): AgendaDataProvider {
|
|
|
264
306
|
kind: input.kind ?? 'appointment',
|
|
265
307
|
status: 'scheduled',
|
|
266
308
|
party_id: input.clientId || null,
|
|
267
|
-
assignee_id: input.professionalId,
|
|
309
|
+
assignee_id: input.professionalId || null,
|
|
268
310
|
location_id: input.locationId ?? null,
|
|
269
311
|
subtotal: totalPrice,
|
|
270
312
|
total: totalPrice,
|
|
@@ -282,7 +324,7 @@ export function createSupabaseAgendaProvider(): AgendaDataProvider {
|
|
|
282
324
|
tenant_id: tenantId,
|
|
283
325
|
kind: input.kind ?? 'appointment',
|
|
284
326
|
party_id: input.clientId || null,
|
|
285
|
-
assignee_id: input.professionalId,
|
|
327
|
+
assignee_id: input.professionalId || null,
|
|
286
328
|
location_id: input.locationId ?? null,
|
|
287
329
|
order_id: order.id,
|
|
288
330
|
starts_at: input.startsAt,
|
|
@@ -309,7 +351,7 @@ export function createSupabaseAgendaProvider(): AgendaDataProvider {
|
|
|
309
351
|
total: s.price,
|
|
310
352
|
sort_order: i,
|
|
311
353
|
duration_minutes: s.durationMinutes,
|
|
312
|
-
assignee_id: s.assigneeId
|
|
354
|
+
assignee_id: s.assigneeId || input.professionalId || null,
|
|
313
355
|
}))
|
|
314
356
|
const { error: oiErr } = await core.from('order_items').insert(orderItems)
|
|
315
357
|
if (oiErr) throw oiErr
|
|
@@ -413,12 +455,24 @@ export function createSupabaseAgendaProvider(): AgendaDataProvider {
|
|
|
413
455
|
if (bookingErr) throw bookingErr
|
|
414
456
|
|
|
415
457
|
const orderId = booking?.order_id as string | null | undefined
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
458
|
+
// Financial movements live in the financial plugin's tables. Pools that
|
|
459
|
+
// don't provision that plugin (e.g. school/event agendas) have no
|
|
460
|
+
// plg_financial_movements table, and the probe below returns a PostgREST
|
|
461
|
+
// "relation does not exist" error. Previously that error was rethrown,
|
|
462
|
+
// which aborted the ENTIRE delete — the modal caught it and left the
|
|
463
|
+
// booking in place, so "Delete" looked like a silent no-op (works in
|
|
464
|
+
// beauty, which has the table; fails in school, which doesn't). Treat any
|
|
465
|
+
// probe failure as "no linked movements" and fall through to a plain delete.
|
|
466
|
+
let movementCount = 0
|
|
467
|
+
if (orderId) {
|
|
468
|
+
const { count, error: movementErr } = await pub
|
|
469
|
+
.from('plg_financial_movements')
|
|
470
|
+
.select('id', { count: 'exact', head: true })
|
|
471
|
+
.eq('invoice_id', orderId)
|
|
472
|
+
if (!movementErr) movementCount = count ?? 0
|
|
473
|
+
}
|
|
420
474
|
|
|
421
|
-
if (orderId &&
|
|
475
|
+
if (orderId && movementCount > 0) {
|
|
422
476
|
// Cancel movements + order (don't delete financial records)
|
|
423
477
|
await pub.from('plg_financial_movements').update({ status: 'cancelled' }).eq('invoice_id', orderId)
|
|
424
478
|
await core.from('orders').update({ status: 'cancelled' }).eq('id', orderId)
|
|
@@ -516,7 +570,7 @@ export function createSupabaseAgendaProvider(): AgendaDataProvider {
|
|
|
516
570
|
const { core } = getClients()
|
|
517
571
|
const { data, error } = await core.from('people')
|
|
518
572
|
.select('id, name, avatar_url, is_active')
|
|
519
|
-
.eq('kind',
|
|
573
|
+
.eq('kind', professionalKind)
|
|
520
574
|
.eq('is_active', true)
|
|
521
575
|
.order('name')
|
|
522
576
|
if (error) throw error
|