@ossy/booking 1.16.2 → 3.0.1

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 (92) hide show
  1. package/README.md +48 -21
  2. package/package.json +11 -8
  3. package/src/AvailabilityEditor.jsx +2 -13
  4. package/src/BookingList.jsx +1 -1
  5. package/src/BookingProductHome.jsx +38 -0
  6. package/src/BookingSalesPage.jsx +55 -0
  7. package/src/Definition.js +2 -2
  8. package/src/HeroCover.jsx +5 -5
  9. package/src/SalesSection.jsx +7 -15
  10. package/src/ServiceCard.jsx +2 -2
  11. package/src/availability-action-maps.js +39 -0
  12. package/src/availability-engine.js +18 -14
  13. package/src/availability-engine.spec.js +36 -4
  14. package/src/availability-setup.page.jsx +45 -46
  15. package/src/{availability.resource.js → availability.schema.js} +1 -5
  16. package/src/booking-cancellation.email.jsx +1 -1
  17. package/src/booking-card.component.jsx +3 -3
  18. package/src/booking-confirmation.email.jsx +5 -3
  19. package/src/booking-detail.page.jsx +15 -10
  20. package/src/booking-email-token.js +45 -0
  21. package/src/booking-home-content.js +50 -0
  22. package/src/booking-list.aggregate.js +65 -0
  23. package/src/booking-reminder.email.jsx +4 -3
  24. package/src/booking-request.email.jsx +5 -2
  25. package/src/booking-resources.js +3 -3
  26. package/src/{booking.resource.js → booking.schema.js} +12 -3
  27. package/src/bookings.component.jsx +26 -25
  28. package/src/bookings.page.jsx +29 -16
  29. package/src/cancel-booking.action.js +1 -1
  30. package/src/cancel-booking.task.js +14 -17
  31. package/src/confirm-booking.action.js +1 -1
  32. package/src/confirm-booking.api.js +35 -0
  33. package/src/confirm-booking.task.js +26 -24
  34. package/src/create-booking.action.js +1 -1
  35. package/src/create-booking.task.js +73 -37
  36. package/src/create-service.action.js +5 -1
  37. package/src/create-service.task.js +7 -7
  38. package/src/decline-booking.action.js +1 -1
  39. package/src/decline-booking.api.js +35 -0
  40. package/src/decline-booking.task.js +15 -18
  41. package/src/delete-service.action.js +1 -1
  42. package/src/delete-service.task.js +12 -15
  43. package/src/en.translations.json +83 -37
  44. package/src/get-availability.action.js +1 -1
  45. package/src/get-availability.task.js +5 -4
  46. package/src/get-available-slots.action.js +1 -1
  47. package/src/get-available-slots.task.js +16 -13
  48. package/src/get-services.action.js +1 -1
  49. package/src/get-services.task.js +13 -6
  50. package/src/home.page.jsx +14 -46
  51. package/src/index.js +17 -0
  52. package/src/list-bookings.action.js +1 -1
  53. package/src/list-bookings.task.js +33 -16
  54. package/src/locations.js +6 -4
  55. package/src/open-availability-setup.action.js +5 -0
  56. package/src/open-bookings.action.js +5 -0
  57. package/src/open-create-service.action.js +5 -0
  58. package/src/open-services.action.js +5 -0
  59. package/src/provider-availability.flow.js +39 -0
  60. package/src/provider-fields.js +34 -0
  61. package/src/provider-onboard.flow.js +25 -0
  62. package/src/public-booking-form.form.js +7 -0
  63. package/src/public-booking-form.schema.js +12 -0
  64. package/src/public-booking.page.jsx +60 -57
  65. package/src/resolve-workspace-contact.js +55 -0
  66. package/src/save-availability.action.js +5 -1
  67. package/src/save-availability.task.js +26 -28
  68. package/src/schema-ids.js +7 -0
  69. package/src/select-duration-30.action.js +4 -0
  70. package/src/select-duration-60.action.js +4 -0
  71. package/src/select-duration-90.action.js +4 -0
  72. package/src/send-booking-reminder.task.js +39 -14
  73. package/src/service-card-slot.component.jsx +4 -4
  74. package/src/service-detail.component.jsx +4 -4
  75. package/src/service.form.js +7 -0
  76. package/src/service.schema.js +14 -0
  77. package/src/services.page.jsx +62 -58
  78. package/src/stories/fixtures.js +149 -0
  79. package/src/sv.translations.json +83 -37
  80. package/src/time.js +27 -0
  81. package/src/toggle-availability-day-0.action.js +4 -0
  82. package/src/toggle-availability-day-1.action.js +4 -0
  83. package/src/toggle-availability-day-2.action.js +4 -0
  84. package/src/toggle-availability-day-3.action.js +4 -0
  85. package/src/toggle-availability-day-4.action.js +4 -0
  86. package/src/toggle-availability-day-5.action.js +4 -0
  87. package/src/toggle-availability-day-6.action.js +4 -0
  88. package/src/update-service.action.js +1 -1
  89. package/src/update-service.task.js +13 -16
  90. package/src/workspace-services.js +5 -0
  91. package/src/moduleStatus.js +0 -2
  92. package/src/service.resource.js +0 -32
