@bash-app/bash-common 30.293.0 → 30.295.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/__tests__/bashSectionTitleForBashType.test.js +3 -0
- package/dist/__tests__/bashSectionTitleForBashType.test.js.map +1 -1
- package/dist/definitions.d.ts +77 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +1 -0
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +7 -0
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/membershipDefinitions.d.ts +9 -0
- package/dist/membershipDefinitions.d.ts.map +1 -1
- package/dist/membershipDefinitions.js +10 -1
- package/dist/membershipDefinitions.js.map +1 -1
- package/dist/utils/__tests__/reputationUtils.serviceListing.test.js +18 -0
- package/dist/utils/__tests__/reputationUtils.serviceListing.test.js.map +1 -1
- package/dist/utils/__tests__/reviewDimensionUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/reviewDimensionUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/reviewDimensionUtils.test.js +48 -0
- package/dist/utils/__tests__/reviewDimensionUtils.test.js.map +1 -0
- package/dist/utils/__tests__/trustScoreUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/trustScoreUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/trustScoreUtils.test.js +101 -0
- package/dist/utils/__tests__/trustScoreUtils.test.js.map +1 -0
- package/dist/utils/reputationUtils.d.ts.map +1 -1
- package/dist/utils/reputationUtils.js +7 -1
- package/dist/utils/reputationUtils.js.map +1 -1
- package/dist/utils/reviewDimensionUtils.d.ts +33 -0
- package/dist/utils/reviewDimensionUtils.d.ts.map +1 -0
- package/dist/utils/reviewDimensionUtils.js +35 -0
- package/dist/utils/reviewDimensionUtils.js.map +1 -0
- package/dist/utils/trustScoreUtils.d.ts +25 -0
- package/dist/utils/trustScoreUtils.d.ts.map +1 -0
- package/dist/utils/trustScoreUtils.js +99 -0
- package/dist/utils/trustScoreUtils.js.map +1 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +133 -15
- package/src/__tests__/bashSectionTitleForBashType.test.ts +4 -0
- package/src/definitions.ts +85 -1
- package/src/extendedSchemas.ts +7 -0
- package/src/index.ts +2 -0
- package/src/membershipDefinitions.ts +12 -1
- package/src/utils/__tests__/reputationUtils.serviceListing.test.ts +24 -0
- package/src/utils/__tests__/reviewDimensionUtils.test.ts +65 -0
- package/src/utils/__tests__/trustScoreUtils.test.ts +105 -0
- package/src/utils/reputationUtils.ts +7 -1
- package/src/utils/reviewDimensionUtils.ts +70 -0
- package/src/utils/trustScoreUtils.ts +136 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const MIN_DIM = 1;
|
|
2
|
+
const MAX_DIM = 5;
|
|
3
|
+
/** Validate optional 1–5 dimension; returns null when omitted. */
|
|
4
|
+
export function parseOptionalDimensionRating(value, fieldName) {
|
|
5
|
+
if (value === null || value === undefined) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const n = Number(value);
|
|
9
|
+
if (!Number.isInteger(n) || n < MIN_DIM || n > MAX_DIM) {
|
|
10
|
+
throw new Error(`${fieldName} must be an integer from ${MIN_DIM} to ${MAX_DIM}`);
|
|
11
|
+
}
|
|
12
|
+
return n;
|
|
13
|
+
}
|
|
14
|
+
export function parseRequiredOverallRating(value) {
|
|
15
|
+
const n = Number(value);
|
|
16
|
+
if (!Number.isInteger(n) || n < MIN_DIM || n > MAX_DIM) {
|
|
17
|
+
throw new Error(`Overall rating must be an integer from ${MIN_DIM} to ${MAX_DIM}`);
|
|
18
|
+
}
|
|
19
|
+
return n;
|
|
20
|
+
}
|
|
21
|
+
export function averageNullable(values) {
|
|
22
|
+
const nums = values.filter((v) => typeof v === "number" && !Number.isNaN(v));
|
|
23
|
+
if (nums.length === 0)
|
|
24
|
+
return null;
|
|
25
|
+
const sum = nums.reduce((a, b) => a + b, 0);
|
|
26
|
+
return Math.round((sum / nums.length) * 100) / 100;
|
|
27
|
+
}
|
|
28
|
+
export function percentTrue(values) {
|
|
29
|
+
const defined = values.filter((v) => typeof v === "boolean");
|
|
30
|
+
if (defined.length === 0)
|
|
31
|
+
return null;
|
|
32
|
+
const yes = defined.filter(Boolean).length;
|
|
33
|
+
return Math.round((yes / defined.length) * 100);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=reviewDimensionUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewDimensionUtils.js","sourceRoot":"","sources":["../../src/utils/reviewDimensionUtils.ts"],"names":[],"mappings":"AA+BA,MAAM,OAAO,GAAG,CAAC,CAAC;AAClB,MAAM,OAAO,GAAG,CAAC,CAAC;AAElB,kEAAkE;AAClE,MAAM,UAAU,4BAA4B,CAC1C,KAA8B,EAC9B,SAAiB;IAEjB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,4BAA4B,OAAO,OAAO,OAAO,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACvD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,0CAA0C,OAAO,OAAO,OAAO,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAqC;IACnE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1F,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAsC;IAChE,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAgB,EAAE,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC;IAC3E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type TrustScoreActorType = "host" | "service";
|
|
2
|
+
export interface TrustScoreInputs {
|
|
3
|
+
bayesianReviewScore: number | null;
|
|
4
|
+
completedVolume: number;
|
|
5
|
+
cancellationRate: number;
|
|
6
|
+
responseRate: number;
|
|
7
|
+
identityVerified: boolean;
|
|
8
|
+
verifiedAttendanceRate?: number;
|
|
9
|
+
verifiedCompletionRate?: number;
|
|
10
|
+
accountAgeDays: number;
|
|
11
|
+
disputeRate: number;
|
|
12
|
+
repeatCustomerRate: number;
|
|
13
|
+
}
|
|
14
|
+
export interface TrustScoreBreakdown {
|
|
15
|
+
score: number | null;
|
|
16
|
+
components: Record<string, number>;
|
|
17
|
+
weights: Record<string, number>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Compute a 0–100 trust score from normalized component signals (0–1 each).
|
|
21
|
+
*/
|
|
22
|
+
export declare function computeTrustScoreFromInputs(actorType: TrustScoreActorType, inputs: TrustScoreInputs): TrustScoreBreakdown;
|
|
23
|
+
/** Convenience when only review stats are available (early bootstrap). */
|
|
24
|
+
export declare function trustScoreFromReviewAverage(actorType: TrustScoreActorType, averageRating: number | null | undefined, totalRatings: number | null | undefined): number | null;
|
|
25
|
+
//# sourceMappingURL=trustScoreUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trustScoreUtils.d.ts","sourceRoot":"","sources":["../../src/utils/trustScoreUtils.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AA6CD;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,mBAAmB,EAC9B,MAAM,EAAE,gBAAgB,GACvB,mBAAmB,CAyCrB;AAED,0EAA0E;AAC1E,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,mBAAmB,EAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACtC,MAAM,GAAG,IAAI,CAef"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { bayesianAverage } from "./reputationUtils.js";
|
|
2
|
+
const HOST_WEIGHTS = {
|
|
3
|
+
reviews: 0.35,
|
|
4
|
+
volume: 0.15,
|
|
5
|
+
cancellation: 0.1,
|
|
6
|
+
response: 0.1,
|
|
7
|
+
identity: 0.1,
|
|
8
|
+
attendance: 0.05,
|
|
9
|
+
accountAge: 0.05,
|
|
10
|
+
disputes: 0.05,
|
|
11
|
+
repeat: 0.05,
|
|
12
|
+
};
|
|
13
|
+
const SERVICE_WEIGHTS = {
|
|
14
|
+
reviews: 0.4,
|
|
15
|
+
volume: 0.15,
|
|
16
|
+
cancellation: 0.1,
|
|
17
|
+
response: 0.1,
|
|
18
|
+
identity: 0.1,
|
|
19
|
+
completion: 0.1,
|
|
20
|
+
accountAge: 0.05,
|
|
21
|
+
disputes: 0.05,
|
|
22
|
+
repeat: 0.05,
|
|
23
|
+
};
|
|
24
|
+
function clamp01(n) {
|
|
25
|
+
if (n < 0)
|
|
26
|
+
return 0;
|
|
27
|
+
if (n > 1)
|
|
28
|
+
return 1;
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
function logScaledVolume(count, cap = 100) {
|
|
32
|
+
if (count <= 0)
|
|
33
|
+
return 0;
|
|
34
|
+
return clamp01(Math.log10(count + 1) / Math.log10(cap + 1));
|
|
35
|
+
}
|
|
36
|
+
function accountAgeScore(days) {
|
|
37
|
+
return clamp01(Math.log10(Math.max(days, 1) + 1) / Math.log10(365 * 3 + 1));
|
|
38
|
+
}
|
|
39
|
+
function inverseRate(rate) {
|
|
40
|
+
return clamp01(1 - clamp01(rate));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Compute a 0–100 trust score from normalized component signals (0–1 each).
|
|
44
|
+
*/
|
|
45
|
+
export function computeTrustScoreFromInputs(actorType, inputs) {
|
|
46
|
+
const weights = actorType === "host" ? HOST_WEIGHTS : SERVICE_WEIGHTS;
|
|
47
|
+
const reviewComponent = inputs.bayesianReviewScore != null
|
|
48
|
+
? clamp01(inputs.bayesianReviewScore / 5)
|
|
49
|
+
: 0.5;
|
|
50
|
+
const components = {
|
|
51
|
+
reviews: reviewComponent,
|
|
52
|
+
volume: logScaledVolume(inputs.completedVolume),
|
|
53
|
+
cancellation: inverseRate(inputs.cancellationRate),
|
|
54
|
+
response: clamp01(inputs.responseRate),
|
|
55
|
+
identity: inputs.identityVerified ? 1 : 0,
|
|
56
|
+
accountAge: accountAgeScore(inputs.accountAgeDays),
|
|
57
|
+
disputes: inverseRate(inputs.disputeRate),
|
|
58
|
+
repeat: clamp01(inputs.repeatCustomerRate),
|
|
59
|
+
};
|
|
60
|
+
if (actorType === "host") {
|
|
61
|
+
components.attendance = clamp01(inputs.verifiedAttendanceRate ?? 0);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
components.completion = clamp01(inputs.verifiedCompletionRate ?? 0);
|
|
65
|
+
}
|
|
66
|
+
let weighted = 0;
|
|
67
|
+
let weightSum = 0;
|
|
68
|
+
for (const [key, weight] of Object.entries(weights)) {
|
|
69
|
+
const val = components[key];
|
|
70
|
+
if (val == null)
|
|
71
|
+
continue;
|
|
72
|
+
weighted += val * weight;
|
|
73
|
+
weightSum += weight;
|
|
74
|
+
}
|
|
75
|
+
if (weightSum === 0) {
|
|
76
|
+
return { score: null, components, weights };
|
|
77
|
+
}
|
|
78
|
+
const normalized = weighted / weightSum;
|
|
79
|
+
const score = Math.round(normalized * 100);
|
|
80
|
+
return { score, components, weights };
|
|
81
|
+
}
|
|
82
|
+
/** Convenience when only review stats are available (early bootstrap). */
|
|
83
|
+
export function trustScoreFromReviewAverage(actorType, averageRating, totalRatings) {
|
|
84
|
+
const bayesian = bayesianAverage(averageRating, totalRatings);
|
|
85
|
+
const result = computeTrustScoreFromInputs(actorType, {
|
|
86
|
+
bayesianReviewScore: bayesian,
|
|
87
|
+
completedVolume: totalRatings ?? 0,
|
|
88
|
+
cancellationRate: 0,
|
|
89
|
+
responseRate: 1,
|
|
90
|
+
identityVerified: false,
|
|
91
|
+
accountAgeDays: 30,
|
|
92
|
+
disputeRate: 0,
|
|
93
|
+
repeatCustomerRate: 0,
|
|
94
|
+
verifiedAttendanceRate: 0,
|
|
95
|
+
verifiedCompletionRate: 0,
|
|
96
|
+
});
|
|
97
|
+
return result.score;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=trustScoreUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trustScoreUtils.js","sourceRoot":"","sources":["../../src/utils/trustScoreUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -91,6 +91,7 @@ model BashFeedPost {
|
|
|
91
91
|
saves BashFeedSave[]
|
|
92
92
|
reposts BashFeedRepost[]
|
|
93
93
|
helpfulVotes ReviewHelpfulVote[]
|
|
94
|
+
serviceReviews ServiceReview[]
|
|
94
95
|
|
|
95
96
|
@@unique([userId, repostOfPostId])
|
|
96
97
|
@@index([bashId])
|
|
@@ -345,6 +346,8 @@ model CompetitionPrizeOffer {
|
|
|
345
346
|
sponsorLabel String?
|
|
346
347
|
sourceServiceId String?
|
|
347
348
|
sourceSku String?
|
|
349
|
+
/// Existing prize slot the offerer wants to sponsor (optional)
|
|
350
|
+
targetPrizeId String?
|
|
348
351
|
hostNote String?
|
|
349
352
|
createdAt DateTime @default(now())
|
|
350
353
|
updatedAt DateTime @updatedAt
|
|
@@ -352,10 +355,12 @@ model CompetitionPrizeOffer {
|
|
|
352
355
|
competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)
|
|
353
356
|
offeredBy User @relation("CompetitionPrizeOffersSubmitted", fields: [offeredByUserId], references: [id], onDelete: Cascade)
|
|
354
357
|
sourceService Service? @relation("CompetitionPrizeOfferSourceService", fields: [sourceServiceId], references: [id], onDelete: SetNull)
|
|
358
|
+
targetPrize Prize? @relation("CompetitionPrizeOfferTarget", fields: [targetPrizeId], references: [id], onDelete: SetNull)
|
|
355
359
|
|
|
356
360
|
@@index([competitionId])
|
|
357
361
|
@@index([offeredByUserId])
|
|
358
362
|
@@index([status])
|
|
363
|
+
@@index([targetPrizeId])
|
|
359
364
|
}
|
|
360
365
|
|
|
361
366
|
model CompetitionParticipant {
|
|
@@ -969,6 +974,8 @@ model BashEvent {
|
|
|
969
974
|
sentReminders SentReminder[]
|
|
970
975
|
recurrence Recurrence?
|
|
971
976
|
reviews Review[]
|
|
977
|
+
serviceReviews ServiceReview[]
|
|
978
|
+
providerHostReviews ProviderHostReview[]
|
|
972
979
|
serviceBookings ServiceBooking[]
|
|
973
980
|
sponsorBookingRequests SponsorBookingRequest[] @relation("SponsorBookingEvent")
|
|
974
981
|
sponsorships SponsoredEvent[]
|
|
@@ -1963,15 +1970,23 @@ enum BackgroundEffect {
|
|
|
1963
1970
|
Starfield
|
|
1964
1971
|
}
|
|
1965
1972
|
|
|
1973
|
+
enum EventDiscussionTopic {
|
|
1974
|
+
General
|
|
1975
|
+
Icebreaker
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1966
1978
|
model EventDiscussionComment {
|
|
1967
|
-
id String
|
|
1979
|
+
id String @id @default(cuid())
|
|
1968
1980
|
bashEventId String
|
|
1969
1981
|
authorId String
|
|
1970
|
-
body String
|
|
1982
|
+
body String @db.Text
|
|
1971
1983
|
mentionedUserIds String[] // User IDs mentioned in the comment
|
|
1972
1984
|
parentId String? // For nested replies (mirrors BashFeedComment)
|
|
1973
|
-
|
|
1974
|
-
|
|
1985
|
+
topic EventDiscussionTopic @default(General)
|
|
1986
|
+
/// Snapshot of the icebreaker prompt this answer belongs to (Icebreaker topic only).
|
|
1987
|
+
icebreakerPrompt String? @db.Text
|
|
1988
|
+
createdAt DateTime @default(now())
|
|
1989
|
+
updatedAt DateTime @updatedAt
|
|
1975
1990
|
|
|
1976
1991
|
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
1977
1992
|
author User @relation("EventDiscussionComments", fields: [authorId], references: [id], onDelete: Cascade)
|
|
@@ -1982,6 +1997,7 @@ model EventDiscussionComment {
|
|
|
1982
1997
|
@@index([bashEventId])
|
|
1983
1998
|
@@index([authorId])
|
|
1984
1999
|
@@index([parentId])
|
|
2000
|
+
@@index([bashEventId, topic, icebreakerPrompt])
|
|
1985
2001
|
}
|
|
1986
2002
|
|
|
1987
2003
|
model EventDiscussionCommentLike {
|
|
@@ -2716,7 +2732,7 @@ model Recurrence {
|
|
|
2716
2732
|
repeatYearlyDate DateTime?
|
|
2717
2733
|
// Per-occurrence exceptions for the computed series (no occurrence rows are materialized):
|
|
2718
2734
|
// dates the host removed from the series (matched against the generated occurrence start instants).
|
|
2719
|
-
excludedDates DateTime[]
|
|
2735
|
+
excludedDates DateTime[] @default([])
|
|
2720
2736
|
// Per-occurrence date/time moves: JSON map of original-occurrence ISO start -> { startDateTime, endDateTime? }.
|
|
2721
2737
|
occurrenceOverrides Json?
|
|
2722
2738
|
createdAt DateTime @default(now())
|
|
@@ -2912,6 +2928,8 @@ model Prize {
|
|
|
2912
2928
|
winnerUserId String?
|
|
2913
2929
|
wonAt DateTime? // When the prize was awarded
|
|
2914
2930
|
claimedAt DateTime? // When the prize was claimed
|
|
2931
|
+
/// Host-set amount sponsors pay to fund this prize slot (cents). Shown when seeking prize contributions.
|
|
2932
|
+
sponsorCostCents Int?
|
|
2915
2933
|
/// Platform user who donated this prize (optional)
|
|
2916
2934
|
contributorUserId String?
|
|
2917
2935
|
/// Sponsor marketplace service the prize was picked from (optional)
|
|
@@ -2944,6 +2962,7 @@ model Prize {
|
|
|
2944
2962
|
winner User? @relation(fields: [winnerUserId], references: [id], onDelete: Cascade)
|
|
2945
2963
|
contributor User? @relation("PrizeContributor", fields: [contributorUserId], references: [id], onDelete: SetNull)
|
|
2946
2964
|
sourceService Service? @relation("PrizeSourceService", fields: [sourceServiceId], references: [id], onDelete: SetNull)
|
|
2965
|
+
sponsorshipOffers CompetitionPrizeOffer[] @relation("CompetitionPrizeOfferTarget")
|
|
2947
2966
|
bashPointsTransaction BashCreditTransaction? @relation(fields: [bashPointsTransactionId], references: [id], onDelete: SetNull)
|
|
2948
2967
|
|
|
2949
2968
|
@@index([competitionId])
|
|
@@ -2955,11 +2974,22 @@ model Prize {
|
|
|
2955
2974
|
|
|
2956
2975
|
model Review {
|
|
2957
2976
|
id String @id @default(cuid())
|
|
2977
|
+
/// Overall rating (1–5). Headline aggregate uses this field.
|
|
2958
2978
|
rating Int
|
|
2959
2979
|
creatorId String
|
|
2960
2980
|
bashEventId String
|
|
2961
2981
|
createdAt DateTime @default(now())
|
|
2962
2982
|
updatedAt DateTime @updatedAt
|
|
2983
|
+
/// Optional multi-dimensional event ratings (Overall required via rating).
|
|
2984
|
+
organizationRating Int?
|
|
2985
|
+
atmosphereRating Int?
|
|
2986
|
+
valueRating Int?
|
|
2987
|
+
safetyRating Int?
|
|
2988
|
+
wouldAttendAgain Boolean?
|
|
2989
|
+
/// Trust badges stamped at write time from platform verification checks.
|
|
2990
|
+
verifiedBooking Boolean @default(false)
|
|
2991
|
+
verifiedAttendance Boolean @default(false)
|
|
2992
|
+
verifiedServiceCompletion Boolean @default(false)
|
|
2963
2993
|
/// Public reply from the event host (service provider) to the reviewer.
|
|
2964
2994
|
hostReplyText String? @db.Text
|
|
2965
2995
|
hostReplyAt DateTime?
|
|
@@ -2971,22 +3001,93 @@ model Review {
|
|
|
2971
3001
|
@@unique([creatorId, bashEventId])
|
|
2972
3002
|
}
|
|
2973
3003
|
|
|
3004
|
+
/// Bash-native review of a service listing at a specific event (attendee or host).
|
|
3005
|
+
model ServiceReview {
|
|
3006
|
+
id String @id @default(cuid())
|
|
3007
|
+
creatorId String
|
|
3008
|
+
serviceId String
|
|
3009
|
+
bashEventId String
|
|
3010
|
+
reviewerRole ServiceReviewerRole
|
|
3011
|
+
overallRating Int
|
|
3012
|
+
communicationRating Int?
|
|
3013
|
+
professionalismRating Int?
|
|
3014
|
+
qualityRating Int?
|
|
3015
|
+
wouldHireAgain Boolean?
|
|
3016
|
+
verifiedBooking Boolean @default(false)
|
|
3017
|
+
verifiedAttendance Boolean @default(false)
|
|
3018
|
+
verifiedServiceCompletion Boolean @default(false)
|
|
3019
|
+
comment String? @db.Text
|
|
3020
|
+
providerReplyText String? @db.Text
|
|
3021
|
+
providerReplyAt DateTime?
|
|
3022
|
+
/// Null until blind host↔provider window reveals (attendee reviews publish immediately).
|
|
3023
|
+
publishedAt DateTime?
|
|
3024
|
+
feedPostId String?
|
|
3025
|
+
createdAt DateTime @default(now())
|
|
3026
|
+
updatedAt DateTime @updatedAt
|
|
3027
|
+
|
|
3028
|
+
creator User @relation("ServiceReviewsCreated", fields: [creatorId], references: [id], onDelete: Cascade)
|
|
3029
|
+
service Service @relation(fields: [serviceId], references: [id], onDelete: Cascade)
|
|
3030
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
3031
|
+
feedPost BashFeedPost? @relation(fields: [feedPostId], references: [id], onDelete: SetNull)
|
|
3032
|
+
helpfulVotes ReviewHelpfulVote[]
|
|
3033
|
+
|
|
3034
|
+
@@unique([creatorId, serviceId, bashEventId])
|
|
3035
|
+
@@index([serviceId, publishedAt])
|
|
3036
|
+
@@index([bashEventId])
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
/// Provider reviews the host after a confirmed booking (blind mutual window with host ServiceReview).
|
|
3040
|
+
model ProviderHostReview {
|
|
3041
|
+
id String @id @default(cuid())
|
|
3042
|
+
creatorId String
|
|
3043
|
+
hostUserId String
|
|
3044
|
+
serviceId String
|
|
3045
|
+
bashEventId String
|
|
3046
|
+
overallRating Int
|
|
3047
|
+
communicationRating Int?
|
|
3048
|
+
professionalismRating Int?
|
|
3049
|
+
qualityRating Int?
|
|
3050
|
+
wouldWorkAgain Boolean?
|
|
3051
|
+
verifiedBooking Boolean @default(false)
|
|
3052
|
+
verifiedServiceCompletion Boolean @default(false)
|
|
3053
|
+
comment String? @db.Text
|
|
3054
|
+
publishedAt DateTime?
|
|
3055
|
+
createdAt DateTime @default(now())
|
|
3056
|
+
updatedAt DateTime @updatedAt
|
|
3057
|
+
|
|
3058
|
+
creator User @relation("ProviderHostReviewsCreated", fields: [creatorId], references: [id], onDelete: Cascade)
|
|
3059
|
+
host User @relation("ProviderHostReviewsReceived", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
3060
|
+
service Service @relation(fields: [serviceId], references: [id], onDelete: Cascade)
|
|
3061
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
3062
|
+
|
|
3063
|
+
@@unique([creatorId, bashEventId, serviceId])
|
|
3064
|
+
@@index([hostUserId, publishedAt])
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
enum ServiceReviewerRole {
|
|
3068
|
+
Attendee
|
|
3069
|
+
Host
|
|
3070
|
+
}
|
|
3071
|
+
|
|
2974
3072
|
/// Profile-only helpfulness votes on host reviews (Review rows or BashFeed posts shown as reviews).
|
|
2975
3073
|
model ReviewHelpfulVote {
|
|
2976
|
-
id
|
|
2977
|
-
userId
|
|
2978
|
-
reviewId
|
|
2979
|
-
feedPostId
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
3074
|
+
id String @id @default(cuid())
|
|
3075
|
+
userId String
|
|
3076
|
+
reviewId String?
|
|
3077
|
+
feedPostId String?
|
|
3078
|
+
serviceReviewId String?
|
|
3079
|
+
isHelpful Boolean
|
|
3080
|
+
createdAt DateTime @default(now())
|
|
3081
|
+
updatedAt DateTime @updatedAt
|
|
2983
3082
|
|
|
2984
|
-
user
|
|
2985
|
-
review
|
|
2986
|
-
feedPost
|
|
3083
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
3084
|
+
review Review? @relation(fields: [reviewId], references: [id], onDelete: Cascade)
|
|
3085
|
+
feedPost BashFeedPost? @relation(fields: [feedPostId], references: [id], onDelete: Cascade)
|
|
3086
|
+
serviceReview ServiceReview? @relation(fields: [serviceReviewId], references: [id], onDelete: Cascade)
|
|
2987
3087
|
|
|
2988
3088
|
@@index([reviewId])
|
|
2989
3089
|
@@index([feedPostId])
|
|
3090
|
+
@@index([serviceReviewId])
|
|
2990
3091
|
@@index([userId])
|
|
2991
3092
|
}
|
|
2992
3093
|
|
|
@@ -2997,6 +3098,9 @@ model GoogleReview {
|
|
|
2997
3098
|
text String?
|
|
2998
3099
|
createdAt DateTime @default(now())
|
|
2999
3100
|
serviceId String
|
|
3101
|
+
service Service @relation(fields: [serviceId], references: [id], onDelete: Cascade)
|
|
3102
|
+
|
|
3103
|
+
@@index([serviceId])
|
|
3000
3104
|
}
|
|
3001
3105
|
|
|
3002
3106
|
model Session {
|
|
@@ -3122,6 +3226,10 @@ model User {
|
|
|
3122
3226
|
status UserStatus
|
|
3123
3227
|
hostRating Float?
|
|
3124
3228
|
totalRatings Int?
|
|
3229
|
+
/// Secondary composite trust signal (0–100) for host profile detail.
|
|
3230
|
+
trustScore Int?
|
|
3231
|
+
trustScoreUpdatedAt DateTime?
|
|
3232
|
+
verifiedBookingsCount Int @default(0)
|
|
3125
3233
|
totalEventsHosted Int? @default(0) // Total published/completed events hosted
|
|
3126
3234
|
totalAttendeesHosted Int? @default(0) // Total checked-in attendees across all events
|
|
3127
3235
|
totalBookingsAccepted Int? @default(0) // Service provider stat
|
|
@@ -3315,6 +3423,9 @@ model User {
|
|
|
3315
3423
|
remindersAssignedToMe Reminder[] @relation("RemindersAssignedToMe")
|
|
3316
3424
|
sentReminders SentReminder[]
|
|
3317
3425
|
reviews Review[]
|
|
3426
|
+
serviceReviewsCreated ServiceReview[] @relation("ServiceReviewsCreated")
|
|
3427
|
+
providerHostReviewsCreated ProviderHostReview[] @relation("ProviderHostReviewsCreated")
|
|
3428
|
+
providerHostReviewsReceived ProviderHostReview[] @relation("ProviderHostReviewsReceived")
|
|
3318
3429
|
firstFreeListingId String?
|
|
3319
3430
|
createdServices Service[] @relation("CreatedService")
|
|
3320
3431
|
ownedServices Service[] @relation("OwnedService")
|
|
@@ -3996,6 +4107,10 @@ model Service {
|
|
|
3996
4107
|
externalReviewSource String?
|
|
3997
4108
|
externalRating Float?
|
|
3998
4109
|
externalReviewCount Int?
|
|
4110
|
+
/// Secondary composite trust signal (0–100) for detail pages and ranking.
|
|
4111
|
+
trustScore Int?
|
|
4112
|
+
trustScoreUpdatedAt DateTime?
|
|
4113
|
+
verifiedCompletionsCount Int @default(0)
|
|
3999
4114
|
stripeAccountId String?
|
|
4000
4115
|
paymentAccountId String?
|
|
4001
4116
|
targetAudienceId String? @unique
|
|
@@ -4081,6 +4196,9 @@ model Service {
|
|
|
4081
4196
|
competitionPrizeOffersSourced CompetitionPrizeOffer[] @relation("CompetitionPrizeOfferSourceService")
|
|
4082
4197
|
venueMatchCandidates VenueMatchCandidate[]
|
|
4083
4198
|
supplyLeadConversions VenueSupplyLead[] @relation("SupplyLeadConvertedService")
|
|
4199
|
+
googleReviews GoogleReview[]
|
|
4200
|
+
serviceReviews ServiceReview[]
|
|
4201
|
+
providerHostReviews ProviderHostReview[]
|
|
4084
4202
|
|
|
4085
4203
|
@@index([serviceListingStripeSubscriptionId])
|
|
4086
4204
|
@@index([paymentAccountId])
|
|
@@ -44,6 +44,10 @@ describe("bashSectionTitleForBashType", () => {
|
|
|
44
44
|
it("'Trending' → 'Trending'", () => {
|
|
45
45
|
expect(bashSectionTitleForBashType("Trending")).toBe("Trending");
|
|
46
46
|
});
|
|
47
|
+
|
|
48
|
+
it("'Ideas' → 'Ideas'", () => {
|
|
49
|
+
expect(bashSectionTitleForBashType("Ideas")).toBe("Ideas");
|
|
50
|
+
});
|
|
47
51
|
});
|
|
48
52
|
|
|
49
53
|
describe("BashEventType enum values get pluralized", () => {
|
package/src/definitions.ts
CHANGED
|
@@ -1471,6 +1471,7 @@ export const INCOME_RANGE_VALUES: ReadonlyArray<IncomeRange> = [
|
|
|
1471
1471
|
|
|
1472
1472
|
/** Homepage / discovery section labels for synthetic rows (not `BashEventType` enum values). */
|
|
1473
1473
|
const SYNTHETIC_BASH_SECTION_TITLE: Record<string, string> = {
|
|
1474
|
+
Ideas: "Ideas",
|
|
1474
1475
|
Featured: "Featured",
|
|
1475
1476
|
Trending: "Trending",
|
|
1476
1477
|
};
|
|
@@ -1817,11 +1818,19 @@ export interface UserTemporaryBadge {
|
|
|
1817
1818
|
|
|
1818
1819
|
export interface Review {
|
|
1819
1820
|
id: string;
|
|
1820
|
-
rating: number; // 1-5 stars
|
|
1821
|
+
rating: number; // 1-5 stars (Overall)
|
|
1821
1822
|
creatorId: string;
|
|
1822
1823
|
bashEventId: string;
|
|
1823
1824
|
createdAt: string;
|
|
1824
1825
|
updatedAt: string;
|
|
1826
|
+
organizationRating?: number | null;
|
|
1827
|
+
atmosphereRating?: number | null;
|
|
1828
|
+
valueRating?: number | null;
|
|
1829
|
+
safetyRating?: number | null;
|
|
1830
|
+
wouldAttendAgain?: boolean | null;
|
|
1831
|
+
verifiedBooking?: boolean;
|
|
1832
|
+
verifiedAttendance?: boolean;
|
|
1833
|
+
verifiedServiceCompletion?: boolean;
|
|
1825
1834
|
comments?: ReviewComment[];
|
|
1826
1835
|
}
|
|
1827
1836
|
|
|
@@ -1853,10 +1862,83 @@ export interface ReviewWithUser extends Review {
|
|
|
1853
1862
|
|
|
1854
1863
|
export interface ReviewWithUserAndEvent extends ReviewWithUser, ReviewWithEvent {}
|
|
1855
1864
|
|
|
1865
|
+
/** Wizard / API shape for BashEvent.itinerary JSON (see prisma BashEvent.itinerary). */
|
|
1866
|
+
export interface ItineraryItem {
|
|
1867
|
+
id: string;
|
|
1868
|
+
title: string;
|
|
1869
|
+
startTime: string;
|
|
1870
|
+
endTime: string;
|
|
1871
|
+
description?: string;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1856
1874
|
export interface NewReview {
|
|
1857
1875
|
rating: number;
|
|
1858
1876
|
bashEventId: string;
|
|
1859
1877
|
commentContent?: string;
|
|
1878
|
+
organizationRating?: number;
|
|
1879
|
+
atmosphereRating?: number;
|
|
1880
|
+
valueRating?: number;
|
|
1881
|
+
safetyRating?: number;
|
|
1882
|
+
wouldAttendAgain?: boolean;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
export interface ServiceReviewDimensions {
|
|
1886
|
+
communicationRating?: number | null;
|
|
1887
|
+
professionalismRating?: number | null;
|
|
1888
|
+
qualityRating?: number | null;
|
|
1889
|
+
wouldHireAgain?: boolean | null;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
export interface NewServiceReview extends ServiceReviewDimensions {
|
|
1893
|
+
overallRating: number;
|
|
1894
|
+
bashEventId: string;
|
|
1895
|
+
comment?: string;
|
|
1896
|
+
createPost?: boolean;
|
|
1897
|
+
postContent?: string;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
export interface ServiceReviewPublic {
|
|
1901
|
+
id: string;
|
|
1902
|
+
overallRating: number;
|
|
1903
|
+
reviewerRole: "Attendee" | "Host";
|
|
1904
|
+
comment?: string | null;
|
|
1905
|
+
createdAt: string;
|
|
1906
|
+
publishedAt?: string | null;
|
|
1907
|
+
verifiedBooking: boolean;
|
|
1908
|
+
verifiedAttendance: boolean;
|
|
1909
|
+
verifiedServiceCompletion: boolean;
|
|
1910
|
+
communicationRating?: number | null;
|
|
1911
|
+
professionalismRating?: number | null;
|
|
1912
|
+
qualityRating?: number | null;
|
|
1913
|
+
wouldHireAgain?: boolean | null;
|
|
1914
|
+
providerReplyText?: string | null;
|
|
1915
|
+
providerReplyAt?: string | null;
|
|
1916
|
+
feedPostId?: string | null;
|
|
1917
|
+
creator?: {
|
|
1918
|
+
id: string;
|
|
1919
|
+
givenName?: string | null;
|
|
1920
|
+
familyName?: string | null;
|
|
1921
|
+
image?: string | null;
|
|
1922
|
+
};
|
|
1923
|
+
bashEvent?: {
|
|
1924
|
+
id: string;
|
|
1925
|
+
title: string;
|
|
1926
|
+
startDateTime: string;
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
export interface ServiceReviewStats {
|
|
1931
|
+
overallAverage: number | null;
|
|
1932
|
+
totalReviews: number;
|
|
1933
|
+
dimensionAverages: import("./utils/reviewDimensionUtils.js").ReviewDimensionAverages;
|
|
1934
|
+
googleAverage?: number | null;
|
|
1935
|
+
googleCount?: number;
|
|
1936
|
+
trustScore?: number | null;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
export interface TrustScoreBreakdownPublic {
|
|
1940
|
+
score: number | null;
|
|
1941
|
+
components: Record<string, number>;
|
|
1860
1942
|
}
|
|
1861
1943
|
|
|
1862
1944
|
export interface ReviewStats {
|
|
@@ -1869,6 +1951,8 @@ export interface ReviewStats {
|
|
|
1869
1951
|
4: number;
|
|
1870
1952
|
5: number;
|
|
1871
1953
|
};
|
|
1954
|
+
dimensionAverages?: import("./utils/reviewDimensionUtils.js").ReviewDimensionAverages;
|
|
1955
|
+
trustScore?: number | null;
|
|
1872
1956
|
}
|
|
1873
1957
|
|
|
1874
1958
|
/** Helpful / Not helpful tallies on profile review cards (API: host-reviews). */
|
package/src/extendedSchemas.ts
CHANGED
|
@@ -287,6 +287,7 @@ export interface CompetitionPrizeOfferExt {
|
|
|
287
287
|
sponsorLabel?: string | null;
|
|
288
288
|
sourceServiceId?: string | null;
|
|
289
289
|
sourceSku?: string | null;
|
|
290
|
+
targetPrizeId?: string | null;
|
|
290
291
|
hostNote?: string | null;
|
|
291
292
|
createdAt: Date | string;
|
|
292
293
|
respondedAt?: Date | string | null;
|
|
@@ -296,6 +297,12 @@ export interface CompetitionPrizeOfferExt {
|
|
|
296
297
|
serviceName: string | null;
|
|
297
298
|
coverPhoto: string | null;
|
|
298
299
|
} | null;
|
|
300
|
+
targetPrize?: {
|
|
301
|
+
id: string;
|
|
302
|
+
name: string;
|
|
303
|
+
placeName: string | null;
|
|
304
|
+
sponsorCostCents: number | null;
|
|
305
|
+
} | null;
|
|
299
306
|
}
|
|
300
307
|
|
|
301
308
|
/** Competition row + relations; Override keeps new columns visible before consumers refresh Prisma. */
|
package/src/index.ts
CHANGED
|
@@ -407,6 +407,8 @@ export * from "./utils/service/serviceRateUtils.js";
|
|
|
407
407
|
export * from "./utils/serviceAvailabilityDisplay.js";
|
|
408
408
|
export * from "./utils/entertainmentServiceTypeOptions.js";
|
|
409
409
|
export * from "./utils/reputationUtils.js";
|
|
410
|
+
export * from "./utils/reviewDimensionUtils.js";
|
|
411
|
+
export * from "./utils/trustScoreUtils.js";
|
|
410
412
|
export * from "./utils/stripeAccountUtils.js";
|
|
411
413
|
export * from "./utils/userUtils.js";
|
|
412
414
|
export * from "./utils/venueTypeFormatter.js";
|
|
@@ -219,6 +219,17 @@ export const BASHPOINTS_REWARDS: Record<MembershipTier, {
|
|
|
219
219
|
},
|
|
220
220
|
} as const;
|
|
221
221
|
|
|
222
|
+
/** Referral tier qualification thresholds — single source of truth for grant + stats UI. */
|
|
223
|
+
export const AMBASHADOR_QUALIFICATION = {
|
|
224
|
+
freeSignups: 1,
|
|
225
|
+
paidSignups: 1,
|
|
226
|
+
} as const;
|
|
227
|
+
|
|
228
|
+
export const INSIDER_QUALIFICATION = {
|
|
229
|
+
freeSignups: 100,
|
|
230
|
+
paidSignups: 10,
|
|
231
|
+
} as const;
|
|
232
|
+
|
|
222
233
|
/**
|
|
223
234
|
* BashPoints system rules and limits
|
|
224
235
|
*
|
|
@@ -233,7 +244,7 @@ export const BASHPOINTS_CONFIG = {
|
|
|
233
244
|
paymentVerificationBonus: 500,
|
|
234
245
|
|
|
235
246
|
// Tier achievement bonuses (one-time when user reaches each tier)
|
|
236
|
-
ambashadorAchievementBonus: 500, // Ambashador:
|
|
247
|
+
ambashadorAchievementBonus: 500, // Ambashador: 1 free or 1 paid referral
|
|
237
248
|
scoutAchievementBonus: 250, // Scout: first service profile referral
|
|
238
249
|
insiderAchievementBonus: 1000, // Bash Insider: 10 paid or 100 free referrals
|
|
239
250
|
agentAchievementBonus: 500, // Agent: 5+ service profile referrals
|