@bash-app/bash-common 30.174.0 → 30.178.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/dist/bashFeedTypes.d.ts +3 -0
- package/dist/bashFeedTypes.d.ts.map +1 -1
- package/dist/definitions.d.ts +7 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +6 -0
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +226 -5
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +7 -0
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/__tests__/birthdayExperienceQuality.test.d.ts +2 -0
- package/dist/utils/__tests__/birthdayExperienceQuality.test.d.ts.map +1 -0
- package/dist/utils/__tests__/birthdayExperienceQuality.test.js +22 -0
- package/dist/utils/__tests__/birthdayExperienceQuality.test.js.map +1 -0
- package/dist/utils/addressUtils.d.ts +5 -0
- package/dist/utils/addressUtils.d.ts.map +1 -1
- package/dist/utils/addressUtils.js +8 -0
- package/dist/utils/addressUtils.js.map +1 -1
- package/dist/utils/birthdayExperienceQuality.d.ts +21 -0
- package/dist/utils/birthdayExperienceQuality.d.ts.map +1 -0
- package/dist/utils/birthdayExperienceQuality.js +33 -0
- package/dist/utils/birthdayExperienceQuality.js.map +1 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +91 -4
- package/src/bashFeedTypes.ts +3 -0
- package/src/definitions.ts +8 -0
- package/src/extendedSchemas.ts +50 -4
- package/src/index.ts +1 -0
- package/src/utils/__tests__/birthdayExperienceQuality.test.ts +34 -0
- package/src/utils/addressUtils.ts +15 -0
- package/src/utils/birthdayExperienceQuality.ts +49 -0
package/src/extendedSchemas.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
Exhibitor,
|
|
23
23
|
IdeaConfiguration,
|
|
24
24
|
IdeaInterest,
|
|
25
|
+
IdeaInterestResponse,
|
|
25
26
|
Invitation,
|
|
26
27
|
Link,
|
|
27
28
|
Media,
|
|
@@ -75,7 +76,12 @@ import {
|
|
|
75
76
|
VolunteerService
|
|
76
77
|
} from "@prisma/client";
|
|
77
78
|
import { BlogStatus, SERVICE_LINK_DATA_TO_INCLUDE } from "./definitions.js";
|
|
78
|
-
import {
|
|
79
|
+
import {
|
|
80
|
+
DeepPartial,
|
|
81
|
+
Override,
|
|
82
|
+
RemoveCommonProperties,
|
|
83
|
+
UnionFromArray,
|
|
84
|
+
} from "./utils/typeUtils.js";
|
|
79
85
|
|
|
80
86
|
export interface ApiResponse<T> {
|
|
81
87
|
data: T;
|
|
@@ -148,6 +154,10 @@ export const FRONT_END_USER_DATA_TO_SELECT = {
|
|
|
148
154
|
isFirstInCity: true,
|
|
149
155
|
/** Venmo / Zelle / CashApp handle for off-platform payouts (promoters). */
|
|
150
156
|
paymentHandle: true,
|
|
157
|
+
/** Host reputation fields */
|
|
158
|
+
hostRating: true,
|
|
159
|
+
totalRatings: true,
|
|
160
|
+
totalEventsHosted: true,
|
|
151
161
|
} satisfies Prisma.UserSelect;
|
|
152
162
|
|
|
153
163
|
export const PRIVATE_USER_ACCOUNT_TO_SELECT = {
|
|
@@ -175,7 +185,10 @@ export interface CompetitionExt extends Competition {
|
|
|
175
185
|
votes?: CompetitionVote[];
|
|
176
186
|
}
|
|
177
187
|
|
|
178
|
-
export interface BashEventExt extends
|
|
188
|
+
export interface BashEventExt extends Omit<
|
|
189
|
+
BashEvent,
|
|
190
|
+
"ideaExpiresAt" | "ideaInterestThreshold" | "isAutoApprovable"
|
|
191
|
+
> {
|
|
179
192
|
targetAudience?: TargetAudience;
|
|
180
193
|
amountOfGuests?: AmountOfGuests;
|
|
181
194
|
recurrence?: Recurrence;
|
|
@@ -191,13 +204,44 @@ export interface BashEventExt extends BashEvent {
|
|
|
191
204
|
venueOwner?: PublicUser;
|
|
192
205
|
sponsorships?: SponsoredEventExt[];
|
|
193
206
|
competitions?: CompetitionExt[];
|
|
194
|
-
interests?: IdeaInterest[];
|
|
207
|
+
interests?: (IdeaInterest & { responses?: IdeaInterestResponse[] })[];
|
|
195
208
|
configurations?: IdeaConfiguration[];
|
|
209
|
+
/** GET /event/:id — tickets sold (non-cancelled), for social proof */
|
|
210
|
+
goingCount?: number;
|
|
211
|
+
/** GET /event/:id when status=Idea — total IdeaInterest rows */
|
|
212
|
+
ideaInterestCount?: number;
|
|
213
|
+
/** GET /event/:id — PublicBashRsvp counts (published / presale) */
|
|
214
|
+
publicRsvpCounts?: { going: number; interested: number; notGoing: number };
|
|
215
|
+
/** Current viewer's soft RSVP (PublicBashRsvp), if any */
|
|
216
|
+
myPublicRsvpStatus?: string | null;
|
|
217
|
+
/** GET /event/:id when status=Idea — viewer's IdeaInterest row (matched by email) */
|
|
218
|
+
myIdeaInterest?: (IdeaInterest & { responses: IdeaInterestResponse[] }) | null;
|
|
219
|
+
/** Rolling expiry timestamp for Idea events (aligned with Prisma `BashEvent.ideaExpiresAt`) */
|
|
220
|
+
ideaExpiresAt: Date | null;
|
|
221
|
+
/** Whether the Idea has met the auto-approval threshold */
|
|
222
|
+
isAutoApprovable: boolean;
|
|
223
|
+
/** Number of interests needed for auto-publish (see DEFAULT_IDEA_INTEREST_THRESHOLD) */
|
|
224
|
+
ideaInterestThreshold: number | null;
|
|
225
|
+
/** GET /event/:id when status=Idea — distinct service providers who sent a service-offer notification */
|
|
226
|
+
ideaServiceProviderInterestCount?: number;
|
|
196
227
|
// Event page visual customisation (stored as scalar columns on BashEvent)
|
|
197
228
|
backgroundImage: string | null;
|
|
198
229
|
themeColor: string | null;
|
|
199
230
|
}
|
|
200
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Incoming create/update payload (e.g. Express JSON body): core date fields may still be ISO strings
|
|
234
|
+
* until normalized in the API (`getBashEventWithNormalizedDates` / `upsertBashEvent`).
|
|
235
|
+
*/
|
|
236
|
+
export type BashEventUpsertWireInput = Override<
|
|
237
|
+
DeepPartial<BashEventExt>,
|
|
238
|
+
Partial<BashEventExt> & {
|
|
239
|
+
startDateTime?: Date | string | null;
|
|
240
|
+
endDateTime?: Date | string | null;
|
|
241
|
+
ideaExpiresAt?: Date | string | null;
|
|
242
|
+
}
|
|
243
|
+
>;
|
|
244
|
+
|
|
201
245
|
export const TICKET_TIER_DATA_TO_INCLUDE = {
|
|
202
246
|
tickets: {
|
|
203
247
|
include: {
|
|
@@ -846,7 +890,6 @@ export interface TargetAudienceExt extends TargetAudience {
|
|
|
846
890
|
tags?: string[];
|
|
847
891
|
createdAt?: Date;
|
|
848
892
|
updatedAt?: Date;
|
|
849
|
-
notes?: string;
|
|
850
893
|
isHighlighted?: boolean;
|
|
851
894
|
associatedCampaigns?: string[];
|
|
852
895
|
icon?: string;
|
|
@@ -866,6 +909,9 @@ export const BASH_NOTIFICATION_DATA_TO_INCLUDE = {
|
|
|
866
909
|
creator: {
|
|
867
910
|
select: {
|
|
868
911
|
image: true,
|
|
912
|
+
uploadedImage: true,
|
|
913
|
+
imageUpdatedAt: true,
|
|
914
|
+
uploadedImageUpdatedAt: true,
|
|
869
915
|
},
|
|
870
916
|
},
|
|
871
917
|
service: {
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,7 @@ export * from "./utils/entityUtils.js";
|
|
|
46
46
|
export * from "./utils/generalDateTimeUtils.js";
|
|
47
47
|
export * from "./utils/luxonUtils.js";
|
|
48
48
|
export * from "./utils/mathUtils.js";
|
|
49
|
+
export * from "./utils/birthdayExperienceQuality.js";
|
|
49
50
|
export * from "./utils/service/apiServiceBookingApiUtils.js";
|
|
50
51
|
export * from "./utils/service/frontendServiceBookingUtils.js";
|
|
51
52
|
export * from "./utils/service/serviceBookingStatusUtils.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
averageExperienceStars,
|
|
3
|
+
EXPERIENCE_STAR_BANDS,
|
|
4
|
+
} from "../birthdayExperienceQuality.js";
|
|
5
|
+
|
|
6
|
+
describe("averageExperienceStars", () => {
|
|
7
|
+
it("returns null when there are no votes", () => {
|
|
8
|
+
expect(
|
|
9
|
+
averageExperienceStars({ Awesome: 0, Okay: 0, Lame: 0 })
|
|
10
|
+
).toBeNull();
|
|
11
|
+
expect(averageExperienceStars(undefined)).toBeNull();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("returns midpoint when all votes are one tier", () => {
|
|
15
|
+
const awesomeMid =
|
|
16
|
+
(EXPERIENCE_STAR_BANDS.Awesome.min +
|
|
17
|
+
EXPERIENCE_STAR_BANDS.Awesome.max) /
|
|
18
|
+
2;
|
|
19
|
+
expect(
|
|
20
|
+
averageExperienceStars({ Awesome: 2, Okay: 0, Lame: 0 })
|
|
21
|
+
).toBeCloseTo(awesomeMid);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("blends mixed votes", () => {
|
|
25
|
+
const awesomeMid =
|
|
26
|
+
(EXPERIENCE_STAR_BANDS.Awesome.min +
|
|
27
|
+
EXPERIENCE_STAR_BANDS.Awesome.max) /
|
|
28
|
+
2;
|
|
29
|
+
const lameMid =
|
|
30
|
+
(EXPERIENCE_STAR_BANDS.Lame.min + EXPERIENCE_STAR_BANDS.Lame.max) / 2;
|
|
31
|
+
const a = averageExperienceStars({ Awesome: 1, Okay: 0, Lame: 1 });
|
|
32
|
+
expect(a).toBeCloseTo((awesomeMid + lameMid) / 2);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -30,6 +30,21 @@ export function databaseAddressStringToAddressValues(addressString: string | und
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* City, state/region, and country for compact card/list UIs (no street or full formatted address).
|
|
35
|
+
* Detail pages should use {@link databaseAddressStringToOneLineString} or full address fields.
|
|
36
|
+
*/
|
|
37
|
+
export function formatLocalityForCardDisplay(
|
|
38
|
+
city?: string | null,
|
|
39
|
+
state?: string | null,
|
|
40
|
+
country?: string | null
|
|
41
|
+
): string {
|
|
42
|
+
const parts = [city?.trim(), state?.trim(), country?.trim()].filter(
|
|
43
|
+
(p): p is string => !!p && p.length > 0
|
|
44
|
+
);
|
|
45
|
+
return parts.join(", ");
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
export function databaseAddressStringToOneLineString(addressString: string | undefined | null): string {
|
|
34
49
|
if (addressString) {
|
|
35
50
|
// Special handling for place-only addresses
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Quality of Experience math for birthday freebie catalog offers.
|
|
3
|
+
* Vote tiers map to star bands; the displayed score is a weighted average of band midpoints (0–5).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type BirthdayExperienceVoteTier = "Awesome" | "Okay" | "Lame";
|
|
7
|
+
|
|
8
|
+
export interface BirthdayExperienceVoteCounts {
|
|
9
|
+
Awesome: number;
|
|
10
|
+
Okay: number;
|
|
11
|
+
Lame: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const EXPERIENCE_STAR_BANDS: Record<
|
|
15
|
+
BirthdayExperienceVoteTier,
|
|
16
|
+
{ min: number; max: number; label: string }
|
|
17
|
+
> = {
|
|
18
|
+
Lame: { min: 0, max: 1.999, label: "0–2.0" },
|
|
19
|
+
Okay: { min: 2.0, max: 3.499, label: "2.0–3.5" },
|
|
20
|
+
Awesome: { min: 3.5, max: 5.0, label: "3.5–5.0" },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const MIDPOINT: Record<BirthdayExperienceVoteTier, number> = {
|
|
24
|
+
Lame: (EXPERIENCE_STAR_BANDS.Lame.min + EXPERIENCE_STAR_BANDS.Lame.max) / 2,
|
|
25
|
+
Okay: (EXPERIENCE_STAR_BANDS.Okay.min + EXPERIENCE_STAR_BANDS.Okay.max) / 2,
|
|
26
|
+
Awesome:
|
|
27
|
+
(EXPERIENCE_STAR_BANDS.Awesome.min + EXPERIENCE_STAR_BANDS.Awesome.max) /
|
|
28
|
+
2,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Weighted average of midpoint stars for each vote tier. Null when there are no votes.
|
|
33
|
+
*/
|
|
34
|
+
export function averageExperienceStars(
|
|
35
|
+
votes: BirthdayExperienceVoteCounts | null | undefined
|
|
36
|
+
): number | null {
|
|
37
|
+
if (!votes) return null;
|
|
38
|
+
const n = votes.Awesome + votes.Okay + votes.Lame;
|
|
39
|
+
if (n === 0) return null;
|
|
40
|
+
const sum =
|
|
41
|
+
votes.Awesome * MIDPOINT.Awesome +
|
|
42
|
+
votes.Okay * MIDPOINT.Okay +
|
|
43
|
+
votes.Lame * MIDPOINT.Lame;
|
|
44
|
+
return sum / n;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function formatExperienceStars(value: number): string {
|
|
48
|
+
return value.toFixed(1);
|
|
49
|
+
}
|