@@ -16,6 +16,15 @@
16
16
  "booking.home.features.doubleBook.text": "En kalender här, en länk där – vi håller koll så samma tid inte bokas två gånger.",
17
17
  "booking.sales.overview": "Översikt",
18
18
  "booking.sales.features": "Funktioner",
19
+ "booking.sales.enable": "Aktivera bokning",
20
+ "booking.sales.enableDescription": "Aktivera bokning för ditt workspace så att kunder kan boka tid och du kan hantera möten.",
21
+ "booking.sales.enabling": "Aktiverar…",
22
+ "booking.sales.enableError": "Kunde inte aktivera bokning. Försök igen eller kontakta support.",
23
+ "booking.product.title": "Bokning är aktiv",
24
+ "booking.product.description": "Ange din tillgänglighet, lägg till tjänster och börja ta emot bokningar från kunder.",
25
+ "booking.product.setupAvailability": "Ange tillgänglighet",
26
+ "booking.product.manageServices": "Hantera tjänster",
27
+ "booking.product.viewBookings": "Visa bokningar",
19
28
  "booking/bookings.documentTitle": "Bokningar",
20
29
  "booking.bookings.titleSuffix": "Bokningar",
21
30
  "booking.bookings.description": "Hantera bokningsförfrågningar och bekräftade möten.",
@@ -29,12 +38,15 @@
29
38
  "booking.bookings.empty.all.heading": "Inga bokningar",
30
39
  "booking.bookings.empty.all.body": "Bokningar från dina klienter visas här.",
31
40
  "booking.bookings.loading": "Laddar…",
41
+ "booking.bookings.errorLoad.title": "Kunde inte ladda bokningar",
32
42
  "booking.bookings.errorStatus": "Fel {status}",
33
43
  "booking.bookings.errorConfirm": "Bekräftning misslyckades",
34
44
  "booking.bookings.errorDecline": "Avböjning misslyckades",
35
45
  "booking.bookings.errorCancel": "Avbokning misslyckades",
36
46
  "booking/booking-detail.documentTitle": "Bokningsdetaljer",
37
47
  "booking.bookingDetail.back": "← Alla bokningar",
48
+ "booking.bookingDetail.pageTitle": "Bokning",
49
+ "booking.bookingDetail.pageDescription": "Visa och hantera den här bokningsförfrågan.",
38
50
  "booking.bookingDetail.loading": "Laddar…",
39
51
  "booking.bookingDetail.notFound": "Bokningen hittades inte",
40
52
  "booking.bookingDetail.errorStatus": "Fel {status}",
@@ -146,17 +158,11 @@
146
158
  "publicBooking.durationMin": "({minutes} min)",
147
159
  "publicBooking.continue": "Fortsätt →",
148
160
  "publicBooking.pickDate": "Välj ett markerat datum i kalendern",
