@bash-app/bash-common 30.2.0 → 30.4.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/.vscode/settings.json +9 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +146 -93
- package/scripts/symlinks.sh +2 -0
- package/src/definitions.ts +37 -43
- package/src/extendedSchemas.ts +122 -104
- package/src/index.ts +4 -3
- package/src/utils/arrayUtils.ts +8 -0
- package/src/utils/luxonUtils.ts +78 -15
- package/src/utils/service/apiServiceBookingApiUtils.ts +222 -0
- package/src/utils/service/frontendServiceBookingUtils.ts +310 -0
- package/src/utils/service/serviceBookingStatusUtils.ts +96 -27
- package/src/utils/service/serviceBookingTypes.ts +56 -0
- package/src/utils/service/serviceDBUtils.ts +35 -35
- package/src/utils/service/serviceRateDBUtils.ts +179 -179
- package/src/utils/service/serviceRateTypes.ts +18 -0
- package/src/utils/service/serviceRateUtils.ts +38 -64
- package/src/utils/service/serviceUtils.ts +53 -21
- package/src/utils/stringUtils.ts +1 -1
- package/src/utils/service/serviceBookingApiUtils.ts +0 -259
- package/src/utils/service/serviceBookingUtils.ts +0 -391
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
Service,
|
|
3
|
+
ServiceCancellationPolicy as ServiceCancellationPolicyOption,
|
|
3
4
|
ServiceSubscriptionStatus,
|
|
4
5
|
ServiceTypes,
|
|
5
6
|
} from "@prisma/client";
|
|
6
7
|
import { ServiceExt } from "../../extendedSchemas";
|
|
7
8
|
|
|
8
|
-
export type
|
|
9
|
+
export type ServiceCancellationRefundPolicy = {
|
|
9
10
|
days?: number;
|
|
10
11
|
hours?: number;
|
|
11
12
|
refundPercentage: number;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
|
-
export type
|
|
15
|
+
export type ServiceCancellationPolicyData = {
|
|
15
16
|
name: string;
|
|
16
17
|
description?: string;
|
|
17
|
-
refundPolicy:
|
|
18
|
+
refundPolicy: ServiceCancellationRefundPolicy[];
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
export type
|
|
21
|
-
[key in
|
|
21
|
+
export type ServiceCancellationPolicyMap = {
|
|
22
|
+
[key in ServiceCancellationPolicyOption]: ServiceCancellationPolicyData;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
|
-
export const
|
|
25
|
-
[
|
|
25
|
+
export const SERVICE_CANCELLATION_POLICY_DATA: ServiceCancellationPolicyMap = {
|
|
26
|
+
[ServiceCancellationPolicyOption.None]: {
|
|
26
27
|
name: "None",
|
|
27
28
|
refundPolicy: [],
|
|
28
29
|
description: "No cancellation policy.",
|
|
29
30
|
},
|
|
30
|
-
[
|
|
31
|
+
[ServiceCancellationPolicyOption.VeryFlexible]: {
|
|
31
32
|
name: "Very Flexible",
|
|
32
33
|
refundPolicy: [
|
|
33
34
|
{
|
|
@@ -36,7 +37,7 @@ export const SERVICE_CANCELATION_POLICY_DATA: ServiceCancelationPolicyMap = {
|
|
|
36
37
|
},
|
|
37
38
|
],
|
|
38
39
|
},
|
|
39
|
-
[
|
|
40
|
+
[ServiceCancellationPolicyOption.Flexible]: {
|
|
40
41
|
name: "Flexible",
|
|
41
42
|
refundPolicy: [
|
|
42
43
|
{
|
|
@@ -49,7 +50,7 @@ export const SERVICE_CANCELATION_POLICY_DATA: ServiceCancelationPolicyMap = {
|
|
|
49
50
|
},
|
|
50
51
|
],
|
|
51
52
|
},
|
|
52
|
-
[
|
|
53
|
+
[ServiceCancellationPolicyOption.Standard30Day]: {
|
|
53
54
|
name: "Standard 30 Day",
|
|
54
55
|
refundPolicy: [
|
|
55
56
|
{
|
|
@@ -62,7 +63,7 @@ export const SERVICE_CANCELATION_POLICY_DATA: ServiceCancelationPolicyMap = {
|
|
|
62
63
|
},
|
|
63
64
|
],
|
|
64
65
|
},
|
|
65
|
-
[
|
|
66
|
+
[ServiceCancellationPolicyOption.Standard90Day]: {
|
|
66
67
|
name: "Very Flexible",
|
|
67
68
|
refundPolicy: [
|
|
68
69
|
{
|
|
@@ -78,7 +79,7 @@ export const SERVICE_CANCELATION_POLICY_DATA: ServiceCancelationPolicyMap = {
|
|
|
78
79
|
} as const;
|
|
79
80
|
|
|
80
81
|
function generateDescription(
|
|
81
|
-
refundPolicy:
|
|
82
|
+
refundPolicy: ServiceCancellationRefundPolicy[]
|
|
82
83
|
): string {
|
|
83
84
|
const descriptions = refundPolicy.map((policy, index) => {
|
|
84
85
|
const unit = policy.days ? "days" : "hours";
|
|
@@ -110,16 +111,16 @@ function generateDescription(
|
|
|
110
111
|
return descriptions.join(" ");
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
Object.keys(
|
|
114
|
+
Object.keys(SERVICE_CANCELLATION_POLICY_DATA)
|
|
114
115
|
.filter(
|
|
115
116
|
(policyKey) =>
|
|
116
|
-
(policyKey as
|
|
117
|
-
|
|
117
|
+
(policyKey as ServiceCancellationPolicyOption) !=
|
|
118
|
+
ServiceCancellationPolicyOption.None
|
|
118
119
|
)
|
|
119
120
|
.forEach((policyKey) => {
|
|
120
121
|
const policy =
|
|
121
|
-
|
|
122
|
-
policyKey as
|
|
122
|
+
SERVICE_CANCELLATION_POLICY_DATA[
|
|
123
|
+
policyKey as ServiceCancellationPolicyOption
|
|
123
124
|
];
|
|
124
125
|
policy.description = generateDescription(policy.refundPolicy);
|
|
125
126
|
});
|
|
@@ -135,6 +136,39 @@ export function serviceSubscriptionIsActive(
|
|
|
135
136
|
).includes(status);
|
|
136
137
|
}
|
|
137
138
|
|
|
139
|
+
export interface ServiceSpecificTypeInfo {
|
|
140
|
+
serviceId: string;
|
|
141
|
+
serviceType: ServiceTypes;
|
|
142
|
+
specificFieldName: ServiceSpecificName;
|
|
143
|
+
specificId: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function serviceGetSpecificType<BookingType extends Service>(
|
|
147
|
+
service: BookingType
|
|
148
|
+
): ServiceSpecificTypeInfo | null {
|
|
149
|
+
if (service.serviceType == null) {
|
|
150
|
+
// throw new Error(`serviceGetSpecificType invalid serviceType`);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const specificFieldName = serviceTypeToField(service.serviceType);
|
|
155
|
+
if (specificFieldName == null) {
|
|
156
|
+
// throw new Error(`serviceGetSpecificType invalid serviceType`);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const specificId = service[
|
|
161
|
+
`${specificFieldName}Id` as keyof BookingType
|
|
162
|
+
] as string;
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
serviceId: service.id,
|
|
166
|
+
serviceType: service.serviceType,
|
|
167
|
+
specificFieldName: specificFieldName,
|
|
168
|
+
specificId: specificId,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
138
172
|
export function serviceDetailUrl(
|
|
139
173
|
serviceId: string,
|
|
140
174
|
serviceType: string,
|
|
@@ -178,8 +212,6 @@ export const specificServiceMap: Record<ServiceTypes, ServiceSpecificName> = {
|
|
|
178
212
|
Organizations: "organization",
|
|
179
213
|
} as const;
|
|
180
214
|
|
|
181
|
-
|
|
182
|
-
serviceType: ServiceTypes
|
|
183
|
-
): ServiceSpecificName => {
|
|
215
|
+
const serviceTypeToField = (serviceType: ServiceTypes): ServiceSpecificName => {
|
|
184
216
|
return specificServiceMap[serviceType];
|
|
185
217
|
};
|
package/src/utils/stringUtils.ts
CHANGED
|
@@ -2,7 +2,7 @@ export function formatString<T extends Record<string, string | number>>(
|
|
|
2
2
|
template: string,
|
|
3
3
|
data: T
|
|
4
4
|
): string {
|
|
5
|
-
return template.replace(
|
|
5
|
+
return template.replace(/\${(\w+)}/g, (_, key) => {
|
|
6
6
|
return data[key] !== undefined ? data[key].toString() : "";
|
|
7
7
|
});
|
|
8
8
|
}
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ServiceBookingApiParamsLuxon,
|
|
3
|
-
ServiceBookingApiParams,
|
|
4
|
-
ServiceBookedDayApiParamsLuxon,
|
|
5
|
-
ServiceAddonApiParams,
|
|
6
|
-
ServiceBookedDayApiParams,
|
|
7
|
-
ServicePriceToBookApiResult,
|
|
8
|
-
} from "../../definitions";
|
|
9
|
-
import {
|
|
10
|
-
ServiceAddonExt,
|
|
11
|
-
ServiceBookingAddOnExt,
|
|
12
|
-
ServiceBookingDayExt,
|
|
13
|
-
ServiceBookingExt,
|
|
14
|
-
ServiceExt,
|
|
15
|
-
} from "../../extendedSchemas";
|
|
16
|
-
import {
|
|
17
|
-
dateTimeFromDate,
|
|
18
|
-
dateTimeFromString,
|
|
19
|
-
dateTimeRangeFromDates,
|
|
20
|
-
} from "../luxonUtils";
|
|
21
|
-
import { convertDollarsToCents } from "../paymentUtils";
|
|
22
|
-
import {
|
|
23
|
-
ServiceAttendeeOption,
|
|
24
|
-
serviceAttendeeOptions,
|
|
25
|
-
} from "./attendeeOptionUtils";
|
|
26
|
-
import {
|
|
27
|
-
ServiceGetPriceToBookFeesParams,
|
|
28
|
-
ServiceBookingDayInfoParams,
|
|
29
|
-
ServiceBookingFee,
|
|
30
|
-
ServiceAddonInput,
|
|
31
|
-
ServiceGetPriceToBookResult,
|
|
32
|
-
serviceGetPriceToBookFees,
|
|
33
|
-
serviceGetPriceToBookExt,
|
|
34
|
-
ServiceGetPriceToBookExtDay,
|
|
35
|
-
} from "./serviceBookingUtils";
|
|
36
|
-
|
|
37
|
-
export type ServiceCantBookReason = {
|
|
38
|
-
type: string;
|
|
39
|
-
msg: string;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export const ServiceCantBookReasons = {
|
|
43
|
-
notInBusinessHours: {
|
|
44
|
-
type: "notInBusinessHours",
|
|
45
|
-
msg: "Time not within normal Business Hours",
|
|
46
|
-
} as ServiceCantBookReason,
|
|
47
|
-
minimumTimeBlock: {
|
|
48
|
-
type: "minimumTimeBlock",
|
|
49
|
-
msg: "Minimum time block not satisfied",
|
|
50
|
-
} as ServiceCantBookReason,
|
|
51
|
-
dateBlocked: {
|
|
52
|
-
type: "dateBlocked",
|
|
53
|
-
msg: "The selected date is blocked.",
|
|
54
|
-
} as ServiceCantBookReason,
|
|
55
|
-
alreadyBooked: {
|
|
56
|
-
type: "alreadyBooked",
|
|
57
|
-
msg: "This service has already been booked.",
|
|
58
|
-
} as ServiceCantBookReason,
|
|
59
|
-
requestDeclined: {
|
|
60
|
-
type: "requestDeclined",
|
|
61
|
-
msg: "The booking request has been declined.",
|
|
62
|
-
} as ServiceCantBookReason,
|
|
63
|
-
} as const;
|
|
64
|
-
|
|
65
|
-
export type ServiceCantBookReasons = keyof typeof ServiceCantBookReasons;
|
|
66
|
-
|
|
67
|
-
export function apiTransformAddOnsToAddonInputs<
|
|
68
|
-
AddonParams_t extends ServiceAddonApiParams
|
|
69
|
-
>(
|
|
70
|
-
addOnsApi: AddonParams_t[],
|
|
71
|
-
serviceAddons: ServiceAddonExt[]
|
|
72
|
-
): ServiceAddonInput[] {
|
|
73
|
-
// Create a Map for faster lookup by addonId
|
|
74
|
-
const addonMap = new Map<string, ServiceAddonExt>(
|
|
75
|
-
serviceAddons.map((addon) => [addon.id, addon])
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
return addOnsApi.map((addOnApi) => {
|
|
79
|
-
const matchedAddon = addonMap.get(addOnApi.addonId);
|
|
80
|
-
if (!matchedAddon) {
|
|
81
|
-
throw new Error(`Failed to match addon with id: ${addOnApi.addonId}`);
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
...matchedAddon,
|
|
85
|
-
...addOnApi,
|
|
86
|
-
} as ServiceAddonInput;
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// export function serviceAddonInputAddOnsToAddonInputs<
|
|
91
|
-
// AddonParams_t extends ServiceAddonInput
|
|
92
|
-
// >(
|
|
93
|
-
// addOnsApi: AddonParams_t[],
|
|
94
|
-
// serviceAddons: ServiceAddonExt[]
|
|
95
|
-
// ): ServiceAddonInput[] {
|
|
96
|
-
// // Create a Map for faster lookup by addonId
|
|
97
|
-
// const addonMap = new Map<string, ServiceAddonExt>(
|
|
98
|
-
// serviceAddons.map((addon) => [addon.id, addon])
|
|
99
|
-
// );
|
|
100
|
-
|
|
101
|
-
// return addOnsApi.map((addOnApi) => {
|
|
102
|
-
// const matchedAddon = addonMap.get(addOnApi.id);
|
|
103
|
-
// if (!matchedAddon) {
|
|
104
|
-
// throw new Error(`Failed to match addon with id: ${addOnApi.id}`);
|
|
105
|
-
// }
|
|
106
|
-
// return {
|
|
107
|
-
// ...matchedAddon,
|
|
108
|
-
// ...addOnApi,
|
|
109
|
-
// } as ServiceAddonInput;
|
|
110
|
-
// });
|
|
111
|
-
// }
|
|
112
|
-
|
|
113
|
-
// export function apiTransformBookedDays(
|
|
114
|
-
// bookedDays: ServiceBookedDayApiParams[],
|
|
115
|
-
// bookingId: string,
|
|
116
|
-
// serviceAddons: ServiceAddonExt[]
|
|
117
|
-
// ): ServiceBookingDayExt[] {
|
|
118
|
-
// return bookedDays.map((day) => {
|
|
119
|
-
// // Transform the addOns using the helper function and then map to the expected output
|
|
120
|
-
// const addOns = apiTransformAddOnsToAddonInputs(
|
|
121
|
-
// day.addOns,
|
|
122
|
-
// serviceAddons
|
|
123
|
-
// ).map(
|
|
124
|
-
// (addOn) =>
|
|
125
|
-
// ({
|
|
126
|
-
// addOnId: addOn.id,
|
|
127
|
-
// chosenQuantity: addOn.chosenQuantity,
|
|
128
|
-
// serviceBookingDayId: bookingId,
|
|
129
|
-
// costATBCents: convertDollarsToCents(addOn.price),
|
|
130
|
-
// } as ServiceBookingAddOnExt)
|
|
131
|
-
// );
|
|
132
|
-
|
|
133
|
-
// return {
|
|
134
|
-
// id: "",
|
|
135
|
-
// startDate: dateTimeFromString(day.startDate).toJSDate(),
|
|
136
|
-
// endDate: dateTimeFromString(day.endDate).toJSDate(),
|
|
137
|
-
// serviceBookingRequestId: bookingId,
|
|
138
|
-
// addOns: addOns,
|
|
139
|
-
// packages: [],
|
|
140
|
-
// } as ServiceBookingDayExt;
|
|
141
|
-
// });
|
|
142
|
-
// }
|
|
143
|
-
|
|
144
|
-
export function apiTransformBookedDays(
|
|
145
|
-
priceToBook: ServicePriceToBookApiResult
|
|
146
|
-
): Partial<ServiceBookingDayExt>[] {
|
|
147
|
-
return priceToBook.daysToBook.map((day): Partial<ServiceBookingDayExt> => {
|
|
148
|
-
// Transform addOns using the helper function, then map to the expected output.
|
|
149
|
-
const addOns = day.addOns.map(
|
|
150
|
-
(addOn): Partial<ServiceBookingAddOnExt> => ({
|
|
151
|
-
addOnId: addOn.id,
|
|
152
|
-
chosenQuantity: addOn.chosenQuantity ?? 0,
|
|
153
|
-
addOn: addOn,
|
|
154
|
-
// costATBCents: convertDollarsToCents(addOn.price),
|
|
155
|
-
})
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
startDate: day.dateTimeRange.start.toJSDate(),
|
|
160
|
-
endDate: day.dateTimeRange.end.toJSDate(),
|
|
161
|
-
addOns: addOns,
|
|
162
|
-
packages: [],
|
|
163
|
-
// costATBCents: convertDollarsToCents(day.totalBeforeTaxes),
|
|
164
|
-
} as Partial<ServiceBookingDayExt>;
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function serviceGetPriceToBookFromBooking(
|
|
169
|
-
service: ServiceExt,
|
|
170
|
-
booking: ServiceBookingExt
|
|
171
|
-
): ServiceGetPriceToBookResult {
|
|
172
|
-
const daysToBook = booking.bookedDays.map((day) => {
|
|
173
|
-
// Transform addOns back to the original format
|
|
174
|
-
const addOns =
|
|
175
|
-
day.addOns?.map(
|
|
176
|
-
(addOn): ServiceAddonInput => ({
|
|
177
|
-
...addOn.addOn,
|
|
178
|
-
id: addOn.addOnId!,
|
|
179
|
-
chosenQuantity: addOn.chosenQuantity,
|
|
180
|
-
// Reconstruct other addOn properties from the stored addOn object
|
|
181
|
-
})
|
|
182
|
-
) ?? [];
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
dateRange: dateTimeRangeFromDates(
|
|
186
|
-
day.startDate,
|
|
187
|
-
day.endDate,
|
|
188
|
-
"UTC",
|
|
189
|
-
booking.timezone
|
|
190
|
-
),
|
|
191
|
-
addOns: addOns,
|
|
192
|
-
// totalBeforeTaxes: day.costATBCents ? convertCentsToDollars(day.costATBCents) : 0,
|
|
193
|
-
} as ServiceGetPriceToBookExtDay;
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
return serviceGetPriceToBookExt(service.serviceRatesAssociation, {
|
|
197
|
-
bookedDays: daysToBook,
|
|
198
|
-
selectedAttendeeOption: serviceAttendeeOptions[0],
|
|
199
|
-
timezone: booking.timezone,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Main function that transforms service booking parameters to price-to-book parameters
|
|
204
|
-
export function serviceBookingParamsToPriceToBookParams(
|
|
205
|
-
params: ServiceBookingApiParamsLuxon,
|
|
206
|
-
serviceAddons: ServiceAddonExt[]
|
|
207
|
-
): ServiceGetPriceToBookFeesParams {
|
|
208
|
-
// Map each booked day to the required parameters
|
|
209
|
-
const daysToBookParams: ServiceBookingDayInfoParams[] = params.bookedDays.map(
|
|
210
|
-
(day) => {
|
|
211
|
-
// Use the helper function to transform the day's addOns
|
|
212
|
-
const addOnsWithDetails = apiTransformAddOnsToAddonInputs(
|
|
213
|
-
day.addOns,
|
|
214
|
-
serviceAddons
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
dateTimeRange: day.dateTimeRange,
|
|
219
|
-
fees: addOnsWithDetails.map(
|
|
220
|
-
(addon): ServiceBookingFee => ({
|
|
221
|
-
name: `${addon.name} Fee`,
|
|
222
|
-
type: "addOnFees",
|
|
223
|
-
fee: (addon.chosenQuantity || 0) * (addon.price || 0),
|
|
224
|
-
})
|
|
225
|
-
),
|
|
226
|
-
} as ServiceBookingDayInfoParams;
|
|
227
|
-
}
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
return {
|
|
231
|
-
daysToBookParams: daysToBookParams,
|
|
232
|
-
selectedAttendeeOption: {} as ServiceAttendeeOption, // Placeholder: update with actual business logic
|
|
233
|
-
timezone: params.timezone,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export function serviceBookingParamsToLuxon(
|
|
238
|
-
params: ServiceBookingApiParams
|
|
239
|
-
): ServiceBookingApiParamsLuxon {
|
|
240
|
-
const bookedDays = params.bookedDays.reduce(
|
|
241
|
-
(sofar, day): ServiceBookedDayApiParamsLuxon[] => {
|
|
242
|
-
sofar.push({
|
|
243
|
-
...day,
|
|
244
|
-
dateTimeRange: {
|
|
245
|
-
start: dateTimeFromString(day.startDate),
|
|
246
|
-
end: dateTimeFromString(day.endDate),
|
|
247
|
-
},
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
return sofar;
|
|
251
|
-
},
|
|
252
|
-
[] as ServiceBookedDayApiParamsLuxon[]
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
return {
|
|
256
|
-
...params,
|
|
257
|
-
bookedDays,
|
|
258
|
-
};
|
|
259
|
-
}
|