@bash-app/bash-common 30.48.0 → 30.50.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/README.md CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bash-app/bash-common",
3
- "version": "30.48.0",
3
+ "version": "30.50.0",
4
4
  "description": "Common data and scripts to use on the frontend and backend",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -24,12 +24,12 @@
24
24
  "@testing-library/jest-dom": "^6.6.3",
25
25
  "@types/qrcode": "^1.5.5",
26
26
  "lodash-es": "^4.17.21",
27
+ "mixpanel-browser": "^2.65.0",
27
28
  "npm": "^10.5.0",
28
29
  "qrcode": "^1.5.3"
29
30
  },
30
31
  "peerDependencies": {
31
32
  "@prisma/client": "^6.1.0",
32
- "dayjs": "^1.11.13",
33
33
  "luxon": "^3.5.0",
34
34
  "prisma": "^6.1.0",
35
35
  "react-tailwindcss-datepicker": "^1.6.6",
@@ -38,6 +38,7 @@
38
38
  "devDependencies": {
39
39
  "@types/jest": "^29.5.5",
40
40
  "@types/luxon": "^3.4.2",
41
+ "@types/mixpanel-browser": "^2.60.0",
41
42
  "@types/node": "^20.11.1",
42
43
  "@types/react": "^18.3.2",
43
44
  "@types/shelljs": "^0.8.15",
@@ -262,6 +262,11 @@ model Invitation {
262
262
  enum BashEventSource {
263
263
  Bash
264
264
  Eventbrite
265
+ Insomniac
266
+ UtahValley
267
+ LinkedIn
268
+ Meetup
269
+ Facebook
265
270
  }
266
271
 
267
272
  model BashEvent {
@@ -751,7 +756,14 @@ enum Education {
751
756
  enum UserIntent {
752
757
  EventSeeker
753
758
  EventOrganizer
759
+ Volunteer
760
+ EntertainmentService
761
+ EventService
762
+ Vendor
763
+ Sponsor
764
+ Exhibitor
754
765
  VenueOwner
766
+ OrganizationOwner
755
767
  Lonely
756
768
  }
757
769
 
@@ -1115,6 +1127,8 @@ model User {
1115
1127
  trendingBashThreshold Int @default(10) // Min attendees to trigger notification
1116
1128
  organizerUser UserFollowing[] @relation("FollowingOrganizers")
1117
1129
  favorites UserFavorite[]
1130
+
1131
+ vendorBid VendorBid[]
1118
1132
  }
1119
1133
 
1120
1134
  model UserPreferences {
@@ -1123,68 +1137,90 @@ model UserPreferences {
1123
1137
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1124
1138
 
1125
1139
  // Notification Preferences
1126
- emailNotifications Boolean @default(true)
1127
- pushNotifications Boolean @default(true)
1128
- smsNotifications Boolean @default(false)
1129
- newMessageNotify Boolean @default(true)
1130
- eventReminderNotify Boolean @default(true)
1131
- friendRequestNotify Boolean @default(true)
1132
- commentNotify Boolean @default(true)
1133
- invitationNotify Boolean @default(true)
1134
- eventUpdatesNotify Boolean @default(true)
1135
- servicePromotionsNotify Boolean @default(true)
1136
- trendingBashesNotify Boolean @default(false) // New field
1137
- organizerUpdatesNotify Boolean @default(false) // New field
1140
+ emailNotifications Boolean @default(true)
1141
+ pushNotifications Boolean @default(true)
1142
+ marketingEmails Boolean @default(true) // Added from our UI
1143
+ eventUpdates Boolean @default(true) // Added from our UI
1144
+ serviceBookingNotifications Boolean @default(true) // Added from our UI
1145
+ reviewReminders Boolean @default(true) // Added from our UI
1146
+ newFollowerNotifications Boolean @default(true) // Added from our UI
1147
+ trendingBashesNotify Boolean @default(true) // Updated default to true
1148
+ organizerUpdatesNotify Boolean @default(true) // Updated default to true
1149
+ recurringReminders Boolean @default(true) // NEW - multiple reminders as event approaches
1150
+ eventSuggestions Boolean @default(true) // NEW - event planning tips and suggestions
1151
+ serviceSuggestions Boolean @default(true) // NEW - vendor/service recommendations
1152
+ eventReminderNotify Boolean @default(true)
1153
+ newMessageNotify Boolean @default(true)
1154
+ invitationNotify Boolean @default(true)
1155
+ commentNotify Boolean @default(true)
1156
+ servicePromotionsNotify Boolean @default(true)
1138
1157
 
1139
1158
  // Privacy Settings
1140
- profileVisibility String @default("PUBLIC") // PUBLIC, FRIENDS, PRIVATE
1141
- showOnlineStatus Boolean @default(true)
1142
- allowTagging Boolean @default(true)
1143
- allowDirectMessages Boolean @default(true)
1144
- showActivityStatus Boolean @default(true)
1145
- hiddenBashIds String[] @default([])
1146
- hideActivitySection Boolean @default(false)
1147
- allowLocationSharing Boolean @default(false)
1148
- blockSearchEngineIndex Boolean @default(false)
1159
+ profileVisibility String @default("public") // Updated to lowercase, matches our UI
1160
+ eventHistoryVisibility Boolean @default(true) // Added from our UI
1161
+ servicesVisibility Boolean @default(true) // Added from our UI
1162
+ searchVisibility Boolean @default(true) // Added from our UI
1163
+ hideFollowLists Boolean @default(false) // NEW - hide follower/following lists
1164
+ attendancePrivacy String @default("public") // Updated to lowercase, matches our UI
1165
+ hideEventPrices Boolean @default(false) // NEW - hide prices until day of event
1166
+ showOnlineStatus Boolean @default(true)
1167
+ allowDirectMessages Boolean @default(true)
1168
+ blockSearchEngineIndex Boolean @default(false)
1169
+
1170
+ // Contact Management (NEW SECTION)
1171
+ autoAddEventAttendees Boolean @default(true) // NEW - auto-add event attendees to contacts
1172
+ trackFirstEventAttended Boolean @default(true) // NEW - remember which bash connected you
1173
+ autoAddServiceClients Boolean @default(true) // NEW - auto-add service clients to contacts
1149
1174
 
1150
1175
  // UI/UX Preferences
1151
- theme String @default("SYSTEM") // LIGHT, DARK, SYSTEM
1152
- language String @default("en-US")
1176
+ theme String @default("system") // Updated to lowercase, matches our UI
1177
+ language String @default("en") // Simplified from en-US
1153
1178
  timeZone String @default("America/New_York")
1154
- defaultLandingPage String @default("dashboard")
1155
- contentDensity String @default("COMFORTABLE") // COMPACT, COMFORTABLE, SPACIOUS
1156
- fontScale String @default("MEDIUM") // SMALL, MEDIUM, LARGE
1157
- animationsEnabled Boolean @default(true)
1158
- useHighContrastMode Boolean @default(false)
1159
-
1160
- // Content Preferences
1161
- contentFilters String[] @default([])
1162
- topicInterests String[] @default([])
1163
- hideSeenContent Boolean @default(false)
1164
- autoplayVideos Boolean @default(true)
1165
- contentSortPreference String @default("RECENT") // RECENT, POPULAR, RELEVANT
1166
- contentLanguages String[] @default(["en"])
1167
- showSensitiveContent Boolean @default(false)
1168
-
1169
- // Calendar & Event Preferences
1170
- defaultCalendarView String @default("MONTH") // DAY, WEEK, MONTH, AGENDA
1171
- calendarStartDay Int @default(0) // 0=Sunday, 1=Monday
1172
- defaultEventReminder Int @default(60) // Minutes before event
1173
- attendancePrivacy String @default("PUBLIC") // PUBLIC, FRIENDS, PRIVATE
1174
-
1175
- // Communications Preferences
1176
- communicationFrequency String @default("NORMAL") // LOW, NORMAL, HIGH
1177
- preferredContactMethod String @default("EMAIL") // EMAIL, PUSH, SMS
1178
-
1179
- // Data & Payment Preferences
1180
- dataUsageConsent Boolean @default(true)
1181
- analyticsConsent Boolean @default(true)
1179
+
1180
+ // Event Preferences
1181
+ eventReminderTiming String @default("1day") // NEW - 1hour, 1day, 1week (replaces defaultEventReminder)
1182
+ locationRadius Int @default(50) // NEW - miles for event suggestions
1183
+ calendarStartDay Int @default(0) // 0=Sunday, 1=Monday
1184
+ defaultCalendarView String @default("MONTH") // DAY, WEEK, MONTH, AGENDA
1185
+
1186
+ // Security & Consent Preferences
1187
+ biometricAuthEnabled Boolean @default(false) // NEW - Face ID/Touch ID/Fingerprint
1188
+ dataUsageConsent Boolean @default(true)
1189
+ analyticsConsent Boolean @default(true)
1190
+
1191
+ // Payment Preferences
1182
1192
  preferredCurrency String @default("USD")
1183
1193
  savePaymentInfo Boolean @default(false)
1184
1194
  showEventPrices Boolean @default(true)
1185
1195
 
1186
1196
  createdAt DateTime @default(now())
1187
1197
  updatedAt DateTime @updatedAt
1198
+
1199
+ // Less Relevant Fields (moved to bottom)
1200
+ smsNotifications Boolean @default(false) // We're not using SMS
1201
+ friendRequestNotify Boolean @default(true) // We use followers, not friends
1202
+ allowTagging Boolean @default(true)
1203
+ showActivityStatus Boolean @default(true)
1204
+ hiddenBashIds String[] @default([])
1205
+ hideActivitySection Boolean @default(false)
1206
+ allowLocationSharing Boolean @default(false)
1207
+ defaultLandingPage String @default("dashboard")
1208
+ contentDensity String @default("COMFORTABLE") // COMPACT, COMFORTABLE, SPACIOUS
1209
+ fontScale String @default("MEDIUM") // SMALL, MEDIUM, LARGE
1210
+ animationsEnabled Boolean @default(true)
1211
+ useHighContrastMode Boolean @default(false)
1212
+ contentFilters String[] @default([])
1213
+ topicInterests String[] @default([])
1214
+ hideSeenContent Boolean @default(false)
1215
+ autoplayVideos Boolean @default(true)
1216
+ contentSortPreference String @default("RECENT") // RECENT, POPULAR, RELEVANT
1217
+ contentLanguages String[] @default(["en"])
1218
+ showSensitiveContent Boolean @default(false)
1219
+ defaultEventReminder Int @default(60) // Minutes before event (replaced by eventReminderTiming)
1220
+ communicationFrequency String @default("NORMAL") // LOW, NORMAL, HIGH
1221
+ preferredContactMethod String @default("EMAIL") // EMAIL, PUSH, SMS
1222
+
1223
+ @@map("user_preferences")
1188
1224
  }
1189
1225
 
1190
1226
  model UserFollowing {
@@ -1602,9 +1638,58 @@ model Vendor {
1602
1638
  subType String?
1603
1639
  otherSubType String?
1604
1640
  detailsText String?
1605
- service Service?
1641
+
1642
+ // Booth fee range fields
1643
+ minBoothFeeCents Int? @default(0)
1644
+ maxBoothFeeCents Int? @default(0)
1645
+ preferredBoothFeeCents Int? @default(0)
1646
+
1647
+ service Service?
1648
+ vendorBids VendorBid[] // Relation to bids
1606
1649
  }
1607
1650
 
1651
+ model VendorBid {
1652
+ id String @id @default(cuid())
1653
+ vendorId String
1654
+ vendor Vendor @relation(fields: [vendorId], references: [id], onDelete: Cascade)
1655
+
1656
+ // Host (bidder) information
1657
+ userId String
1658
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1659
+
1660
+ // Bid details
1661
+ bidAmountCents Int
1662
+ eventDetails String @db.Text
1663
+ eventDate DateTime?
1664
+ expectedAttendees Int?
1665
+
1666
+ // Bid status
1667
+ status VendorBidStatus @default(Pending)
1668
+ respondedAt DateTime?
1669
+ responseMessage String?
1670
+
1671
+ // Payment tracking (if accepted)
1672
+ stripePaymentIntentId String?
1673
+ paidAt DateTime?
1674
+
1675
+ createdAt DateTime @default(now())
1676
+ updatedAt DateTime @updatedAt
1677
+
1678
+ @@index([vendorId])
1679
+ @@index([userId])
1680
+ @@index([status])
1681
+ }
1682
+
1683
+ enum VendorBidStatus {
1684
+ Pending
1685
+ Accepted
1686
+ Rejected
1687
+ Withdrawn
1688
+ Expired
1689
+ Paid
1690
+ }
1691
+
1692
+
1608
1693
  model Exhibitor {
1609
1694
  id String @id @default(cuid())
1610
1695
  serviceRangeId String?
@@ -1875,6 +1960,42 @@ enum EventManagerFormat {
1875
1960
  MultiVenueManager
1876
1961
  }
1877
1962
 
1963
+ enum LightCleaningFormat {
1964
+ PreEventSetup
1965
+ DuringEventMaintenance
1966
+ PostEventCleanup
1967
+ VenueWipeDown
1968
+ TrashRemoval
1969
+ BathroomMaintenance
1970
+ DiningAreaCleanup
1971
+ KitchenBasics
1972
+ FloorSweeping
1973
+ SurfaceCleaning
1974
+ WindowCleaning
1975
+ TableReset
1976
+ ChairArrangement
1977
+ BasicVacuuming
1978
+ SpotCleaning
1979
+ }
1980
+
1981
+ enum DeepCleaningFormat {
1982
+ PreEventDeepClean
1983
+ PostEventRestoration
1984
+ CarpetCleaning
1985
+ FloorStripping
1986
+ WindowWashing
1987
+ KitchenDeepClean
1988
+ BathroomSanitization
1989
+ UpholsteryClean
1990
+ VenueDisinfection
1991
+ EquipmentCleaning
1992
+ HighDusting
1993
+ PressureWashing
1994
+ GraffitiRemoval
1995
+ FloorWaxing
1996
+ DetailedSanitization
1997
+ }
1998
+
1878
1999
  enum VirtualAssistantFormat {
1879
2000
  EventScheduling
1880
2001
  VendorCoordination
@@ -3111,6 +3232,11 @@ model ServiceBooking {
3111
3232
  bashEventId String?
3112
3233
  bashEvent BashEvent? @relation(fields: [bashEventId], references: [id])
3113
3234
 
3235
+ // Vendor-specific booking fields
3236
+ isVendorBid Boolean @default(false)
3237
+ vendorBidAmountCents Int? // Custom bid amount for vendor bookings
3238
+ vendorEventDetails String? // Additional event details for vendor
3239
+
3114
3240
  @@index([status])
3115
3241
  @@index([creatorId])
3116
3242
  @@index([forUserId])
@@ -721,6 +721,7 @@ export const INVITATION_DATA_TO_INCLUDE = {
721
721
  export interface InvitationExtraData extends Invitation {
722
722
  isFreeGuest: boolean;
723
723
  isOrganizer?: boolean;
724
+ isOwnershipTransfer?: boolean;
724
725
  }
725
726
 
726
727
  export interface AssociatedBashExt extends AssociatedBash {
File without changes
File without changes
File without changes
@@ -1,19 +1,14 @@
1
+ import { DateTime } from "luxon";
1
2
  import { DateType, DateValueType } from "react-tailwindcss-datepicker";
2
- import dayjs, { Dayjs } from "dayjs";
3
- import dayjsUtc from "dayjs/plugin/utc";
4
- import dayjsTimeZone from "dayjs/plugin/timezone";
5
3
  import { DateTimeArgType } from "../definitions";
6
4
 
7
- dayjs.extend(dayjsUtc);
8
- dayjs.extend(dayjsTimeZone);
9
-
10
5
  const PARSE_TIME_REG = /^(\d{1,2}):(\d{2}) ?([APM]{0,2})$/i;
11
6
 
12
- export const DATETIME_FORMAT_STANDARD = "MMM D, YYYY - h:mm A";
13
- export const DATETIME_FORMAT_ISO_LIKE = "YYYY-MM-DD HH:mm";
14
- export const DATE_FORMAT_STANDARD = "MM/DD/YYYY";
15
- export const DATE_FORMAT_ISO = "YYYY-MM-DD";
16
- export const TIME_FORMAT_AM_PM = "h:mm A";
7
+ export const DATETIME_FORMAT_STANDARD = "MMM d, yyyy - h:mm a";
8
+ export const DATETIME_FORMAT_ISO_LIKE = "yyyy-MM-dd HH:mm";
9
+ export const DATE_FORMAT_STANDARD = "MM/dd/yyyy";
10
+ export const DATE_FORMAT_ISO = "yyyy-MM-dd";
11
+ export const TIME_FORMAT_AM_PM = "h:mm a";
17
12
 
18
13
  export interface ITime {
19
14
  hours: number;
@@ -21,42 +16,46 @@ export interface ITime {
21
16
  ampm: "AM" | "PM";
22
17
  }
23
18
 
19
+ export function getLocalTimezoneName(): string {
20
+ return DateTime.local().zoneName;
21
+ }
22
+
24
23
  export function formatDateRangeBasic(
25
24
  startDateTimeArg: Date | null,
26
25
  endDateTimeArg: Date | null
27
26
  ): string {
28
- const startDateTime = dayjs(startDateTimeArg).tz(dayjs.tz.guess());
29
- const endDateTime = dayjs(endDateTimeArg).tz(dayjs.tz.guess());
30
- if (startDateTime.isSame(endDateTime, "day")) {
31
- return `${startDateTime.format(
27
+ const userTimezone = getLocalTimezoneName();
28
+ const startDateTime = DateTime.fromJSDate(startDateTimeArg || new Date()).setZone(userTimezone);
29
+ const endDateTime = DateTime.fromJSDate(endDateTimeArg || new Date()).setZone(userTimezone);
30
+
31
+ if (startDateTime.hasSame(endDateTime, "day")) {
32
+ return `${startDateTime.toFormat(
32
33
  DATETIME_FORMAT_STANDARD
33
- )} to ${endDateTime.format(TIME_FORMAT_AM_PM)}`;
34
+ )} to ${endDateTime.toFormat(TIME_FORMAT_AM_PM)}`;
34
35
  }
35
36
 
36
- return `${startDateTime.format(
37
+ return `${startDateTime.toFormat(
37
38
  DATETIME_FORMAT_STANDARD
38
- )} to ${endDateTime.format(DATETIME_FORMAT_STANDARD)}`;
39
+ )} to ${endDateTime.toFormat(DATETIME_FORMAT_STANDARD)}`;
39
40
  }
40
41
 
41
42
  export function formatDateRange(
42
43
  startDateTimeArg: Date | null,
43
44
  endDateTimeArg: Date | null
44
45
  ): string {
45
- const startDateTime = ensureDateTimeIsLocalDateTime(startDateTimeArg).tz(
46
- dayjs.tz.guess()
47
- );
48
- const endDateTime = ensureDateTimeIsLocalDateTime(endDateTimeArg).tz(
49
- dayjs.tz.guess()
50
- );
51
- if (startDateTime.isSame(endDateTime, "day")) {
52
- return `${startDateTime.format(
46
+ const userTimezone = getLocalTimezoneName();
47
+ const startDateTime = ensureDateTimeIsLocalDateTime(startDateTimeArg).setZone(userTimezone);
48
+ const endDateTime = ensureDateTimeIsLocalDateTime(endDateTimeArg).setZone(userTimezone);
49
+
50
+ if (startDateTime.hasSame(endDateTime, "day")) {
51
+ return `${startDateTime.toFormat(
53
52
  DATETIME_FORMAT_STANDARD
54
- )} to ${endDateTime.format(TIME_FORMAT_AM_PM)}`;
53
+ )} to ${endDateTime.toFormat(TIME_FORMAT_AM_PM)}`;
55
54
  }
56
55
 
57
- return `${startDateTime.format(
56
+ return `${startDateTime.toFormat(
58
57
  DATETIME_FORMAT_STANDARD
59
- )} to ${endDateTime.format(DATETIME_FORMAT_STANDARD)}`;
58
+ )} to ${endDateTime.toFormat(DATETIME_FORMAT_STANDARD)}`;
60
59
  }
61
60
 
62
61
  export function ensureIsDateTime(
@@ -84,8 +83,9 @@ export function normalizeDate(
84
83
  }
85
84
 
86
85
  // Normalize to UTC and start of minute
87
- return dayjs(date).utc().startOf("minute").toDate();
86
+ return DateTime.fromJSDate(date).toUTC().startOf("minute").toJSDate();
88
87
  }
88
+
89
89
  export function normalizeDates(
90
90
  dates: (Date | string | undefined | null)[]
91
91
  ): Date[] {
@@ -108,12 +108,11 @@ export function dateWithinDateRange(
108
108
  date: DateTimeArgType,
109
109
  dateRange: DateValueType | undefined
110
110
  ): boolean {
111
- const startDate = dayjs(dateRange?.startDate).startOf("day").toDate();
112
- const endDate = dayjs(dateRange?.endDate).endOf("day").toDate();
111
+ const startDate = DateTime.fromJSDate(dateRange?.startDate || new Date()).startOf("day");
112
+ const endDate = DateTime.fromJSDate(dateRange?.endDate || new Date()).endOf("day");
113
+ const checkDate = DateTime.fromJSDate(new Date(date || ''));
113
114
 
114
- return (
115
- compareDateTime(date, startDate) >= 0 && compareDateTime(date, endDate) < 0
116
- );
115
+ return checkDate >= startDate && checkDate < endDate;
117
116
  }
118
117
 
119
118
  export function findLatestDateTime(
@@ -197,76 +196,82 @@ export function setDateButPreserveTime(
197
196
  }
198
197
 
199
198
  // Get local timezone
200
- const localTimeZone = dayjs.tz.guess();
201
-
202
- // Format date and time parts
203
- const datePart = dayjs(dateArg).tz(localTimeZone).format("YYYY-MM-DD"); // Local date
204
- const timePart = dayjs(dateWithTheTimeYouWantToKeep)
205
- .tz(localTimeZone)
206
- .format("HH:mm"); // Local time
199
+ const localTimeZone = getLocalTimezoneName();
200
+
201
+ // Extract date and time components using Luxon
202
+ const dateOnly = DateTime.fromJSDate(new Date(dateArg)).setZone(localTimeZone);
203
+ const timeOnly = DateTime.fromJSDate(new Date(dateWithTheTimeYouWantToKeep)).setZone(localTimeZone);
204
+
205
+ // Combine date from first argument with time from second argument
206
+ const combinedDateTime = dateOnly.set({
207
+ hour: timeOnly.hour,
208
+ minute: timeOnly.minute,
209
+ second: 0,
210
+ millisecond: 0
211
+ });
207
212
 
208
- // Combine into an ISO string
209
- const combinedDateTimeString = `${datePart}T${timePart}`;
210
213
  console.log("Combining date and time:", {
211
- datePart,
212
- timePart,
213
- combinedDateTimeString,
214
+ dateOnly: dateOnly.toISO(),
215
+ timeOnly: timeOnly.toFormat("HH:mm"),
216
+ combinedDateTime: combinedDateTime.toISO(),
214
217
  localTimeZone,
215
218
  });
216
219
 
217
- // Convert to UTC
218
- const combinedDateTime = dayjs
219
- .tz(combinedDateTimeString, localTimeZone)
220
- .toDate();
221
- if (isNaN(combinedDateTime.getTime())) {
222
- console.error("Invalid combined datetime:", combinedDateTimeString);
220
+ const result = combinedDateTime.toJSDate();
221
+ if (isNaN(result.getTime())) {
222
+ console.error("Invalid combined datetime:", combinedDateTime.toISO());
223
223
  throw new Error("Invalid date or time format.");
224
224
  }
225
225
 
226
- console.log("Final combined DateTime (UTC):", combinedDateTime);
227
- return combinedDateTime;
226
+ console.log("Final combined DateTime (UTC):", result);
227
+ return result;
228
228
  }
229
229
 
230
230
  export function setTimeOnDate(
231
231
  date: DateType | Date | undefined,
232
232
  parsedTime: ITime | null
233
233
  ): Date {
234
- const isValidDate = dayjs(date).isValid();
235
- const dateTime = new Date(isValidDate && date ? date : Date.now());
234
+ const dateTime = DateTime.fromJSDate(date ? new Date(date) : new Date());
235
+
236
236
  if (parsedTime) {
237
- const parsedTimeDate = new Date();
238
- parsedTimeDate.setHours(parsedTime.hours, parsedTime.minutes, 0); // Will change the date depending on time; corrected later
239
- const correctTimeOnDate = setDateButPreserveTime(dateTime, parsedTimeDate);
240
- return correctTimeOnDate;
237
+ const updatedDateTime = dateTime.set({
238
+ hour: parsedTime.hours,
239
+ minute: parsedTime.minutes,
240
+ second: 0,
241
+ millisecond: 0
242
+ });
243
+ return setDateButPreserveTime(dateTime.toJSDate(), updatedDateTime.toJSDate());
241
244
  }
242
- return dateTime;
245
+ return dateTime.toJSDate();
243
246
  }
244
247
 
245
248
  export function ensureDateTimeIsLocalDateTime(
246
- dateArg: DateType | Dayjs | undefined
247
- ): Dayjs {
248
- if (dayjs(dateArg).isUTC()) {
249
- const dateLocalTimeZone = dayjs(dateArg);
250
- return dateLocalTimeZone;
251
- } else {
252
- return dayjs(dateArg ?? Date.now());
249
+ dateArg: DateType | DateTime | undefined
250
+ ): DateTime {
251
+ if (!dateArg) {
252
+ return DateTime.local();
253
+ }
254
+
255
+ if (dateArg instanceof DateTime) {
256
+ return dateArg.setZone(getLocalTimezoneName());
253
257
  }
258
+
259
+ const dt = DateTime.fromJSDate(new Date(dateArg));
260
+ return dt.setZone(getLocalTimezoneName());
254
261
  }
255
262
 
256
263
  export function formatDateTimeToTimeString(
257
264
  date: Date | string | undefined | null
258
265
  ): string {
259
- date = new Date(date ?? Date.now());
260
- const result = dayjs(date).format("h:mm A");
261
- return result;
266
+ const dt = DateTime.fromJSDate(new Date(date ?? Date.now()));
267
+ return dt.toFormat("h:mm a");
262
268
  }
263
269
 
264
270
  export function formatDateTimeToTimeString24hr(
265
271
  date: Date | string | undefined | null
266
272
  ): string {
267
- date = new Date(date ?? Date.now());
268
- const result = dayjs(date).format("h:mm");
269
- return result;
273
+ const dt = DateTime.fromJSDate(new Date(date ?? Date.now()));
274
+ return dt.toFormat("H:mm");
270
275
  }
271
276
 
272
277
  export function parseTimeString(timeStr: string): ITime | null {
@@ -293,6 +298,7 @@ export function parseTimeString(timeStr: string): ITime | null {
293
298
  }
294
299
  return null;
295
300
  }
301
+
296
302
  export function timestampMinutesFromNow(minutes: number): number {
297
- return Math.floor(new Date().getTime() / 1000 + minutes * 60);
303
+ return Math.floor(DateTime.local().plus({ minutes }).toSeconds());
298
304
  }
File without changes
File without changes
@@ -281,11 +281,6 @@ export function getLocalTimezone() {
281
281
  return utcOffsetMinutesToTimezone(localDateTime.offset);
282
282
  }
283
283
 
284
- export function getLocalTimezoneName() {
285
- const localDateTime = DateTime.local();
286
- return localDateTime.zoneName;
287
- }
288
-
289
284
  export function dateTimeFormatDateRange(
290
285
  startDateTimeArg: DateTime | null,
291
286
  endDateTimeArg: DateTime | null
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,11 +1,7 @@
1
- import {DayOfWeek, Recurrence, RecurringFrequency} from "@prisma/client";
2
- import dayjs, {Dayjs} from "dayjs";
3
- import dayjsWeekDay from "dayjs/plugin/weekday";
4
- import {DateTimeArgType} from "../definitions";
5
- import {compareDateTime, DATETIME_FORMAT_ISO_LIKE} from "./dateTimeUtils";
6
-
7
- dayjs.extend(dayjsWeekDay);
8
-
1
+ import { DayOfWeek, Recurrence, RecurringFrequency } from "@prisma/client";
2
+ import { DateTime, WeekdayNumbers } from "luxon";
3
+ import { DateTimeArgType } from "../definitions";
4
+ import { compareDateTime, DATETIME_FORMAT_ISO_LIKE } from "./dateTimeUtils";
9
5
 
10
6
  export function getRecurringBashEventPossibleDateTimes(startDateTime: DateTimeArgType,
11
7
  recurrence: Recurrence | undefined | null): Date[] {
@@ -13,20 +9,19 @@ export function getRecurringBashEventPossibleDateTimes(startDateTime: DateTimeAr
13
9
  }
14
10
 
15
11
  export function getRecurringBashEventPossibleDayjsTimes(startDateTime: DateTimeArgType,
16
- recurrence: Recurrence | undefined | null): Dayjs[] {
17
- return getRecurringBashEventPossibleDatesTimesInternal(startDateTime, recurrence, false) as Dayjs[];
12
+ recurrence: Recurrence | undefined | null): DateTime[] {
13
+ return getRecurringBashEventPossibleDatesTimesInternal(startDateTime, recurrence, false) as DateTime[];
18
14
  }
19
15
 
20
-
21
16
  function getRecurringBashEventPossibleDatesTimesInternal(startDateTime: DateTimeArgType,
22
17
  recurrence: Recurrence | undefined | null,
23
- toDate: boolean): (Dayjs | Date)[] {
18
+ toDate: boolean): (DateTime | Date)[] {
24
19
  if (!recurrence || !recurrence.frequency || recurrence.frequency === RecurringFrequency.Never) {
25
20
  return [];
26
21
  }
27
22
 
28
- let recurrenceDates: Dayjs[] = [];
29
- const beginningDate = findEarliestPossibleBashEventDate(startDateTime) as Dayjs; // Don't allow dates before the current date
23
+ let recurrenceDates: DateTime[] = [];
24
+ const beginningDate = findEarliestPossibleBashEventDate(startDateTime) as DateTime; // Don't allow dates before the current date
30
25
 
31
26
  switch (recurrence.frequency) {
32
27
  case RecurringFrequency.Weekly:
@@ -35,14 +30,15 @@ function getRecurringBashEventPossibleDatesTimesInternal(startDateTime: DateTime
35
30
  default:
36
31
  console.error(`Only weekly frequency is currently supported`);
37
32
  }
38
- return recurrenceDates.map((date: Dayjs): string => date.format(DATETIME_FORMAT_ISO_LIKE))
33
+ return recurrenceDates.map((date: DateTime): string => date.toFormat(DATETIME_FORMAT_ISO_LIKE))
39
34
  .sort()
40
- .map((dateStr: string): Dayjs | Date => {
35
+ .map((dateStr: string): DateTime | Date => {
36
+ const luxonDate = DateTime.fromFormat(dateStr, DATETIME_FORMAT_ISO_LIKE);
41
37
  if (toDate) {
42
- return dayjs(dateStr).toDate();
38
+ return luxonDate.toJSDate();
43
39
  }
44
40
  else {
45
- return dayjs(dateStr);
41
+ return luxonDate;
46
42
  }
47
43
  });
48
44
  }
@@ -52,25 +48,25 @@ function getRecurringBashEventPossibleDatesTimesInternal(startDateTime: DateTime
52
48
  * @param beginningDateTime The beginning DateTime of the event, adjusted to be no earlier than now.
53
49
  * @param recurrence
54
50
  */
55
- function getWeeklyRecurringDates(beginningDateTime: Dayjs, recurrence: Recurrence): Dayjs[] {
51
+ function getWeeklyRecurringDates(beginningDateTime: DateTime, recurrence: Recurrence): DateTime[] {
56
52
  if (recurrence.frequency !== RecurringFrequency.Weekly) {
57
53
  throw new Error(`Cannot do a weekly recurrence if the frequency isn't weekly.`);
58
54
  }
59
55
 
60
56
  const interval = recurrence.interval ?? 1;
61
- const recurrenceEnds = dayjs(recurrence.ends).endOf('day'); // Get the end of the day to not miss the last day
62
- const numberOfDays = recurrenceEnds.diff(beginningDateTime, 'days');
57
+ const recurrenceEnds = DateTime.fromJSDate(new Date(recurrence.ends || '')).endOf('day'); // Get the end of the day to not miss the last day
58
+ const numberOfDays = recurrenceEnds.diff(beginningDateTime, 'days').days;
63
59
  const numberOfWeeks = Math.ceil((numberOfDays / 7) / interval); // Get the number of days and then round up so that we include the ending date
64
60
 
65
- const recurrenceDates: Dayjs[] = [];
61
+ const recurrenceDates: DateTime[] = [];
66
62
  let theNextWeekOfRecurrences = beginningDateTime;
67
63
 
68
64
  // Go week by week getting the next recurrence date
69
65
  for (let i = 0; i < numberOfWeeks; i++) {
70
- let weekday: Dayjs | null = null;
66
+ let weekday: DateTime | null = null;
71
67
  for (const dayOfWeekEnum of recurrence.repeatOnDays) {
72
68
  const dayOfWeekNum = dayOfWeekEnumToDayNumber(dayOfWeekEnum);
73
- weekday = theNextWeekOfRecurrences.weekday(dayOfWeekNum);
69
+ weekday = theNextWeekOfRecurrences.set({ weekday: dayOfWeekNum as WeekdayNumbers });
74
70
  if (weekday > recurrenceEnds || weekday < beginningDateTime) {
75
71
  continue; // Continue because repeatOnDays isn't sorted by the day of the week
76
72
  }
@@ -85,21 +81,22 @@ function getWeeklyRecurringDates(beginningDateTime: Dayjs, recurrence: Recurrenc
85
81
  return recurrenceDates;
86
82
  }
87
83
 
88
- function copyTimeToDate(dateWithTimeToCopy: Dayjs, dateWhoseTimeToChange: Dayjs): Dayjs {
89
- if (dateWithTimeToCopy.second() !== 0) {
84
+ function copyTimeToDate(dateWithTimeToCopy: DateTime, dateWhoseTimeToChange: DateTime): DateTime {
85
+ if (dateWithTimeToCopy.second !== 0) {
90
86
  console.warn(`dateWithTimeToCopy has non-zero seconds: ${dateWithTimeToCopy.toString()} \nFixing...`);
91
- dateWithTimeToCopy = dateWithTimeToCopy.set('seconds', 0);
87
+ dateWithTimeToCopy = dateWithTimeToCopy.set({second: 0});
92
88
  }
93
- dateWhoseTimeToChange = dateWhoseTimeToChange.set('hours', dateWithTimeToCopy.hour());
94
- dateWhoseTimeToChange = dateWhoseTimeToChange.set('minutes', dateWithTimeToCopy.minute());
95
- dateWhoseTimeToChange = dateWhoseTimeToChange.set('seconds', 0); // Should be 0
96
- return dateWhoseTimeToChange;
89
+ return dateWhoseTimeToChange.set({
90
+ hour: dateWithTimeToCopy.hour,
91
+ minute: dateWithTimeToCopy.minute,
92
+ second: 0
93
+ });
97
94
  }
98
95
 
99
96
  function dayOfWeekEnumToDayNumber(dayEnum: DayOfWeek): number {
100
97
  switch (dayEnum) {
101
98
  case DayOfWeek.Sunday:
102
- return 0;
99
+ return 7; // Luxon uses 1-7 where 7 is Sunday
103
100
  case DayOfWeek.Monday:
104
101
  return 1;
105
102
  case DayOfWeek.Tuesday:
@@ -122,13 +119,13 @@ function dayOfWeekEnumToDayNumber(dayEnum: DayOfWeek): number {
122
119
  * @param date
123
120
  * @param interval
124
121
  */
125
- function getBeginningOfWeekInterval(date: Dayjs, interval: number): Dayjs {
122
+ function getBeginningOfWeekInterval(date: DateTime, interval: number): DateTime {
126
123
  if (!interval) {
127
124
  interval = 1; // Avoid 0
128
125
  }
129
126
  // An interval of 2 would be (2 - 1) * 7 + 1 = 8 days to add to skip a week and go into the next week
130
127
  const numberOfDaysToAdd = interval > 1 ? (interval - 1) * 7 + 1 : 1;
131
- return date.endOf('week').add(numberOfDaysToAdd, 'day');
128
+ return date.endOf('week').plus({ days: numberOfDaysToAdd });
132
129
  }
133
130
 
134
131
  export function freqToGrammarString(freq: RecurringFrequency, interval: number | undefined, toLowerCase: boolean = false): string {
@@ -154,18 +151,18 @@ export function freqToGrammarString(freq: RecurringFrequency, interval: number |
154
151
  return toLowerCase ? result.toLowerCase() : result;
155
152
  }
156
153
 
157
- export function findEarliestPossibleBashEventDate(startDateTimeArg: DateTimeArgType): Dayjs | undefined {
154
+ export function findEarliestPossibleBashEventDate(startDateTimeArg: DateTimeArgType): DateTime | undefined {
158
155
  return findEarliestOrLatestPossibleBashEventDate(startDateTimeArg, true);
159
156
  }
160
157
 
161
- function findEarliestOrLatestPossibleBashEventDate(startDateTimeArg: DateTimeArgType, findEarly: boolean): Dayjs | undefined {
158
+ function findEarliestOrLatestPossibleBashEventDate(startDateTimeArg: DateTimeArgType, findEarly: boolean): DateTime | undefined {
162
159
  if (!startDateTimeArg) {
163
160
  return;
164
161
  }
165
162
  // Don't allow dates before the current date
166
- const startDateTime = dayjs(startDateTimeArg);
167
- const currentDateTime = dayjs();
168
- const comparedDateTime = compareDateTime(currentDateTime.toDate(), startDateTimeArg);
163
+ const startDateTime = DateTime.fromJSDate(new Date(startDateTimeArg));
164
+ const currentDateTime = DateTime.local();
165
+ const comparedDateTime = compareDateTime(currentDateTime.toJSDate(), startDateTimeArg);
169
166
  if (findEarly) {
170
167
  return comparedDateTime > 0 ? currentDateTime : startDateTime;
171
168
  }
File without changes
File without changes
File without changes
@@ -107,20 +107,20 @@ export function serviceBookingCanHaveApprovalDecision(
107
107
  errorMessage: "Approval decisions can only be made on booking requests.",
108
108
  };
109
109
  }
110
- // if (serviceBookingIsCanceled(booking)) {
111
- // return {
112
- // valid: false,
113
- // errorMessage:
114
- // "Approval decisions can not be made on canceled booking requests.",
115
- // };
116
- // }
117
- // if (serviceBookingIsConfirmed(booking)) {
118
- // return {
119
- // valid: false,
120
- // errorMessage:
121
- // "Approval decisions can not be made on confirmed booking requests.",
122
- // };
123
- // }
110
+ if (serviceBookingIsCanceled(booking)) {
111
+ return {
112
+ valid: false,
113
+ errorMessage:
114
+ "Approval decisions can not be made on canceled booking requests.",
115
+ };
116
+ }
117
+ if (serviceBookingIsConfirmed(booking)) {
118
+ return {
119
+ valid: false,
120
+ errorMessage:
121
+ "Approval decisions can not be made on confirmed booking requests.",
122
+ };
123
+ }
124
124
 
125
125
  return { valid: true };
126
126
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,10 +1,10 @@
1
- import dayjs from "dayjs";
1
+ import { DateTime } from "luxon";
2
2
  import {
3
- NumberOfTicketsForDate,
4
- URL_PARAMS_NUMBER_OF_TICKETS_TICKETS_DATE_DELIM,
5
- URL_PARAMS_TICKET_TIER_ID_NUMBER_OF_TICKETS_DATE_DELIM,
6
- URL_PARAMS_TICKET_LIST_DELIM,
7
- URL_PARAMS_TICKETS_DATE_DELIM
3
+ NumberOfTicketsForDate,
4
+ URL_PARAMS_NUMBER_OF_TICKETS_TICKETS_DATE_DELIM,
5
+ URL_PARAMS_TICKET_LIST_DELIM,
6
+ URL_PARAMS_TICKET_TIER_ID_NUMBER_OF_TICKETS_DATE_DELIM,
7
+ URL_PARAMS_TICKETS_DATE_DELIM
8
8
  } from "../definitions";
9
9
  import { DATETIME_FORMAT_ISO_LIKE } from "./dateTimeUtils";
10
10
 
@@ -60,11 +60,11 @@ export function ticketListStrToTicketList(ticketListStr: string): Map<string, Nu
60
60
  for (const ticketNumAndDateStr of ticketNumAndDates) {
61
61
  // [numberOfTickets];;[date]
62
62
  const [numberOfTickets, ticketDateTimeStr] = ticketNumAndDateStr.split(URL_PARAMS_TICKETS_DATE_DELIM);
63
- const ticketDateTime = dayjs(ticketDateTimeStr);
63
+ const ticketDateTime = DateTime.fromISO(ticketDateTimeStr);
64
64
  let ticketDateTimeFormattedStr: string | undefined = undefined;
65
65
 
66
- if (ticketDateTime.isValid()) {
67
- ticketDateTimeFormattedStr = ticketDateTime.format(DATETIME_FORMAT_ISO_LIKE);
66
+ if (ticketDateTime.isValid) {
67
+ ticketDateTimeFormattedStr = ticketDateTime.toFormat(DATETIME_FORMAT_ISO_LIKE);
68
68
  }
69
69
 
70
70
  ticketNumAndDatesArr.push({
File without changes
File without changes
File without changes
File without changes