149
- "publicBooking.form.name": "Ditt namn",
150
- "publicBooking.form.namePlaceholder": "Anna Svensson",
151
- "publicBooking.form.email": "E-postadress",
152
- "publicBooking.form.emailPlaceholder": "anna@exempel.se",
153
- "publicBooking.form.message": "Meddelande (valfritt)",
154
- "publicBooking.form.messagePlaceholder": "Berätta gärna vad du vill prata om...",
155
161
  "publicBooking.form.submit": "Skicka förfrågan",
156
162
  "publicBooking.form.submitting": "Skickar…",
157
163
  "publicBooking.form.error": "Bokning misslyckades ({status})",
158
164
  "publicBooking.pending.title": "Din förfrågan är skickad!",
159
- "publicBooking.pending.body": "Konsulten bekräftar inom kort. Du får ett mejl med kalenderinbjudan när bokningen är bekräftad.",
165
+ "publicBooking.pending.body": "Bokningsansvarig bekräftar inom kort. Du får ett mejl med kalenderinbjudan när bokningen är bekräftad.",
160
166
  "publicBooking.pending.requestedTime": "Önskad tid",
161
167
  "publicBooking.pending.duration": "{minutes} minuter",
162
168
  "publicBooking.month.january": "Januari",
@@ -194,30 +200,76 @@
194
200
  "booking.card.cancel": "Avboka",
195
201
  "booking.card.waiting": "Väntar…",
196
202
  "booking.card.errorGeneric": "Något gick fel",
