@oneclick.dev/cms-core-modules 0.0.73 → 0.0.75
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/server-handlers.cjs.js +1 -0
- package/dist/server-handlers.mjs +898 -0
- package/package.json +7 -4
- package/src/appointments/server.ts +0 -195
- package/src/googleAnalytics/server.ts +0 -1188
- package/src/products/server.ts +0 -146
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oneclick.dev/cms-core-modules",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.75",
|
|
4
4
|
"main": "dist/index.cjs.js",
|
|
5
5
|
"module": "dist/index.mjs",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,12 +11,15 @@
|
|
|
11
11
|
"types": "./dist/index.d.ts"
|
|
12
12
|
},
|
|
13
13
|
"./dist/cms-core-modules.css": "./dist/cms-core-modules.css",
|
|
14
|
-
"./server-handlers":
|
|
14
|
+
"./server-handlers": {
|
|
15
|
+
"import": "./dist/server-handlers.mjs",
|
|
16
|
+
"require": "./dist/server-handlers.cjs.js",
|
|
17
|
+
"types": "./dist/src/server-handlers.d.ts"
|
|
18
|
+
}
|
|
15
19
|
},
|
|
16
20
|
"files": [
|
|
17
21
|
"dist",
|
|
18
|
-
"src/*/tools.ts"
|
|
19
|
-
"src/*/server.ts"
|
|
22
|
+
"src/*/tools.ts"
|
|
20
23
|
],
|
|
21
24
|
"sideEffects": false,
|
|
22
25
|
"scripts": {
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { createRouter, defineEventHandler, createError, getRouterParam, getQuery } from 'h3'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Dependencies injected by the CMS when creating this handler.
|
|
5
|
-
* Modules never import CMS internals directly — they receive them here.
|
|
6
|
-
*/
|
|
7
|
-
export interface ModuleHandlerDeps {
|
|
8
|
-
/** Initialize a Firebase Admin app for a project-level integration */
|
|
9
|
-
initFirebase: (event: any, integrationId: string) => Promise<any>
|
|
10
|
-
/** Normalize Firestore Timestamp fields to plain dates */
|
|
11
|
-
normalizeTimestamps: (data: any) => any
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Appointments module server handler factory.
|
|
16
|
-
*
|
|
17
|
-
* Routes (relative to `/api/v1/modules/:instanceId/`):
|
|
18
|
-
* GET /appointments → list recent appointments/reservations
|
|
19
|
-
* GET /appointments/find → find appointment by name, email, date
|
|
20
|
-
* GET /appointments/:appointmentId → get single appointment/reservation
|
|
21
|
-
*/
|
|
22
|
-
export function createServerHandler(deps: ModuleHandlerDeps) {
|
|
23
|
-
const { initFirebase, normalizeTimestamps } = deps
|
|
24
|
-
const router = createRouter()
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Resolve the Firebase app + collection names from the module instance config.
|
|
28
|
-
*/
|
|
29
|
-
async function getFirebaseContext(event: any) {
|
|
30
|
-
const { supabase, instanceId } = event.context.module
|
|
31
|
-
|
|
32
|
-
const { data: moduleRow, error } = await supabase
|
|
33
|
-
.from('project_modules')
|
|
34
|
-
.select('config')
|
|
35
|
-
.eq('id', instanceId)
|
|
36
|
-
.single()
|
|
37
|
-
|
|
38
|
-
if (error || !moduleRow?.config) {
|
|
39
|
-
throw createError({ statusCode: 500, statusMessage: 'Failed to load module config.' })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const config = moduleRow.config as Record<string, any>
|
|
43
|
-
const integrationId = config.project as string
|
|
44
|
-
const reservationsCollection = (config.reservationsCollection as string) || 'bookings_orders'
|
|
45
|
-
const agendaCollection = (config.agendaCollection as string) || 'agendas'
|
|
46
|
-
|
|
47
|
-
if (!integrationId) {
|
|
48
|
-
throw createError({ statusCode: 400, statusMessage: 'Appointments module has no Firebase integration configured.' })
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const firebase = await initFirebase(event, integrationId)
|
|
52
|
-
return { firebase, reservationsCollection, agendaCollection }
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Flatten a booking order into individual reservation rows for the response.
|
|
57
|
-
*/
|
|
58
|
-
function flattenOrder(order: any) {
|
|
59
|
-
const reservations = order.reservations || []
|
|
60
|
-
return reservations.map((res: any) => ({
|
|
61
|
-
orderId: order.id,
|
|
62
|
-
reservationId: res.id,
|
|
63
|
-
customerInfo: order.customerInfo,
|
|
64
|
-
date: res.date,
|
|
65
|
-
startTime: res.timeslot?.startTime,
|
|
66
|
-
endTime: res.timeslot?.endTime,
|
|
67
|
-
spots: res.spots,
|
|
68
|
-
status: order.status,
|
|
69
|
-
reservationStatus: res.status,
|
|
70
|
-
resourceId: res.resourceId,
|
|
71
|
-
reservationPrice: res.totalPrice,
|
|
72
|
-
reservationBasePrice: res.basePrice,
|
|
73
|
-
reservationAddOnsPrice: res.addOnsPrice,
|
|
74
|
-
pricingOption: res.pricingOption,
|
|
75
|
-
amountDue: order.amountDue,
|
|
76
|
-
amountPaid: order.amountPaid,
|
|
77
|
-
createdAt: order.createdAt,
|
|
78
|
-
}))
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// ── GET /appointments (recent) ─────────────────────
|
|
82
|
-
router.get('/appointments', defineEventHandler(async (event) => {
|
|
83
|
-
const query = getQuery(event)
|
|
84
|
-
const limit = Math.min(Number(query.quantity) || 20, 100)
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
const { firebase, reservationsCollection } = await getFirebaseContext(event)
|
|
88
|
-
|
|
89
|
-
const snapshot = await firebase
|
|
90
|
-
.firestore()
|
|
91
|
-
.collection(reservationsCollection)
|
|
92
|
-
.orderBy('createdAt', 'desc')
|
|
93
|
-
.limit(limit)
|
|
94
|
-
.get()
|
|
95
|
-
|
|
96
|
-
const appointments = snapshot.docs.flatMap((doc: any) =>
|
|
97
|
-
flattenOrder(normalizeTimestamps({ id: doc.id, ...doc.data() }))
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
count: appointments.length,
|
|
102
|
-
appointments,
|
|
103
|
-
}
|
|
104
|
-
} catch (err: any) {
|
|
105
|
-
console.error('Appointments handler — list error:', err)
|
|
106
|
-
throw createError({
|
|
107
|
-
statusCode: err.statusCode || 500,
|
|
108
|
-
statusMessage: err.statusMessage || 'Failed to list appointments',
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
}))
|
|
112
|
-
|
|
113
|
-
// ── GET /appointments/find?name=&email=&date= ──────
|
|
114
|
-
router.get('/appointments/find', defineEventHandler(async (event) => {
|
|
115
|
-
const query = getQuery(event)
|
|
116
|
-
const name = (query.name as string || '').toLowerCase().trim()
|
|
117
|
-
const email = (query.email as string || '').toLowerCase().trim()
|
|
118
|
-
const date = query.date as string | undefined // YYYY-MM-DD
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
const { firebase, reservationsCollection } = await getFirebaseContext(event)
|
|
122
|
-
|
|
123
|
-
// Build Firestore query — we always have at least a date
|
|
124
|
-
let fbQuery: FirebaseFirestore.Query = firebase.firestore().collection(reservationsCollection)
|
|
125
|
-
|
|
126
|
-
// If an email is provided, use it as a server-side filter (exact match on Firestore)
|
|
127
|
-
if (email) {
|
|
128
|
-
fbQuery = fbQuery.where('customerInfo.email', '==', email)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const snapshot = await fbQuery.orderBy('createdAt', 'desc').limit(200).get()
|
|
132
|
-
|
|
133
|
-
let results = snapshot.docs.flatMap((doc: any) =>
|
|
134
|
-
flattenOrder(normalizeTimestamps({ id: doc.id, ...doc.data() }))
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
// Client-side filter: date (reservation date, not order date)
|
|
138
|
-
if (date) {
|
|
139
|
-
results = results.filter((r: any) => r.date === date)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Client-side filter: name (fuzzy — first or last name contains)
|
|
143
|
-
if (name) {
|
|
144
|
-
results = results.filter((r: any) => {
|
|
145
|
-
const first = (r.customerInfo?.firstName || '').toLowerCase()
|
|
146
|
-
const last = (r.customerInfo?.lastName || '').toLowerCase()
|
|
147
|
-
return first.includes(name) || last.includes(name) || `${first} ${last}`.includes(name)
|
|
148
|
-
})
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
count: results.length,
|
|
153
|
-
appointments: results,
|
|
154
|
-
}
|
|
155
|
-
} catch (err: any) {
|
|
156
|
-
console.error('Appointments handler — find error:', err)
|
|
157
|
-
throw createError({
|
|
158
|
-
statusCode: err.statusCode || 500,
|
|
159
|
-
statusMessage: err.statusMessage || 'Failed to find appointments',
|
|
160
|
-
})
|
|
161
|
-
}
|
|
162
|
-
}))
|
|
163
|
-
|
|
164
|
-
// ── GET /appointments/:appointmentId ───────────────
|
|
165
|
-
router.get('/appointments/:appointmentId', defineEventHandler(async (event) => {
|
|
166
|
-
const appointmentId = getRouterParam(event, 'appointmentId')
|
|
167
|
-
if (!appointmentId) {
|
|
168
|
-
throw createError({ statusCode: 400, statusMessage: 'Appointment ID is required.' })
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
const { firebase, reservationsCollection } = await getFirebaseContext(event)
|
|
173
|
-
console.log('Fetching appointment with ID:', appointmentId, reservationsCollection)
|
|
174
|
-
const snapshot = await firebase.firestore().collection(reservationsCollection).doc(appointmentId).get()
|
|
175
|
-
|
|
176
|
-
if (!snapshot.exists) {
|
|
177
|
-
throw createError({ statusCode: 404, statusMessage: 'Appointment not found.' })
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const order = normalizeTimestamps({ id: snapshot.id, ...snapshot.data() })
|
|
181
|
-
return {
|
|
182
|
-
...order,
|
|
183
|
-
reservations: flattenOrder(order),
|
|
184
|
-
}
|
|
185
|
-
} catch (err: any) {
|
|
186
|
-
console.error('Appointments handler — get error:', err)
|
|
187
|
-
throw createError({
|
|
188
|
-
statusCode: err.statusCode || 500,
|
|
189
|
-
statusMessage: err.statusMessage || 'Failed to get appointment',
|
|
190
|
-
})
|
|
191
|
-
}
|
|
192
|
-
}))
|
|
193
|
-
|
|
194
|
-
return router.handler
|
|
195
|
-
}
|