@bash-app/bash-common 29.42.1 → 29.42.2

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.
@@ -1,56 +1,56 @@
1
- import {BashEventPromoCode, TicketTier} from "@prisma/client";
2
- import {BASH_FEE_PERCENTAGE, NumberOfTicketsForDate, PRICE_DOLLARS_AND_CENTS_RATIO} from "../definitions";
3
-
4
-
5
- /**
6
- * Returns the amount of discount in dollars
7
- * @param totalInDollars
8
- * @param promoCode
9
- */
10
- export function calculateDiscountFromPromoCode(totalInDollars: number, promoCode: BashEventPromoCode | undefined): number {
11
- if (promoCode) {
12
- if (promoCode.discountAmountInCents) {
13
- const discountInDollars = convertCentsToDollars(promoCode.discountAmountInCents);
14
- return Math.max(0, discountInDollars); // Ensure we don't discount more than 100%
15
- }
16
- if (promoCode.discountAmountPercentage) {
17
- const discountInDollars = totalInDollars * (promoCode.discountAmountPercentage / 100);
18
- return Math.min(totalInDollars, discountInDollars); // Ensure we don't discount more than 100%
19
- }
20
- }
21
- return 0;
22
- }
23
-
24
-
25
- /**
26
- * Returns the total price based on a map where the keys are the ticketTierIds
27
- * @param ticketTiers
28
- * @param ticketList
29
- */
30
- export function calculateTotalPriceWithoutTax(ticketTiers: TicketTier[], ticketList: Map<string, NumberOfTicketsForDate[]>): number {
31
- let total = 0;
32
- ticketTiers.forEach((tier: TicketTier) => {
33
- const ticketListNumAndDate = ticketList.get(tier.id);
34
- ticketListNumAndDate?.forEach((numAndDate: NumberOfTicketsForDate) => {
35
- total += tier.price * (numAndDate.numberOfTickets ?? 0);
36
- });
37
- });
38
- return total;
39
- }
40
-
41
- export function convertCentsToDollars(price: number | undefined | null): number {
42
- if (!price) {
43
- return 0;
44
- }
45
- return price / PRICE_DOLLARS_AND_CENTS_RATIO;
46
- }
47
- export function convertDollarsToCents(price: number | undefined | null): number {
48
- if (!price) {
49
- return 0;
50
- }
51
- return price * PRICE_DOLLARS_AND_CENTS_RATIO;
52
- }
53
-
54
- export function calculateBashAppFee(total: number): number {
55
- return parseFloat((total * BASH_FEE_PERCENTAGE).toFixed(2));
56
- }
1
+ import {BashEventPromoCode, TicketTier} from "@prisma/client";
2
+ import {BASH_FEE_PERCENTAGE, NumberOfTicketsForDate, PRICE_DOLLARS_AND_CENTS_RATIO} from "../definitions";
3
+
4
+
5
+ /**
6
+ * Returns the amount of discount in dollars
7
+ * @param totalInDollars
8
+ * @param promoCode
9
+ */
10
+ export function calculateDiscountFromPromoCode(totalInDollars: number, promoCode: BashEventPromoCode | undefined): number {
11
+ if (promoCode) {
12
+ if (promoCode.discountAmountInCents) {
13
+ const discountInDollars = convertCentsToDollars(promoCode.discountAmountInCents);
14
+ return Math.max(0, discountInDollars); // Ensure we don't discount more than 100%
15
+ }
16
+ if (promoCode.discountAmountPercentage) {
17
+ const discountInDollars = totalInDollars * (promoCode.discountAmountPercentage / 100);
18
+ return Math.min(totalInDollars, discountInDollars); // Ensure we don't discount more than 100%
19
+ }
20
+ }
21
+ return 0;
22
+ }
23
+
24
+
25
+ /**
26
+ * Returns the total price based on a map where the keys are the ticketTierIds
27
+ * @param ticketTiers
28
+ * @param ticketList
29
+ */
30
+ export function calculateTotalPriceWithoutTax(ticketTiers: TicketTier[], ticketList: Map<string, NumberOfTicketsForDate[]>): number {
31
+ let total = 0;
32
+ ticketTiers.forEach((tier: TicketTier) => {
33
+ const ticketListNumAndDate = ticketList.get(tier.id);
34
+ ticketListNumAndDate?.forEach((numAndDate: NumberOfTicketsForDate) => {
35
+ total += tier.price * (numAndDate.numberOfTickets ?? 0);
36
+ });
37
+ });
38
+ return total;
39
+ }
40
+
41
+ export function convertCentsToDollars(price: number | undefined | null): number {
42
+ if (!price) {
43
+ return 0;
44
+ }
45
+ return price / PRICE_DOLLARS_AND_CENTS_RATIO;
46
+ }
47
+ export function convertDollarsToCents(price: number | undefined | null): number {
48
+ if (!price) {
49
+ return 0;
50
+ }
51
+ return price * PRICE_DOLLARS_AND_CENTS_RATIO;
52
+ }
53
+
54
+ export function calculateBashAppFee(total: number): number {
55
+ return parseFloat((total * BASH_FEE_PERCENTAGE).toFixed(2));
56
+ }
@@ -1,29 +1,29 @@
1
- import {BashEventExt, TicketTierExt} from "../extendedSchemas";
2
- import {BashEventPromoCode} from "@prisma/client";
3
-
4
-
5
- export function findPromoCodeFromBashEvent(bashEvent: BashEventExt | undefined | null,
6
- promoCodeId: string | undefined): BashEventPromoCode | undefined {
7
- if (!promoCodeId) {
8
- return;
9
- }
10
- if (bashEvent?.ticketTiers.length) {
11
- for (const tier of bashEvent?.ticketTiers) {
12
- const foundPromoCode = tier.promoCodes.find((promoCode) => promoCode.id === promoCodeId);
13
- if (foundPromoCode) {
14
- return foundPromoCode;
15
- }
16
- }
17
- }
18
- }
19
-
20
- export function getPromoCodesFromBashEvent(bashEvent: BashEventExt | undefined | null): BashEventPromoCode[] {
21
- const ticketTiersWithPromoCode: TicketTierExt[] = bashEvent?.ticketTiers?.filter(ticketTier => ticketTier?.promoCodes?.length) ?? [];
22
- return ticketTiersWithPromoCode.map(ticketTier => ticketTier.promoCodes).flat() as BashEventPromoCode[] ?? [];
23
- }
24
-
25
- export function updatePromoCodesOnBashEventTicketTiers(bashEvent: BashEventExt, promoCodes: BashEventPromoCode[]): void {
26
- bashEvent.ticketTiers.forEach((tier) => {
27
- tier.promoCodes = promoCodes.filter((pc) => tier.id === pc.ticketTierId);
28
- });
29
- }
1
+ import {BashEventExt, TicketTierExt} from "../extendedSchemas";
2
+ import {BashEventPromoCode} from "@prisma/client";
3
+
4
+
5
+ export function findPromoCodeFromBashEvent(bashEvent: BashEventExt | undefined | null,
6
+ promoCodeId: string | undefined): BashEventPromoCode | undefined {
7
+ if (!promoCodeId) {
8
+ return;
9
+ }
10
+ if (bashEvent?.ticketTiers.length) {
11
+ for (const tier of bashEvent?.ticketTiers) {
12
+ const foundPromoCode = tier.promoCodes.find((promoCode) => promoCode.id === promoCodeId);
13
+ if (foundPromoCode) {
14
+ return foundPromoCode;
15
+ }
16
+ }
17
+ }
18
+ }
19
+
20
+ export function getPromoCodesFromBashEvent(bashEvent: BashEventExt | undefined | null): BashEventPromoCode[] {
21
+ const ticketTiersWithPromoCode: TicketTierExt[] = bashEvent?.ticketTiers?.filter(ticketTier => ticketTier?.promoCodes?.length) ?? [];
22
+ return ticketTiersWithPromoCode.map(ticketTier => ticketTier.promoCodes).flat() as BashEventPromoCode[] ?? [];
23
+ }
24
+
25
+ export function updatePromoCodesOnBashEventTicketTiers(bashEvent: BashEventExt, promoCodes: BashEventPromoCode[]): void {
26
+ bashEvent.ticketTiers.forEach((tier) => {
27
+ tier.promoCodes = promoCodes.filter((pc) => tier.id === pc.ticketTierId);
28
+ });
29
+ }
@@ -1,23 +1,23 @@
1
- import QRCode from "qrcode";
2
- import { TICKET_DETAILS } from "../definitions";
3
-
4
- export function bashEventPrismaCheckoutQrCodeUrl(bashEventId: string | undefined | null, prismaCheckoutId: string | undefined | null): string {
5
- return `${TICKET_DETAILS}/${bashEventId}/${prismaCheckoutId}`;
6
- }
7
-
8
- export function bashEventQrCodeUrl(bashEventId: string | undefined | null, ticketTierId?: string | undefined | null, ticketId?: string | undefined | null): string {
9
- const baseUrl = `${TICKET_DETAILS}/${bashEventId}`;
10
- if (ticketId) {
11
- return `${baseUrl}/ticket/${ticketId}`;
12
- }
13
- else if (ticketTierId) {
14
- return `${baseUrl}/ticket-tier/${ticketTierId}`;
15
- }
16
- return baseUrl;
17
- }
18
-
19
- export async function createQrCodeImage(qrData: string): Promise<string> {
20
- return QRCode.toDataURL(qrData, {
21
- errorCorrectionLevel: 'high',
22
- });
23
- }
1
+ import QRCode from "qrcode";
2
+ import { TICKET_DETAILS } from "../definitions";
3
+
4
+ export function bashEventPrismaCheckoutQrCodeUrl(bashEventId: string | undefined | null, prismaCheckoutId: string | undefined | null): string {
5
+ return `${TICKET_DETAILS}/${bashEventId}/${prismaCheckoutId}`;
6
+ }
7
+
8
+ export function bashEventQrCodeUrl(bashEventId: string | undefined | null, ticketTierId?: string | undefined | null, ticketId?: string | undefined | null): string {
9
+ const baseUrl = `${TICKET_DETAILS}/${bashEventId}`;
10
+ if (ticketId) {
11
+ return `${baseUrl}/ticket/${ticketId}`;
12
+ }
13
+ else if (ticketTierId) {
14
+ return `${baseUrl}/ticket-tier/${ticketTierId}`;
15
+ }
16
+ return baseUrl;
17
+ }
18
+
19
+ export async function createQrCodeImage(qrData: string): Promise<string> {
20
+ return QRCode.toDataURL(qrData, {
21
+ errorCorrectionLevel: 'high',
22
+ });
23
+ }
@@ -1,175 +1,175 @@
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
-
9
-
10
- export function getRecurringBashEventPossibleDateTimes(startDateTime: DateTimeArgType,
11
- recurrence: Recurrence | undefined | null): Date[] {
12
- return getRecurringBashEventPossibleDatesTimesInternal(startDateTime, recurrence, true) as Date[];
13
- }
14
-
15
- export function getRecurringBashEventPossibleDayjsTimes(startDateTime: DateTimeArgType,
16
- recurrence: Recurrence | undefined | null): Dayjs[] {
17
- return getRecurringBashEventPossibleDatesTimesInternal(startDateTime, recurrence, false) as Dayjs[];
18
- }
19
-
20
-
21
- function getRecurringBashEventPossibleDatesTimesInternal(startDateTime: DateTimeArgType,
22
- recurrence: Recurrence | undefined | null,
23
- toDate: boolean): (Dayjs | Date)[] {
24
- if (!recurrence || !recurrence.frequency || recurrence.frequency === RecurringFrequency.Never) {
25
- return [];
26
- }
27
-
28
- let recurrenceDates: Dayjs[] = [];
29
- const beginningDate = findEarliestPossibleBashEventDate(startDateTime) as Dayjs; // Don't allow dates before the current date
30
-
31
- switch (recurrence.frequency) {
32
- case RecurringFrequency.Weekly:
33
- recurrenceDates = getWeeklyRecurringDates(beginningDate, recurrence);
34
- break;
35
- default:
36
- console.error(`Only weekly frequency is currently supported`);
37
- }
38
- return recurrenceDates.map((date: Dayjs): string => date.format(DATETIME_FORMAT_ISO_LIKE))
39
- .sort()
40
- .map((dateStr: string): Dayjs | Date => {
41
- if (toDate) {
42
- return dayjs(dateStr).toDate();
43
- }
44
- else {
45
- return dayjs(dateStr);
46
- }
47
- });
48
- }
49
-
50
- /**
51
- * Get the weekly recurring dates
52
- * @param beginningDateTime The beginning DateTime of the event, adjusted to be no earlier than now.
53
- * @param recurrence
54
- */
55
- function getWeeklyRecurringDates(beginningDateTime: Dayjs, recurrence: Recurrence): Dayjs[] {
56
- if (recurrence.frequency !== RecurringFrequency.Weekly) {
57
- throw new Error(`Cannot do a weekly recurrence if the frequency isn't weekly.`);
58
- }
59
-
60
- 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');
63
- const numberOfWeeks = Math.ceil((numberOfDays / 7) / interval); // Get the number of days and then round up so that we include the ending date
64
-
65
- const recurrenceDates: Dayjs[] = [];
66
- let theNextWeekOfRecurrences = beginningDateTime;
67
-
68
- // Go week by week getting the next recurrence date
69
- for (let i = 0; i < numberOfWeeks; i++) {
70
- let weekday: Dayjs | null = null;
71
- for (const dayOfWeekEnum of recurrence.repeatOnDays) {
72
- const dayOfWeekNum = dayOfWeekEnumToDayNumber(dayOfWeekEnum);
73
- weekday = theNextWeekOfRecurrences.weekday(dayOfWeekNum);
74
- if (weekday > recurrenceEnds || weekday < beginningDateTime) {
75
- continue; // Continue because repeatOnDays isn't sorted by the day of the week
76
- }
77
- // Set the time on the date so that it matches the time when the event starts
78
- weekday = copyTimeToDate(beginningDateTime, weekday);
79
- recurrenceDates.push(weekday);
80
- }
81
- if (weekday) {
82
- theNextWeekOfRecurrences = getBeginningOfWeekInterval(weekday, interval);
83
- }
84
- }
85
- return recurrenceDates;
86
- }
87
-
88
- function copyTimeToDate(dateWithTimeToCopy: Dayjs, dateWhoseTimeToChange: Dayjs): Dayjs {
89
- if (dateWithTimeToCopy.second() !== 0) {
90
- console.warn(`dateWithTimeToCopy has non-zero seconds: ${dateWithTimeToCopy.toString()} \nFixing...`);
91
- dateWithTimeToCopy = dateWithTimeToCopy.set('seconds', 0);
92
- }
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;
97
- }
98
-
99
- function dayOfWeekEnumToDayNumber(dayEnum: DayOfWeek): number {
100
- switch (dayEnum) {
101
- case DayOfWeek.Sunday:
102
- return 0;
103
- case DayOfWeek.Monday:
104
- return 1;
105
- case DayOfWeek.Tuesday:
106
- return 2;
107
- case DayOfWeek.Wednesday:
108
- return 3;
109
- case DayOfWeek.Thursday:
110
- return 4;
111
- case DayOfWeek.Friday:
112
- return 5;
113
- case DayOfWeek.Saturday:
114
- return 6;
115
- default:
116
- throw new Error(`How did this happen? Invalid DayOfWeek: ${dayEnum}`); // Shouldn't happen
117
- }
118
- }
119
-
120
- /**
121
- * Get the next week determined by the interval by going to the end of the week interval and adding one day
122
- * @param date
123
- * @param interval
124
- */
125
- function getBeginningOfWeekInterval(date: Dayjs, interval: number): Dayjs {
126
- if (!interval) {
127
- interval = 1; // Avoid 0
128
- }
129
- // 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
- const numberOfDaysToAdd = interval > 1 ? (interval - 1) * 7 + 1 : 1;
131
- return date.endOf('week').add(numberOfDaysToAdd, 'day');
132
- }
133
-
134
- export function freqToGrammarString(freq: RecurringFrequency, interval: number | undefined, toLowerCase: boolean = false): string {
135
- if (!interval) {
136
- return freq;
137
- }
138
- const isPlural = interval > 1;
139
- let result: string = freq;
140
- switch (freq) {
141
- case RecurringFrequency.Daily:
142
- result = isPlural ? 'Days' : 'Day';
143
- break;
144
- case RecurringFrequency.Weekly:
145
- result = isPlural ? 'Weeks' : 'Week';
146
- break;
147
- case RecurringFrequency.Monthly:
148
- result = isPlural ? 'Months' : 'Month';
149
- break;
150
- case RecurringFrequency.Yearly:
151
- result = isPlural ? 'Years' : 'Year';
152
- break;
153
- }
154
- return toLowerCase ? result.toLowerCase() : result;
155
- }
156
-
157
- export function findEarliestPossibleBashEventDate(startDateTimeArg: DateTimeArgType): Dayjs | undefined {
158
- return findEarliestOrLatestPossibleBashEventDate(startDateTimeArg, true);
159
- }
160
-
161
- function findEarliestOrLatestPossibleBashEventDate(startDateTimeArg: DateTimeArgType, findEarly: boolean): Dayjs | undefined {
162
- if (!startDateTimeArg) {
163
- return;
164
- }
165
- // Don't allow dates before the current date
166
- const startDateTime = dayjs(startDateTimeArg);
167
- const currentDateTime = dayjs();
168
- const comparedDateTime = compareDateTime(currentDateTime.toDate(), startDateTimeArg);
169
- if (findEarly) {
170
- return comparedDateTime > 0 ? currentDateTime : startDateTime;
171
- }
172
- else {
173
- return comparedDateTime < 0 ? currentDateTime : startDateTime;
174
- }
175
- }
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
+
9
+
10
+ export function getRecurringBashEventPossibleDateTimes(startDateTime: DateTimeArgType,
11
+ recurrence: Recurrence | undefined | null): Date[] {
12
+ return getRecurringBashEventPossibleDatesTimesInternal(startDateTime, recurrence, true) as Date[];
13
+ }
14
+
15
+ export function getRecurringBashEventPossibleDayjsTimes(startDateTime: DateTimeArgType,
16
+ recurrence: Recurrence | undefined | null): Dayjs[] {
17
+ return getRecurringBashEventPossibleDatesTimesInternal(startDateTime, recurrence, false) as Dayjs[];
18
+ }
19
+
20
+
21
+ function getRecurringBashEventPossibleDatesTimesInternal(startDateTime: DateTimeArgType,
22
+ recurrence: Recurrence | undefined | null,
23
+ toDate: boolean): (Dayjs | Date)[] {
24
+ if (!recurrence || !recurrence.frequency || recurrence.frequency === RecurringFrequency.Never) {
25
+ return [];
26
+ }
27
+
28
+ let recurrenceDates: Dayjs[] = [];
29
+ const beginningDate = findEarliestPossibleBashEventDate(startDateTime) as Dayjs; // Don't allow dates before the current date
30
+
31
+ switch (recurrence.frequency) {
32
+ case RecurringFrequency.Weekly:
33
+ recurrenceDates = getWeeklyRecurringDates(beginningDate, recurrence);
34
+ break;
35
+ default:
36
+ console.error(`Only weekly frequency is currently supported`);
37
+ }
38
+ return recurrenceDates.map((date: Dayjs): string => date.format(DATETIME_FORMAT_ISO_LIKE))
39
+ .sort()
40
+ .map((dateStr: string): Dayjs | Date => {
41
+ if (toDate) {
42
+ return dayjs(dateStr).toDate();
43
+ }
44
+ else {
45
+ return dayjs(dateStr);
46
+ }
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Get the weekly recurring dates
52
+ * @param beginningDateTime The beginning DateTime of the event, adjusted to be no earlier than now.
53
+ * @param recurrence
54
+ */
55
+ function getWeeklyRecurringDates(beginningDateTime: Dayjs, recurrence: Recurrence): Dayjs[] {
56
+ if (recurrence.frequency !== RecurringFrequency.Weekly) {
57
+ throw new Error(`Cannot do a weekly recurrence if the frequency isn't weekly.`);
58
+ }
59
+
60
+ 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');
63
+ const numberOfWeeks = Math.ceil((numberOfDays / 7) / interval); // Get the number of days and then round up so that we include the ending date
64
+
65
+ const recurrenceDates: Dayjs[] = [];
66
+ let theNextWeekOfRecurrences = beginningDateTime;
67
+
68
+ // Go week by week getting the next recurrence date
69
+ for (let i = 0; i < numberOfWeeks; i++) {
70
+ let weekday: Dayjs | null = null;
71
+ for (const dayOfWeekEnum of recurrence.repeatOnDays) {
72
+ const dayOfWeekNum = dayOfWeekEnumToDayNumber(dayOfWeekEnum);
73
+ weekday = theNextWeekOfRecurrences.weekday(dayOfWeekNum);
74
+ if (weekday > recurrenceEnds || weekday < beginningDateTime) {
75
+ continue; // Continue because repeatOnDays isn't sorted by the day of the week
76
+ }
77
+ // Set the time on the date so that it matches the time when the event starts
78
+ weekday = copyTimeToDate(beginningDateTime, weekday);
79
+ recurrenceDates.push(weekday);
80
+ }
81
+ if (weekday) {
82
+ theNextWeekOfRecurrences = getBeginningOfWeekInterval(weekday, interval);
83
+ }
84
+ }
85
+ return recurrenceDates;
86
+ }
87
+
88
+ function copyTimeToDate(dateWithTimeToCopy: Dayjs, dateWhoseTimeToChange: Dayjs): Dayjs {
89
+ if (dateWithTimeToCopy.second() !== 0) {
90
+ console.warn(`dateWithTimeToCopy has non-zero seconds: ${dateWithTimeToCopy.toString()} \nFixing...`);
91
+ dateWithTimeToCopy = dateWithTimeToCopy.set('seconds', 0);
92
+ }
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;
97
+ }
98
+
99
+ function dayOfWeekEnumToDayNumber(dayEnum: DayOfWeek): number {
100
+ switch (dayEnum) {
101
+ case DayOfWeek.Sunday:
102
+ return 0;
103
+ case DayOfWeek.Monday:
104
+ return 1;
105
+ case DayOfWeek.Tuesday:
106
+ return 2;
107
+ case DayOfWeek.Wednesday:
108
+ return 3;
109
+ case DayOfWeek.Thursday:
110
+ return 4;
111
+ case DayOfWeek.Friday:
112
+ return 5;
113
+ case DayOfWeek.Saturday:
114
+ return 6;
115
+ default:
116
+ throw new Error(`How did this happen? Invalid DayOfWeek: ${dayEnum}`); // Shouldn't happen
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Get the next week determined by the interval by going to the end of the week interval and adding one day
122
+ * @param date
123
+ * @param interval
124
+ */
125
+ function getBeginningOfWeekInterval(date: Dayjs, interval: number): Dayjs {
126
+ if (!interval) {
127
+ interval = 1; // Avoid 0
128
+ }
129
+ // 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
+ const numberOfDaysToAdd = interval > 1 ? (interval - 1) * 7 + 1 : 1;
131
+ return date.endOf('week').add(numberOfDaysToAdd, 'day');
132
+ }
133
+
134
+ export function freqToGrammarString(freq: RecurringFrequency, interval: number | undefined, toLowerCase: boolean = false): string {
135
+ if (!interval) {
136
+ return freq;
137
+ }
138
+ const isPlural = interval > 1;
139
+ let result: string = freq;
140
+ switch (freq) {
141
+ case RecurringFrequency.Daily:
142
+ result = isPlural ? 'Days' : 'Day';
143
+ break;
144
+ case RecurringFrequency.Weekly:
145
+ result = isPlural ? 'Weeks' : 'Week';
146
+ break;
147
+ case RecurringFrequency.Monthly:
148
+ result = isPlural ? 'Months' : 'Month';
149
+ break;
150
+ case RecurringFrequency.Yearly:
151
+ result = isPlural ? 'Years' : 'Year';
152
+ break;
153
+ }
154
+ return toLowerCase ? result.toLowerCase() : result;
155
+ }
156
+
157
+ export function findEarliestPossibleBashEventDate(startDateTimeArg: DateTimeArgType): Dayjs | undefined {
158
+ return findEarliestOrLatestPossibleBashEventDate(startDateTimeArg, true);
159
+ }
160
+
161
+ function findEarliestOrLatestPossibleBashEventDate(startDateTimeArg: DateTimeArgType, findEarly: boolean): Dayjs | undefined {
162
+ if (!startDateTimeArg) {
163
+ return;
164
+ }
165
+ // Don't allow dates before the current date
166
+ const startDateTime = dayjs(startDateTimeArg);
167
+ const currentDateTime = dayjs();
168
+ const comparedDateTime = compareDateTime(currentDateTime.toDate(), startDateTimeArg);
169
+ if (findEarly) {
170
+ return comparedDateTime > 0 ? currentDateTime : startDateTime;
171
+ }
172
+ else {
173
+ return comparedDateTime < 0 ? currentDateTime : startDateTime;
174
+ }
175
+ }