197
- "booking/create.label": "Skapa bokning",
198
- "booking/create.description": "Skapa en ny bokningsförfrågan från en klient",
199
- "booking/get-available-slots.label": "Hämta tillgängliga tider",
200
- "booking/get-available-slots.description": "Lista bokningsbara tider för en konsult",
201
- "booking/get-availability.label": "Hämta tillgänglighet",
202
- "booking/get-availability.description": "Ladda veckotillgänglighet för workspace",
203
- "booking/save-availability.label": "Spara tillgänglighet",
204
- "booking/save-availability.description": "Spara veckotillgänglighet och bokningsinställningar",
205
- "booking/list.label": "Lista bokningar",
206
- "booking/list.description": "Lista alla bokningar för aktuellt workspace",
207
- "booking/decline.label": "Avböj bokning",
208
- "booking/decline.description": "Avböj en väntande bokningsförfrågan",
209
- "booking/confirm.label": "Bekräfta bokning",
210
- "booking/confirm.description": "Bekräfta en väntande bokningsförfrågan",
211
- "booking/cancel.label": "Avboka bokning",
212
- "booking/cancel.description": "Avboka en bekräftad bokning",
213
- "booking/get-services.label": "Hämta tjänster",
214
- "booking/get-services.description": "Lista bokningsbara tjänster för konsult eller workspace",
215
- "booking/create-service.label": "Skapa tjänst",
216
- "booking/create-service.description": "Skapa en ny bokningsbar tjänst",
217
- "booking/update-service.label": "Uppdatera tjänst",
218
- "booking/update-service.description": "Uppdatera en befintlig bokningsbar tjänst",
219
- "booking/delete-service.label": "Ta bort tjänst",
220
- "booking/delete-service.description": "Ta bort en bokningsbar tjänst",
203
+ "@ossy/booking/actions/create.label": "Skapa bokning",
204
+ "@ossy/booking/actions/create.description": "Skapa en ny bokningsförfrågan från en klient",
205
+ "@ossy/booking/actions/get-available-slots.label": "Hämta tillgängliga tider",
206
+ "@ossy/booking/actions/get-available-slots.description": "Lista bokningsbara tider för en utförare",
207
+ "@ossy/booking/actions/get-availability.label": "Hämta tillgänglighet",
208
+ "@ossy/booking/actions/get-availability.description": "Ladda veckotillgänglighet för workspace",
209
+ "@ossy/booking/actions/save-availability.label": "Spara tillgänglighet",
210
+ "@ossy/booking/actions/save-availability.description": "Spara veckotillgänglighet och bokningsinställningar",
211
+ "@ossy/booking/actions/list.label": "Lista bokningar",
212
+ "@ossy/booking/actions/list.description": "Lista alla bokningar för aktuellt workspace",
213
+ "@ossy/booking/actions/decline.label": "Avböj bokning",
214
+ "@ossy/booking/actions/decline.description": "Avböj en väntande bokningsförfrågan",
215
+ "@ossy/booking/actions/confirm.label": "Bekräfta bokning",
216
+ "@ossy/booking/actions/confirm.description": "Bekräfta en väntande bokningsförfrågan",
217
+ "@ossy/booking/actions/cancel.label": "Avboka bokning",
218
+ "@ossy/booking/actions/cancel.description": "Avboka en bekräftad bokning",
219
+ "@ossy/booking/actions/get-services.label": "Hämta tjänster",
220
+ "@ossy/booking/actions/get-services.description": "Lista bokningsbara tjänster för utförare eller workspace",
221
+ "@ossy/booking/actions/create-service.label": "Skapa tjänst",
222
+ "@ossy/booking/actions/create-service.description": "Skapa en ny bokningsbar tjänst",
223
+ "@ossy/booking/form/service.label": "Tjänst",
224
+ "@ossy/booking/form/service.description": "Skapa eller redigera en bokningsbar tjänst",
225
+ "@ossy/booking/schema/service.name.label": "Namn",
226
+ "@ossy/booking/schema/service.duration.label": "Längd (minuter)",
227
+ "@ossy/booking/schema/service.price.label": "Pris (öre)",
228
+ "@ossy/booking/schema/service.currency.label": "Valuta",
229
+ "@ossy/booking/schema/service.description.label": "Beskrivning",
230
+ "@ossy/booking/schema/service.active.label": "Aktiv",
231
+ "@ossy/booking/schema/service.active.description": "Synlig för klienter på den publika bokningssidan",
232
+ "@ossy/booking/schema/public-booking-form.clientName.label": "Ditt namn",
233
+ "@ossy/booking/schema/public-booking-form.clientName.placeholder": "Anna Svensson",
234
+ "@ossy/booking/schema/public-booking-form.clientEmail.label": "E-postadress",
235
+ "@ossy/booking/schema/public-booking-form.clientEmail.placeholder": "anna@exempel.se",
236
+ "@ossy/booking/schema/public-booking-form.clientMessage.label": "Meddelande (valfritt)",
237
+ "@ossy/booking/schema/public-booking-form.clientMessage.placeholder": "Berätta gärna vad du vill prata om...",
238
+ "@ossy/booking/schema/public-booking-form.attachments.label": "Bilagor (valfritt)",
239
+ "@ossy/booking/form/public-booking.label": "Bokningsuppgifter",
240
+ "@ossy/booking/form/public-booking.description": "Kunduppgifter för en bokningsförfrågan",
241
+ "@ossy/booking/actions/update-service.label": "Uppdatera tjänst",
242
+ "@ossy/booking/actions/update-service.description": "Uppdatera en befintlig bokningsbar tjänst",
243
+ "@ossy/booking/actions/delete-service.label": "Ta bort tjänst",
244
+ "@ossy/booking/actions/delete-service.description": "Ta bort en bokningsbar tjänst",
245
+ "@ossy/booking/actions/open-availability-setup.label": "Ställ in tillgänglighet",
246
+ "@ossy/booking/actions/open-availability-setup.description": "Öppna tillgänglighetsinställningar",
247
+ "@ossy/booking/actions/open-services.label": "Hantera tjänster",
248
+ "@ossy/booking/actions/open-services.description": "Öppna tjänstesidan",
249
+ "@ossy/booking/actions/open-bookings.label": "Visa bokningar",
250
+ "@ossy/booking/actions/open-bookings.description": "Öppna bokningslistan",
251
+ "@ossy/booking/actions/open-create-service.label": "Lägg till tjänst",
252
+ "@ossy/booking/actions/open-create-service.description": "Öppna formuläret för att skapa en ny tjänst",
253
+ "@ossy/booking/actions/toggle-day-0.label": "Växla söndag",
254
+ "@ossy/booking/actions/toggle-day-0.description": "Aktivera eller inaktivera tillgänglighet på söndag",
255
+ "@ossy/booking/actions/toggle-day-1.label": "Växla måndag",
256
+ "@ossy/booking/actions/toggle-day-1.description": "Aktivera eller inaktivera tillgänglighet på måndag",
257
+ "@ossy/booking/actions/toggle-day-2.label": "Växla tisdag",
258
+ "@ossy/booking/actions/toggle-day-2.description": "Aktivera eller inaktivera tillgänglighet på tisdag",
259
+ "@ossy/booking/actions/toggle-day-3.label": "Växla onsdag",
260
+ "@ossy/booking/actions/toggle-day-3.description": "Aktivera eller inaktivera tillgänglighet på onsdag",
261
+ "@ossy/booking/actions/toggle-day-4.label": "Växla torsdag",
262
+ "@ossy/booking/actions/toggle-day-4.description": "Aktivera eller inaktivera tillgänglighet på torsdag",
263
+ "@ossy/booking/actions/toggle-day-5.label": "Växla fredag",
264
+ "@ossy/booking/actions/toggle-day-5.description": "Aktivera eller inaktivera tillgänglighet på fredag",
265
+ "@ossy/booking/actions/toggle-day-6.label": "Växla lördag",
266
+ "@ossy/booking/actions/toggle-day-6.description": "Aktivera eller inaktivera tillgänglighet på lördag",
267
+ "@ossy/booking/actions/select-duration-30.label": "30 min sessioner",
268
+ "@ossy/booking/actions/select-duration-30.description": "Erbjud 30 minuters sessionslängd",
269
+ "@ossy/booking/actions/select-duration-60.label": "60 min sessioner",
270
+ "@ossy/booking/actions/select-duration-60.description": "Erbjud 60 minuters sessionslängd",
271
+ "@ossy/booking/actions/select-duration-90.label": "90 min sessioner",
272
+ "@ossy/booking/actions/select-duration-90.description": "Erbjud 90 minuters sessionslängd",
221
273
  "booking.services.loading": "Laddar…",
