@fayz-ai/plugin-agenda 0.1.7 → 0.2.0
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/components/AgendaMobileList.d.ts +10 -0
- package/dist/components/AgendaMobileList.d.ts.map +1 -0
- package/dist/components/AppointmentModal.d.ts +4 -2
- package/dist/components/AppointmentModal.d.ts.map +1 -1
- package/dist/components/AppointmentPopover.d.ts.map +1 -1
- package/dist/components/EventContextMenu.d.ts.map +1 -1
- package/dist/config.d.ts +62 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/data/index.d.ts +1 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/mock.d.ts +15 -1
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/index.cjs +614 -308
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +452 -147
- package/dist/index.js.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/store.d.ts +3 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/views/CalendarView.d.ts.map +1 -1
- package/package.json +5 -8
- package/src/components/AgendaMobileList.tsx +103 -0
- package/src/components/AppointmentModal.tsx +86 -9
- package/src/components/AppointmentPopover.tsx +20 -10
- package/src/components/EventContextMenu.tsx +3 -0
- package/src/config.ts +104 -5
- package/src/data/index.ts +1 -0
- package/src/data/mock.ts +22 -5
- package/src/data/supabase.ts +13 -2
- package/src/index.ts +8 -3
- package/src/locales/en.ts +12 -0
- package/src/locales/pt-BR.ts +12 -0
- package/src/store.ts +4 -2
- package/src/types.ts +8 -0
- package/src/views/CalendarView.tsx +220 -54
package/dist/locales/en.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/locales/en.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/locales/en.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyNrC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pt-BR.d.ts","sourceRoot":"","sources":["../../src/locales/pt-BR.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"pt-BR.d.ts","sourceRoot":"","sources":["../../src/locales/pt-BR.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyNvC,CAAA"}
|
package/dist/store.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type StoreApi } from 'zustand/vanilla';
|
|
2
2
|
import type { AgendaDataProvider } from './data/types';
|
|
3
3
|
import type { AgendaFinancialBridge } from './financial-bridge';
|
|
4
|
+
import type { ExternalBookingSource } from './config';
|
|
4
5
|
import type { CalendarBooking, Professional, Schedule, CreateBookingInput, UpdateBookingInput } from './types';
|
|
5
6
|
export interface AgendaUIState {
|
|
6
7
|
bookings: CalendarBooking[];
|
|
@@ -32,6 +33,7 @@ export interface AgendaUIState {
|
|
|
32
33
|
endsAt?: string;
|
|
33
34
|
};
|
|
34
35
|
initialTab?: string;
|
|
36
|
+
externalSource?: ExternalBookingSource;
|
|
35
37
|
};
|
|
36
38
|
fetchBookings(range: {
|
|
37
39
|
start: string;
|
|
@@ -59,6 +61,7 @@ export interface AgendaUIState {
|
|
|
59
61
|
endsAt?: string;
|
|
60
62
|
};
|
|
61
63
|
initialTab?: string;
|
|
64
|
+
externalSource?: ExternalBookingSource;
|
|
62
65
|
}): void;
|
|
63
66
|
closeAppointmentModal(): void;
|
|
64
67
|
}
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAI5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,KAAK,EACV,eAAe,EAAE,YAAY,EAAE,QAAQ,EACvC,kBAAkB,EAAE,kBAAkB,EACvC,MAAM,SAAS,CAAA;AAMhB,MAAM,WAAW,aAAa;IAE5B,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,eAAe,EAAE,OAAO,CAAA;IACxB,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACzC,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IAGpB,aAAa,EAAE,YAAY,EAAE,CAAA;IAC7B,oBAAoB,EAAE,OAAO,CAAA;IAG7B,uBAAuB,EAAE,MAAM,EAAE,CAAA;IACjC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAG1B,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAG9C,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,gBAAgB,EAAE,OAAO,CAAA;IAGzB,oBAAoB,EAAE,eAAe,EAAE,CAAA;IACvC,oBAAoB,EAAE,OAAO,CAAA;IAG7B,gBAAgB,EAAE;QAChB,IAAI,EAAE,OAAO,CAAA;QACb,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;QACvB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,OAAO,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG;YAAE,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QAC3D,UAAU,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,9 @@ export interface BookingTypeConfig {
|
|
|
11
11
|
icon: string;
|
|
12
12
|
color: string;
|
|
13
13
|
fields: {
|
|
14
|
+
/** Free-text event title (maps to the booking's display name). Used by
|
|
15
|
+
* simple / Google-Calendar-style events that have no client party. */
|
|
16
|
+
title?: boolean;
|
|
14
17
|
client: boolean;
|
|
15
18
|
professional: boolean;
|
|
16
19
|
services: boolean;
|
|
@@ -148,6 +151,9 @@ export interface CreateBookingInput {
|
|
|
148
151
|
locationId?: string;
|
|
149
152
|
startsAt: string;
|
|
150
153
|
notes?: string;
|
|
154
|
+
/** Free-text event title for simple events (no client party). Maps to the
|
|
155
|
+
* booking's display name (clientName in the read model). */
|
|
156
|
+
title?: string;
|
|
151
157
|
services: CreateBookingServiceInput[];
|
|
152
158
|
}
|
|
153
159
|
export interface CreateBookingServiceInput {
|
|
@@ -165,6 +171,8 @@ export interface UpdateBookingInput {
|
|
|
165
171
|
endsAt?: string;
|
|
166
172
|
status?: BookingStatus;
|
|
167
173
|
notes?: string;
|
|
174
|
+
/** Free-text event title for simple events (no client party). */
|
|
175
|
+
title?: string;
|
|
168
176
|
services?: CreateBookingServiceInput[];
|
|
169
177
|
}
|
|
170
178
|
export interface SaveScheduleInput {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/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;
|
|
1
|
+
{"version":3,"file":"CalendarView.d.ts","sourceRoot":"","sources":["../../src/views/CalendarView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAwRhF,wBAAgB,YAAY,sBAqxB3B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fayz-ai/plugin-agenda",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Fayz SDK — plugin-agenda plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"@tanstack/react-table": "^8.20.0",
|
|
34
34
|
"@fullcalendar/resource-timegrid": "^6.1.0",
|
|
35
35
|
"@fullcalendar/list": "^6.1.0",
|
|
36
|
-
"@fayz-ai/
|
|
37
|
-
"@fayz-ai/
|
|
38
|
-
"@fayz-ai/saas": "^0.
|
|
39
|
-
"@fayz-ai/plugin-financial": "^0.
|
|
36
|
+
"@fayz-ai/core": "^0.6.6",
|
|
37
|
+
"@fayz-ai/ui": "^0.6.6",
|
|
38
|
+
"@fayz-ai/saas": "^0.6.6",
|
|
39
|
+
"@fayz-ai/plugin-financial": "^0.2.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/react": "^18.3.0",
|
|
@@ -53,9 +53,6 @@
|
|
|
53
53
|
"fayz-plugin",
|
|
54
54
|
"fayz-sdk"
|
|
55
55
|
],
|
|
56
|
-
"publishConfig": {
|
|
57
|
-
"access": "public"
|
|
58
|
-
},
|
|
59
56
|
"scripts": {
|
|
60
57
|
"build": "tsup && tsc --emitDeclarationOnly --declaration --declarationMap --noEmit false",
|
|
61
58
|
"dev": "tsup --watch",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
|
+
import { formatCurrency, useTranslation, getCurrentLocale } from '@fayz-ai/core'
|
|
3
|
+
import type { CalendarBooking } from '../types'
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Mobile agenda list — a thumb-friendly, date-grouped list of upcoming items.
|
|
7
|
+
// Rendered on <md viewports instead of the FullCalendar grid. Each row shows a
|
|
8
|
+
// category/status dot, the title, the time, and (for finance-style events) the
|
|
9
|
+
// amount. Fed straight from the store bookings so it works for any seeded app.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
function startOfDay(d: Date): Date {
|
|
13
|
+
const x = new Date(d)
|
|
14
|
+
x.setHours(0, 0, 0, 0)
|
|
15
|
+
return x
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function timeLabel(iso: string): string {
|
|
19
|
+
const d = new Date(iso)
|
|
20
|
+
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AgendaMobileListProps {
|
|
24
|
+
bookings: CalendarBooking[]
|
|
25
|
+
statusColors: Record<string, string>
|
|
26
|
+
currencyCode: string
|
|
27
|
+
onSelect: (booking: CalendarBooking) => void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function AgendaMobileList({ bookings, statusColors, currencyCode, onSelect }: AgendaMobileListProps) {
|
|
31
|
+
const t = useTranslation()
|
|
32
|
+
const locale = getCurrentLocale() === 'pt-BR' ? 'pt-BR' : 'en'
|
|
33
|
+
|
|
34
|
+
const groups = useMemo(() => {
|
|
35
|
+
const todayStart = startOfDay(new Date()).getTime()
|
|
36
|
+
const sorted = bookings
|
|
37
|
+
.filter((b) => !!b.startsAt)
|
|
38
|
+
.slice()
|
|
39
|
+
.sort((a, b) => (a.startsAt < b.startsAt ? -1 : a.startsAt > b.startsAt ? 1 : 0))
|
|
40
|
+
const map = new Map<string, CalendarBooking[]>()
|
|
41
|
+
for (const b of sorted) {
|
|
42
|
+
const day = startOfDay(new Date(b.startsAt))
|
|
43
|
+
if (day.getTime() < todayStart) continue // upcoming only
|
|
44
|
+
const key = day.toISOString().slice(0, 10)
|
|
45
|
+
const list = map.get(key) ?? []
|
|
46
|
+
list.push(b)
|
|
47
|
+
map.set(key, list)
|
|
48
|
+
}
|
|
49
|
+
return Array.from(map.entries()).map(([key, items]) => ({ key, date: new Date(key + 'T00:00:00'), items }))
|
|
50
|
+
}, [bookings])
|
|
51
|
+
|
|
52
|
+
function dayLabel(date: Date): string {
|
|
53
|
+
const today = startOfDay(new Date())
|
|
54
|
+
const diff = Math.round((startOfDay(date).getTime() - today.getTime()) / 86400000)
|
|
55
|
+
if (diff === 0) return t('agenda.list.today')
|
|
56
|
+
if (diff === 1) return t('agenda.list.tomorrow')
|
|
57
|
+
return date.toLocaleDateString(locale, { weekday: 'long', day: 'numeric', month: 'long' })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (groups.length === 0) {
|
|
61
|
+
return (
|
|
62
|
+
<div className="flex-1 flex flex-col items-center justify-center px-6 py-16 text-center text-muted-foreground">
|
|
63
|
+
<p className="text-sm">{t('agenda.list.empty')}</p>
|
|
64
|
+
</div>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="flex-1 overflow-y-auto px-3 py-3 space-y-5">
|
|
70
|
+
{groups.map((g) => (
|
|
71
|
+
<div key={g.key}>
|
|
72
|
+
<h3 className="px-1 pb-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
73
|
+
{dayLabel(g.date)}
|
|
74
|
+
</h3>
|
|
75
|
+
<div className="space-y-1.5">
|
|
76
|
+
{g.items.map((b) => {
|
|
77
|
+
const color = statusColors[b.status] ?? '#6b7280'
|
|
78
|
+
const hasAmount = typeof b.orderTotal === 'number' && b.orderTotal > 0
|
|
79
|
+
return (
|
|
80
|
+
<button
|
|
81
|
+
key={b.id}
|
|
82
|
+
onClick={() => onSelect(b)}
|
|
83
|
+
className="flex w-full items-center gap-3 rounded-lg border bg-card px-3 py-2.5 text-left shadow-sm transition-colors active:bg-muted/50"
|
|
84
|
+
>
|
|
85
|
+
<span className="h-2.5 w-2.5 shrink-0 rounded-full" style={{ backgroundColor: color }} />
|
|
86
|
+
<div className="min-w-0 flex-1">
|
|
87
|
+
<p className="truncate text-sm font-medium text-foreground">{b.clientName ?? '—'}</p>
|
|
88
|
+
<p className="text-xs text-muted-foreground">{timeLabel(b.startsAt)}</p>
|
|
89
|
+
</div>
|
|
90
|
+
{hasAmount && (
|
|
91
|
+
<span className="shrink-0 text-sm font-semibold tabular-nums text-foreground">
|
|
92
|
+
{formatCurrency(b.orderTotal, currencyCode)}
|
|
93
|
+
</span>
|
|
94
|
+
)}
|
|
95
|
+
</button>
|
|
96
|
+
)
|
|
97
|
+
})}
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
))}
|
|
101
|
+
</div>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
@@ -21,6 +21,7 @@ import { useTranslation } from '@fayz-ai/core'
|
|
|
21
21
|
import { getCurrentLocale } from '@fayz-ai/core'
|
|
22
22
|
import { isStatusAvailable } from '../types'
|
|
23
23
|
import type { CreateBookingInput, BookingTypeId } from '../types'
|
|
24
|
+
import type { ExternalBookingSource } from '../config'
|
|
24
25
|
|
|
25
26
|
// ---------------------------------------------------------------------------
|
|
26
27
|
// Helpers
|
|
@@ -38,6 +39,7 @@ function formatCompactDate(dateStr: string, locale: string): string {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
interface ServiceItem { serviceId: string; name: string; durationMinutes: number; price: number }
|
|
42
|
+
interface CancellationReasonItem { id: string; label: string; requiresNotes?: boolean }
|
|
41
43
|
|
|
42
44
|
// ---------------------------------------------------------------------------
|
|
43
45
|
// Inline editable number — allows clearing, commits on blur/enter
|
|
@@ -266,11 +268,12 @@ interface Props {
|
|
|
266
268
|
bookingId?: string
|
|
267
269
|
prefill?: Partial<CreateBookingInput> & { endsAt?: string }
|
|
268
270
|
initialTab?: string
|
|
271
|
+
externalSource?: ExternalBookingSource
|
|
269
272
|
onClose: () => void
|
|
270
|
-
onCreated?: (bookingId: string) => void
|
|
273
|
+
onCreated?: (bookingId: string, source?: ExternalBookingSource) => void
|
|
271
274
|
}
|
|
272
275
|
|
|
273
|
-
export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose, onCreated }: Props) {
|
|
276
|
+
export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, externalSource, onClose, onCreated }: Props) {
|
|
274
277
|
const t = useTranslation()
|
|
275
278
|
const locale = getCurrentLocale()
|
|
276
279
|
const config = useAgendaConfig()
|
|
@@ -285,8 +288,12 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
285
288
|
const storeLocations = useAgendaStore((s) => s.locations)
|
|
286
289
|
|
|
287
290
|
// --- State ---
|
|
291
|
+
// Default to the first configured booking type (e.g. 'event' in simple mode,
|
|
292
|
+
// 'appointment' otherwise) so single-type agendas resolve their type cleanly.
|
|
293
|
+
const defaultBookingType = (config.bookingTypes[0]?.id ?? 'appointment') as BookingTypeId
|
|
288
294
|
const [loading, setLoading] = useState(mode === 'edit')
|
|
289
|
-
const [bookingType, setBookingType] = useState<BookingTypeId>(prefill?.kind ??
|
|
295
|
+
const [bookingType, setBookingType] = useState<BookingTypeId>(prefill?.kind ?? defaultBookingType)
|
|
296
|
+
const [title, setTitle] = useState('')
|
|
290
297
|
const [clientId, setClientId] = useState(prefill?.clientId ?? '')
|
|
291
298
|
const [clientSearch, setClientSearch] = useState('')
|
|
292
299
|
const [clientPhone, setClientPhone] = useState('')
|
|
@@ -331,6 +338,10 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
331
338
|
const [clientData, setClientData] = useState<Record<string, any> | null>(null)
|
|
332
339
|
const [clientDataLoading, setClientDataLoading] = useState(false)
|
|
333
340
|
const [clientEditing, setClientEditing] = useState(false)
|
|
341
|
+
const [cancellationReasons, setCancellationReasons] = useState<CancellationReasonItem[]>([])
|
|
342
|
+
const [cancellationReasonId, setCancellationReasonId] = useState('')
|
|
343
|
+
const [cancellationNotes, setCancellationNotes] = useState('')
|
|
344
|
+
const [cancellationReasonsLoading, setCancellationReasonsLoading] = useState(false)
|
|
334
345
|
|
|
335
346
|
// Active booking type config
|
|
336
347
|
const activeType = config.bookingTypes.find((bt) => bt.id === bookingType) ?? config.bookingTypes[0]
|
|
@@ -343,10 +354,11 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
343
354
|
setServiceSearch(''); setNotes(''); setStatus('scheduled'); setShowNotes(false)
|
|
344
355
|
setConflict(false); setLoading(mode === 'edit')
|
|
345
356
|
setQuickCreate(false); setNewClientName(''); setNewClientPhone(''); setNewClientEmail(''); setCreatingClient(false)
|
|
346
|
-
setBookingType(prefill?.kind ?? '
|
|
357
|
+
setBookingType(prefill?.kind ?? defaultBookingType); setTitle('')
|
|
347
358
|
setEditingDate(false); setEditingTime(false); setConfirmingDelete(false); setDeleting(false)
|
|
348
359
|
setEditOrderId(null); setEditOrderTotal(0); setEditPaymentStatus('none'); setPaymentStatusLoading(false)
|
|
349
360
|
setClientData(null); setClientDataLoading(false); setClientEditing(false)
|
|
361
|
+
setCancellationReasonId(''); setCancellationNotes(''); setCancellationReasonsLoading(false)
|
|
350
362
|
setModalTab((initialTab as 'appointment' | 'client' | 'financial') || 'appointment')
|
|
351
363
|
if (!prefill?.professionalId && professionals.length === 1) setProfessionalId(professionals[0].id)
|
|
352
364
|
setDate(() => {
|
|
@@ -368,7 +380,8 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
368
380
|
setLoading(true)
|
|
369
381
|
provider.getBookingById(bookingId).then((b) => {
|
|
370
382
|
if (!b) { setLoading(false); return }
|
|
371
|
-
setBookingType((b.kind as BookingTypeId) ??
|
|
383
|
+
setBookingType((b.kind as BookingTypeId) ?? defaultBookingType)
|
|
384
|
+
setTitle(b.clientName ?? '')
|
|
372
385
|
setClientId(b.clientId ?? ''); setClientSearch(b.clientName ?? '')
|
|
373
386
|
setProfessionalId(b.professionalId ?? ''); setLocationId(b.locationId ?? '')
|
|
374
387
|
setDate(b.startsAt.slice(0, 10))
|
|
@@ -385,10 +398,11 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
385
398
|
// Save initial snapshot for dirty check
|
|
386
399
|
const svcSnap = b.services ? b.services.map((s: any) => ({ id: s.serviceId ?? '', dur: s.durationMinutes, price: s.price })) : []
|
|
387
400
|
initialSnapshot.current = JSON.stringify({
|
|
401
|
+
title: b.clientName ?? '',
|
|
388
402
|
clientId: b.clientId ?? '', professionalId: b.professionalId ?? '', locationId: b.locationId ?? '',
|
|
389
403
|
date: b.startsAt.slice(0, 10),
|
|
390
404
|
startTime: `${String(new Date(b.startsAt).getHours()).padStart(2, '0')}:${String(new Date(b.startsAt).getMinutes()).padStart(2, '0')}`,
|
|
391
|
-
status: b.status, notes: b.notes ?? '', services: svcSnap,
|
|
405
|
+
status: b.status, notes: b.notes ?? '', cancellationReasonId: '', cancellationNotes: '', services: svcSnap,
|
|
392
406
|
})
|
|
393
407
|
setLoading(false)
|
|
394
408
|
// Resolve payment status from bridge if available
|
|
@@ -415,6 +429,15 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
415
429
|
}).catch(() => setClientDataLoading(false))
|
|
416
430
|
}, [modalTab, clientId])
|
|
417
431
|
|
|
432
|
+
useEffect(() => {
|
|
433
|
+
if (!open || status !== 'cancelled' || !config.cancellationDetails || cancellationReasons.length > 0) return
|
|
434
|
+
setCancellationReasonsLoading(true)
|
|
435
|
+
config.cancellationDetails.listReasons()
|
|
436
|
+
.then((reasons) => setCancellationReasons(reasons))
|
|
437
|
+
.catch(() => setCancellationReasons([]))
|
|
438
|
+
.finally(() => setCancellationReasonsLoading(false))
|
|
439
|
+
}, [open, status, config.cancellationDetails, cancellationReasons.length])
|
|
440
|
+
|
|
418
441
|
// Fetch initial service list when modal opens
|
|
419
442
|
useEffect(() => {
|
|
420
443
|
if (!open || !config.serviceLookup) return
|
|
@@ -497,14 +520,24 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
497
520
|
const startsAt = new Date(`${date}T${startTime}:00`).toISOString()
|
|
498
521
|
if (mode === 'create') {
|
|
499
522
|
const newBooking = await createBooking({ kind: bookingType, clientId, professionalId, locationId: locationId || undefined, startsAt, notes: notes || undefined,
|
|
523
|
+
title: activeType.fields.title ? title.trim() : undefined,
|
|
500
524
|
services: services.map((s: any) => ({ serviceId: s.serviceId, name: s.name, durationMinutes: s.durationMinutes, price: s.price })) })
|
|
501
525
|
onClose()
|
|
502
|
-
onCreated?.(newBooking.id)
|
|
526
|
+
onCreated?.(newBooking.id, externalSource)
|
|
503
527
|
} else if (bookingId) {
|
|
504
528
|
await updateBooking(bookingId, {
|
|
505
529
|
clientId, professionalId, locationId: locationId || undefined, startsAt, notes: notes || undefined, status: status as any,
|
|
530
|
+
title: activeType.fields.title ? title.trim() : undefined,
|
|
506
531
|
services: services.map((s: any) => ({ serviceId: s.serviceId, name: s.name, durationMinutes: s.durationMinutes, price: s.price })),
|
|
507
532
|
})
|
|
533
|
+
if (status === 'cancelled' && config.cancellationDetails && cancellationReasonId) {
|
|
534
|
+
await config.cancellationDetails.saveCancellationDetails({
|
|
535
|
+
bookingId,
|
|
536
|
+
reasonId: cancellationReasonId,
|
|
537
|
+
notes: cancellationNotes.trim() || undefined,
|
|
538
|
+
cancelledAt: new Date().toISOString(),
|
|
539
|
+
})
|
|
540
|
+
}
|
|
508
541
|
onClose()
|
|
509
542
|
}
|
|
510
543
|
} catch { /* toast */ }
|
|
@@ -519,18 +552,24 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
519
552
|
}
|
|
520
553
|
|
|
521
554
|
const isPaidBooking = editPaymentStatus === 'paid'
|
|
555
|
+
const selectedCancellationReason = cancellationReasons.find((reason) => reason.id === cancellationReasonId)
|
|
556
|
+
const needsCancellationReason = mode === 'edit' && status === 'cancelled' && !!config.cancellationDetails
|
|
557
|
+
const needsCancellationNotes = needsCancellationReason && !!selectedCancellationReason?.requiresNotes
|
|
522
558
|
|
|
523
559
|
// Dirty check — only enable Update when something changed
|
|
524
560
|
const formSnapshot = useMemo(() =>
|
|
525
|
-
JSON.stringify({ clientId, professionalId, locationId, date, startTime, status, notes, services: services.map((s: any) => ({ id: s.serviceId, dur: s.durationMinutes, price: s.price })) }),
|
|
526
|
-
[clientId, professionalId, locationId, date, startTime, status, notes, services]
|
|
561
|
+
JSON.stringify({ title, clientId, professionalId, locationId, date, startTime, status, notes, cancellationReasonId, cancellationNotes, services: services.map((s: any) => ({ id: s.serviceId, dur: s.durationMinutes, price: s.price })) }),
|
|
562
|
+
[title, clientId, professionalId, locationId, date, startTime, status, notes, cancellationReasonId, cancellationNotes, services]
|
|
527
563
|
)
|
|
528
564
|
const isDirty = mode === 'create' || formSnapshot !== initialSnapshot.current
|
|
529
565
|
|
|
530
566
|
const canSubmit = (
|
|
567
|
+
(!activeType.fields.title || title.trim()) &&
|
|
531
568
|
(!activeType.requiresClient || clientId) &&
|
|
532
569
|
(!activeType.fields.professional || professionalId) &&
|
|
533
570
|
(!activeType.requiresServices || services.length > 0) &&
|
|
571
|
+
(!needsCancellationReason || !!cancellationReasonId) &&
|
|
572
|
+
(!needsCancellationNotes || !!cancellationNotes.trim()) &&
|
|
534
573
|
!conflict && !saving
|
|
535
574
|
)
|
|
536
575
|
|
|
@@ -748,6 +787,16 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
748
787
|
|
|
749
788
|
{!loading && <>
|
|
750
789
|
|
|
790
|
+
{/* Title — free-text event name (simple / Google-Calendar-style events) */}
|
|
791
|
+
{activeType.fields.title && (
|
|
792
|
+
<div className="flex items-center gap-3 py-1">
|
|
793
|
+
<Calendar className="h-4 w-4 text-muted-foreground shrink-0" />
|
|
794
|
+
<input type="text" value={title} onChange={(e) => setTitle(e.target.value)}
|
|
795
|
+
placeholder={t('agenda.appointment.addTitle')} autoFocus={mode === 'create'}
|
|
796
|
+
className="w-full 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" />
|
|
797
|
+
</div>
|
|
798
|
+
)}
|
|
799
|
+
|
|
751
800
|
{/* Client */}
|
|
752
801
|
{activeType.fields.client && (
|
|
753
802
|
<div className="flex items-center gap-3 py-1">
|
|
@@ -927,6 +976,34 @@ export function AppointmentModal({ open, mode, bookingId, prefill, initialTab, o
|
|
|
927
976
|
</div>
|
|
928
977
|
)}
|
|
929
978
|
|
|
979
|
+
{needsCancellationReason && (
|
|
980
|
+
<div className="flex items-start gap-3 py-1">
|
|
981
|
+
<Ban className="h-4 w-4 mt-2 text-muted-foreground shrink-0" />
|
|
982
|
+
<div className="flex-1 space-y-2">
|
|
983
|
+
<select
|
|
984
|
+
value={cancellationReasonId}
|
|
985
|
+
onChange={(event) => setCancellationReasonId(event.target.value)}
|
|
986
|
+
disabled={cancellationReasonsLoading}
|
|
987
|
+
className="w-full rounded-input border border-input bg-card px-2.5 py-1.5 text-sm shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring"
|
|
988
|
+
>
|
|
989
|
+
<option value="">{cancellationReasonsLoading ? t('agenda.cancellation.loadingReasons') : t('agenda.cancellation.selectReason')}</option>
|
|
990
|
+
{cancellationReasons.map((reason) => (
|
|
991
|
+
<option key={reason.id} value={reason.id}>
|
|
992
|
+
{reason.label}{reason.requiresNotes ? ' *' : ''}
|
|
993
|
+
</option>
|
|
994
|
+
))}
|
|
995
|
+
</select>
|
|
996
|
+
<textarea
|
|
997
|
+
value={cancellationNotes}
|
|
998
|
+
onChange={(event) => setCancellationNotes(event.target.value)}
|
|
999
|
+
placeholder={needsCancellationNotes ? t('agenda.cancellation.notesRequired') : t('agenda.cancellation.notesOptional')}
|
|
1000
|
+
rows={2}
|
|
1001
|
+
className="w-full rounded-input border border-input bg-card px-2.5 py-1.5 text-sm resize-none shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring"
|
|
1002
|
+
/>
|
|
1003
|
+
</div>
|
|
1004
|
+
</div>
|
|
1005
|
+
)}
|
|
1006
|
+
|
|
930
1007
|
{/* Notes — progressive disclosure */}
|
|
931
1008
|
{!showNotes ? (
|
|
932
1009
|
<div className="flex items-center gap-3 py-1">
|
|
@@ -126,7 +126,14 @@ export function AppointmentPopover({ booking, position, onClose, onEdit }: Props
|
|
|
126
126
|
<div className="flex items-start gap-3">
|
|
127
127
|
<span className="mt-1.5 h-3 w-3 rounded shrink-0" style={{ backgroundColor: color }} />
|
|
128
128
|
<div className="min-w-0">
|
|
129
|
-
|
|
129
|
+
{/* Client agendas link to the person; simple events (no client
|
|
130
|
+
capability) render the free-text title as a plain heading —
|
|
131
|
+
booking.clientName carries the event title in that model. */}
|
|
132
|
+
{config.capabilities.client ? (
|
|
133
|
+
<PersonLink personId={booking.clientId} name={booking.clientName ?? 'Unknown'} className="text-base" />
|
|
134
|
+
) : (
|
|
135
|
+
<p className="text-base font-medium truncate">{booking.clientName || t('agenda.appointment.untitled')}</p>
|
|
136
|
+
)}
|
|
130
137
|
<p className="text-sm text-muted-foreground mt-0.5">{fmtDate(booking.startsAt)} · {timeRange}</p>
|
|
131
138
|
</div>
|
|
132
139
|
</div>
|
|
@@ -142,7 +149,7 @@ export function AppointmentPopover({ booking, position, onClose, onEdit }: Props
|
|
|
142
149
|
</div>
|
|
143
150
|
)}
|
|
144
151
|
|
|
145
|
-
{booking.professionalName && (
|
|
152
|
+
{config.capabilities.professional && booking.professionalName && (
|
|
146
153
|
<div className="flex items-center gap-3">
|
|
147
154
|
<User className="h-4 w-4 text-muted-foreground shrink-0" />
|
|
148
155
|
<span className="text-sm">{booking.professionalName}</span>
|
|
@@ -204,14 +211,17 @@ export function AppointmentPopover({ booking, position, onClose, onEdit }: Props
|
|
|
204
211
|
)
|
|
205
212
|
})()}
|
|
206
213
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
214
|
+
{/* Status workflow — hidden for simple events (no status capability). */}
|
|
215
|
+
{config.capabilities.status && (
|
|
216
|
+
<div className="pt-2 border-t fayz-glass-divider">
|
|
217
|
+
<PopoverStatusSelect
|
|
218
|
+
value={booking.status}
|
|
219
|
+
statuses={config.statuses}
|
|
220
|
+
bookingStartsAt={booking.startsAt}
|
|
221
|
+
onChange={(v) => { updateStatus(booking.id, v); onClose() }}
|
|
222
|
+
/>
|
|
223
|
+
</div>
|
|
224
|
+
)}
|
|
215
225
|
</div>
|
|
216
226
|
</>
|
|
217
227
|
)}
|
|
@@ -101,6 +101,8 @@ export function EventContextMenu({ booking, position, onClose, onEdit }: Props)
|
|
|
101
101
|
)}
|
|
102
102
|
</div>
|
|
103
103
|
|
|
104
|
+
{/* Status workflow — hidden for simple events (no status capability). */}
|
|
105
|
+
{config.capabilities.status && (
|
|
104
106
|
<div className="border-t px-4 py-3">
|
|
105
107
|
<p className="text-[11px] font-medium text-muted-foreground mb-2">{t('agenda.contextMenu.changeStatus')}</p>
|
|
106
108
|
<div className="flex items-center gap-1.5">
|
|
@@ -115,6 +117,7 @@ export function EventContextMenu({ booking, position, onClose, onEdit }: Props)
|
|
|
115
117
|
})}
|
|
116
118
|
</div>
|
|
117
119
|
</div>
|
|
120
|
+
)}
|
|
118
121
|
</>
|
|
119
122
|
)}
|
|
120
123
|
</FloatingPanel>
|