@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneclick.dev/cms-core-modules",
3
- "version": "0.0.73",
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": "./src/server-handlers.ts"
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
- }