222
274
  "booking.services.empty": "Inga tjänster ännu. Lägg till din första tjänst.",
223
275
  "booking.services.errorLoad": "Kunde inte ladda tjänster ({status})",
@@ -231,12 +283,6 @@
231
283
  "booking.services.cancel": "Avbryt",
232
284
  "booking.services.edit": "Redigera",
233
285
  "booking.services.delete": "Ta bort",
234
- "booking.services.field.name": "Namn",
235
- "booking.services.field.duration": "Längd (minuter)",
236
- "booking.services.field.price": "Pris (öre)",
237
- "booking.services.field.currency": "Valuta",
238
- "booking.services.field.description": "Beskrivning",
239
- "booking.services.field.active": "Aktiv (synlig för klienter)",
240
286
  "publicBooking.chooseService": "Välj tjänst",
241
287
  "publicBooking.loadingServices": "Laddar tjänster…",
242
288
  "publicBooking.servicesError": "Kunde inte ladda tjänster.",
package/src/time.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Canonical time helpers for booking — persisted instants are Unix ms (ADR 0004).
3
+ */
4
+
5
+ /**
6
+ * @param {unknown} value — Unix ms, ISO string, or Date
7
+ * @returns {number | null}
8
+ */
9
+ export function toMs(value) {
10
+ if (value == null || value === '') return null
11
+ if (typeof value === 'number' && Number.isFinite(value)) return value
12
+ if (value instanceof Date && !Number.isNaN(value.getTime())) return value.getTime()
13
+ if (typeof value === 'string') {
14
+ const parsed = Date.parse(value)
15
+ if (!Number.isNaN(parsed)) return parsed
16
+ }
17
+ return null
18
+ }
19
+
20
+ /**
21
+ * @param {number | null | undefined} ms
22
+ * @returns {Date | null}
23
+ */
24
+ export function fromMs(ms) {
25
+ if (ms == null || !Number.isFinite(ms)) return null
26
+ return new Date(ms)
27
+ }
@@ -0,0 +1,4 @@
1
+ export const metadata = {
2
+ id: '@ossy/booking/actions/toggle-day-0',
3
+ access: 'workspace',
4
+ }
@@ -0,0 +1,4 @@
1
+ export const metadata = {
2
+ id: '@ossy/booking/actions/toggle-day-1',
3
+ access: 'workspace',
4
+ }
@@ -0,0 +1,4 @@
1
+ export const metadata = {
2
+ id: '@ossy/booking/actions/toggle-day-2',
3
+ access: 'workspace',
4
+ }
@@ -0,0 +1,4 @@
1
+ export const metadata = {
2
+ id: '@ossy/booking/actions/toggle-day-3',
3
+ access: 'workspace',
4
+ }
@@ -0,0 +1,4 @@
1
+ export const metadata = {
2
+ id: '@ossy/booking/actions/toggle-day-4',
3
+ access: 'workspace',
4
+ }
@@ -0,0 +1,4 @@
1
+ export const metadata = {
2
+ id: '@ossy/booking/actions/toggle-day-5',
3
+ access: 'workspace',
4
+ }
@@ -0,0 +1,4 @@
1
+ export const metadata = {
2
+ id: '@ossy/booking/actions/toggle-day-6',
3
+ access: 'workspace',
4
+ }
@@ -1 +1 @@
1
- export const metadata = { id: 'booking/update-service', access: 'workspace' }
1
+ export const metadata = { id: '@ossy/booking/actions/update-service', access: 'workspace' }
@@ -1,8 +1,8 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Resource, ResourcesEvents } from '@ossy/resources/server'
1
+ import { ResourcesEvents, mutateResource } from '@ossy/resources/server'
2
+ import { BookingSchema } from './schema-ids.js'
3
3
  import { getBookingResources } from './booking-resources.js'
4
4
 
5
- export const metadata = { id: 'booking/update-service' }
5
+ export const metadata = { id: '@ossy/booking/tasks/update-service' }
6
6
 
7
7
  export async function run ({ payload, req, log }) {
8
8
  const workspaceId = req?.workspaceId
@@ -16,7 +16,7 @@ export async function run ({ payload, req, log }) {
16
16
  }
17
17
 
18
18
  const existing = await getBookingResources({
19
- type: '@ossy/booking/service',
19
+ schemaId: BookingSchema.service,
20
20
  belongsTo: workspaceId,
21
21
  })
22
22
 
@@ -35,18 +35,15 @@ export async function run ({ payload, req, log }) {
35
35
  ...(active != null ? { active: active !== false } : {}),
36
36
  }
37
37
 
38
- log?.info(`[booking/update-service] Updating service ${serviceId}`)
39
-
40
- await Aggregate.Of(Resource, serviceId)
41
- .then(
42
- Aggregate.Add(
43
- ResourcesEvents.ContentUpdated({
44
- createdBy: req?.userId ?? 'system',
45
- content,
46
- }),
47
- ),
48
- )
49
- .then(Aggregate.Save())
38
+ log?.info(`[booking/tasks/update-service] Updating service ${serviceId}`)
39
+
40
+ await mutateResource(
41
+ serviceId,
42
+ ResourcesEvents.Patched({
43
+ createdBy: req?.userId ?? 'system',
44
+ content,
45
+ }),
46
+ )
50
47
 
51
48
  return { id: serviceId, ...content }
52
49
  }
@@ -0,0 +1,5 @@
1
+ export function isServiceOn (services, slug) {
2
+ const entry = services?.[slug]
3
+ if (entry != null && typeof entry === 'object') return entry.enabled === true
4
+ return entry === true
5
+ }
@@ -1,2 +0,0 @@
1
- export const moduleStatusTags = (definition) =>
2
- (definition.statuses ?? []).map((status) => ({ label: status }))
@@ -1,32 +0,0 @@
1
- export default {
2
- name: 'Service',
3
- id: '@ossy/booking/service',
4
- categoryName: 'Booking',
5
- icon: 'briefcase',
6
- fields: [
7
- {
8
- name: 'name',
9
- type: 'text',
10
- },
11
- {
12
- name: 'duration',
13
- type: 'number',
14
- },
15
- {
16
- name: 'price',
17
- type: 'number',
18
- },
19
- {
20
- name: 'currency',
21
- type: 'text',
22
- },
23
- {
24
- name: 'description',
25
- type: 'textarea',
26
- },
27
- {
28
- name: 'active',
29
- type: 'boolean',
30
- },
31
- ],
32
- }