@bash-app/bash-common 30.225.0 → 30.227.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__/agePolicy.test.d.ts +2 -0
- package/dist/__tests__/agePolicy.test.d.ts.map +1 -0
- package/dist/__tests__/agePolicy.test.js +203 -0
- package/dist/__tests__/agePolicy.test.js.map +1 -0
- package/dist/__tests__/getDisplayGender.test.d.ts +2 -0
- package/dist/__tests__/getDisplayGender.test.d.ts.map +1 -0
- package/dist/__tests__/getDisplayGender.test.js +34 -0
- package/dist/__tests__/getDisplayGender.test.js.map +1 -0
- package/dist/__tests__/statusEnums.test.d.ts +2 -0
- package/dist/__tests__/statusEnums.test.d.ts.map +1 -0
- package/dist/__tests__/statusEnums.test.js +24 -0
- package/dist/__tests__/statusEnums.test.js.map +1 -0
- package/dist/aiApproval.d.ts +89 -0
- package/dist/aiApproval.d.ts.map +1 -0
- package/dist/aiApproval.js +82 -0
- package/dist/aiApproval.js.map +1 -0
- package/dist/definitions.d.ts +44 -2
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +106 -2
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +280 -105
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +34 -2
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/legalTemplates.d.ts +1 -1
- package/dist/legalTemplates.js +1 -1
- package/dist/utils/__tests__/badgeUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/badgeUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/badgeUtils.test.js +157 -0
- package/dist/utils/__tests__/badgeUtils.test.js.map +1 -0
- package/dist/utils/__tests__/bashPointsPaymentUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/bashPointsPaymentUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/bashPointsPaymentUtils.test.js +114 -0
- package/dist/utils/__tests__/bashPointsPaymentUtils.test.js.map +1 -0
- package/dist/utils/__tests__/contentFilterUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/contentFilterUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/contentFilterUtils.test.js +443 -0
- package/dist/utils/__tests__/contentFilterUtils.test.js.map +1 -0
- package/dist/utils/__tests__/displayDateUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/displayDateUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/displayDateUtils.test.js +105 -0
- package/dist/utils/__tests__/displayDateUtils.test.js.map +1 -0
- package/dist/utils/__tests__/flyerUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/flyerUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/flyerUtils.test.js +49 -0
- package/dist/utils/__tests__/flyerUtils.test.js.map +1 -0
- package/dist/utils/__tests__/getBackendHost.test.d.ts +2 -0
- package/dist/utils/__tests__/getBackendHost.test.d.ts.map +1 -0
- package/dist/utils/__tests__/getBackendHost.test.js +104 -0
- package/dist/utils/__tests__/getBackendHost.test.js.map +1 -0
- package/dist/utils/__tests__/getFrontendHost.test.d.ts +2 -0
- package/dist/utils/__tests__/getFrontendHost.test.d.ts.map +1 -0
- package/dist/utils/__tests__/getFrontendHost.test.js +207 -0
- package/dist/utils/__tests__/getFrontendHost.test.js.map +1 -0
- package/dist/utils/__tests__/promoCodesUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/promoCodesUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/promoCodesUtils.test.js +183 -0
- package/dist/utils/__tests__/promoCodesUtils.test.js.map +1 -0
- package/dist/utils/__tests__/qrCodeUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/qrCodeUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/qrCodeUtils.test.js +99 -0
- package/dist/utils/__tests__/qrCodeUtils.test.js.map +1 -0
- package/dist/utils/__tests__/stripeAccountUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/stripeAccountUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/stripeAccountUtils.test.js +75 -0
- package/dist/utils/__tests__/stripeAccountUtils.test.js.map +1 -0
- package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.d.ts +2 -0
- package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.d.ts.map +1 -0
- package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.js +233 -0
- package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.js.map +1 -0
- package/dist/utils/blog/blogCommentDbUtils.d.ts +161 -0
- package/dist/utils/blog/blogCommentDbUtils.d.ts.map +1 -0
- package/dist/utils/blog/blogCommentDbUtils.js +111 -0
- package/dist/utils/blog/blogCommentDbUtils.js.map +1 -0
- package/dist/utils/blog/blogDbUtils.d.ts +10 -0
- package/dist/utils/blog/blogDbUtils.d.ts.map +1 -1
- package/dist/utils/blog/blogDbUtils.js +71 -0
- package/dist/utils/blog/blogDbUtils.js.map +1 -1
- package/dist/utils/flyerUtils.d.ts +42 -0
- package/dist/utils/flyerUtils.d.ts.map +1 -0
- package/dist/utils/flyerUtils.js +104 -0
- package/dist/utils/flyerUtils.js.map +1 -0
- package/dist/utils/urlUtils.d.ts +10 -0
- package/dist/utils/urlUtils.d.ts.map +1 -1
- package/dist/utils/urlUtils.js +24 -5
- package/dist/utils/urlUtils.js.map +1 -1
- package/dist/utils/userUtils.d.ts +32 -0
- package/dist/utils/userUtils.d.ts.map +1 -1
- package/dist/utils/userUtils.js +27 -0
- package/dist/utils/userUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +1196 -845
- package/src/__tests__/agePolicy.test.ts +257 -0
- package/src/__tests__/getDisplayGender.test.ts +50 -0
- package/src/__tests__/statusEnums.test.ts +36 -0
- package/src/aiApproval.ts +118 -0
- package/src/definitions.ts +138 -1
- package/src/extendedSchemas.ts +86 -3
- package/src/index.ts +15 -0
- package/src/legalTemplates.ts +1 -1
- package/src/utils/__tests__/badgeUtils.test.ts +191 -0
- package/src/utils/__tests__/bashPointsPaymentUtils.test.ts +153 -0
- package/src/utils/__tests__/contentFilterUtils.test.ts +547 -0
- package/src/utils/__tests__/displayDateUtils.test.ts +113 -0
- package/src/utils/__tests__/flyerUtils.test.ts +70 -0
- package/src/utils/__tests__/getBackendHost.test.ts +128 -0
- package/src/utils/__tests__/getFrontendHost.test.ts +232 -0
- package/src/utils/__tests__/promoCodesUtils.test.ts +237 -0
- package/src/utils/__tests__/qrCodeUtils.test.ts +143 -0
- package/src/utils/__tests__/stripeAccountUtils.test.ts +93 -0
- package/src/utils/blog/__tests__/getRelatedBlogPosts.test.ts +271 -0
- package/src/utils/blog/blogCommentDbUtils.ts +161 -0
- package/src/utils/blog/blogDbUtils.ts +95 -1
- package/src/utils/flyerUtils.ts +137 -0
- package/src/utils/urlUtils.ts +27 -5
- package/src/utils/userUtils.ts +40 -0
- package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.d.ts +0 -6
- package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.d.ts.map +0 -1
- package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.js +0 -104
- package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.js.map +0 -1
- package/dist/utils/service/serviceRateDBUtils.d.ts +0 -1
- package/dist/utils/service/serviceRateDBUtils.d.ts.map +0 -1
- package/dist/utils/service/serviceRateDBUtils.js +0 -159
- package/dist/utils/service/serviceRateDBUtils.js.map +0 -1
- package/prisma/migrations/add_bash_availability.sql +0 -53
- package/prisma/migrations/add_bashcash_pricing_to_ticket_tier.sql +0 -15
- package/prisma/migrations/add_bashpoints_purchase_tracking.sql +0 -33
- package/prisma/migrations/add_event_group_allow_auto_join.sql +0 -17
- package/prisma/migrations/add_group_member_status_not_going_maybe.sql +0 -5
- package/prisma/migrations/add_groups_and_momentum.sql +0 -135
- package/prisma/migrations/add_groups_momentum_phase0_4.sql +0 -200
- package/prisma/migrations/add_new_feature_tables.sql +0 -140
- package/prisma/migrations/add_pricing_type_enum.sql +0 -88
- package/prisma/migrations/add_tier_privacy_and_fee_handling.sql +0 -29
- package/prisma/migrations/diagnostic_bashcash_columns.sql +0 -157
- package/prisma/migrations/fix_bashcash_referral_code_schema_mismatch.sql +0 -81
- package/prisma/migrations/rename_bashcash_to_bashpoints.sql +0 -183
- package/prisma/migrations/rename_bashcredits_to_bashpoints.sql +0 -96
|
@@ -345,4 +345,98 @@ export async function getRecentBlogPosts(
|
|
|
345
345
|
});
|
|
346
346
|
|
|
347
347
|
return posts as BlogPostExt[];
|
|
348
|
-
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface RelatedBlogPost {
|
|
351
|
+
id: string;
|
|
352
|
+
slug: string | null;
|
|
353
|
+
title: string;
|
|
354
|
+
excerpt: string | null;
|
|
355
|
+
coverImage: string | null;
|
|
356
|
+
readTime: number | null;
|
|
357
|
+
publishedAt: Date | null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Score candidate posts by (1) tag overlap with the source post, (2) shared
|
|
361
|
+
// category, then (3) recency. Drives the "Related posts" module that internal-
|
|
362
|
+
// links blog detail pages -- a major SEO lever per the backlink/SEO plan.
|
|
363
|
+
// Falls back to recent same-category posts when the source has no tags.
|
|
364
|
+
export async function getRelatedBlogPosts(
|
|
365
|
+
prisma: PrismaClient,
|
|
366
|
+
postId: string,
|
|
367
|
+
limit: number = 4
|
|
368
|
+
): Promise<RelatedBlogPost[]> {
|
|
369
|
+
const source = await prisma.blogPost.findUnique({
|
|
370
|
+
where: { id: postId },
|
|
371
|
+
select: {
|
|
372
|
+
id: true,
|
|
373
|
+
categoryId: true,
|
|
374
|
+
tags: { select: { id: true } },
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
if (!source) {
|
|
378
|
+
return [];
|
|
379
|
+
}
|
|
380
|
+
const tagIds = source.tags.map((t) => t.id);
|
|
381
|
+
|
|
382
|
+
const orClauses: Prisma.BlogPostWhereInput[] = [];
|
|
383
|
+
if (tagIds.length > 0) {
|
|
384
|
+
orClauses.push({ tags: { some: { id: { in: tagIds } } } });
|
|
385
|
+
}
|
|
386
|
+
if (source.categoryId) {
|
|
387
|
+
orClauses.push({ categoryId: source.categoryId });
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const where: Prisma.BlogPostWhereInput = {
|
|
391
|
+
id: { not: postId },
|
|
392
|
+
status: BlogStatus.Published,
|
|
393
|
+
publishedAt: { not: null },
|
|
394
|
+
...(orClauses.length > 0 ? { OR: orClauses } : {}),
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const candidates = await prisma.blogPost.findMany({
|
|
398
|
+
where,
|
|
399
|
+
select: {
|
|
400
|
+
id: true,
|
|
401
|
+
slug: true,
|
|
402
|
+
title: true,
|
|
403
|
+
excerpt: true,
|
|
404
|
+
coverImage: true,
|
|
405
|
+
readTime: true,
|
|
406
|
+
publishedAt: true,
|
|
407
|
+
categoryId: true,
|
|
408
|
+
tags: { select: { id: true } },
|
|
409
|
+
},
|
|
410
|
+
orderBy: { publishedAt: 'desc' },
|
|
411
|
+
take: limit * 4,
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
const scored = candidates.map((c) => {
|
|
415
|
+
const sharedTagCount = c.tags.filter((t) => tagIds.includes(t.id)).length;
|
|
416
|
+
const sharedCategory =
|
|
417
|
+
source.categoryId && c.categoryId === source.categoryId ? 1 : 0;
|
|
418
|
+
const recency = c.publishedAt ? c.publishedAt.getTime() : 0;
|
|
419
|
+
return { c, sharedTagCount, sharedCategory, recency };
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
scored.sort((a, b) => {
|
|
423
|
+
if (b.sharedTagCount !== a.sharedTagCount) {
|
|
424
|
+
return b.sharedTagCount - a.sharedTagCount;
|
|
425
|
+
}
|
|
426
|
+
if (b.sharedCategory !== a.sharedCategory) {
|
|
427
|
+
return b.sharedCategory - a.sharedCategory;
|
|
428
|
+
}
|
|
429
|
+
return b.recency - a.recency;
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
return scored.slice(0, limit).map(({ c }) => ({
|
|
433
|
+
id: c.id,
|
|
434
|
+
slug: c.slug,
|
|
435
|
+
title: c.title,
|
|
436
|
+
excerpt: c.excerpt,
|
|
437
|
+
coverImage: c.coverImage,
|
|
438
|
+
readTime: c.readTime,
|
|
439
|
+
publishedAt: c.publishedAt,
|
|
440
|
+
}));
|
|
441
|
+
}
|
|
442
|
+
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* USPS EDDM flat minimum: 6.125" × 11" (one dimension must be at least 11").
|
|
3
|
+
* Product standard used by Flyer Blast: 6.25" × 11" portrait.
|
|
4
|
+
*/
|
|
5
|
+
export const FLYER_EDDM_WIDTH_IN = 6.25;
|
|
6
|
+
export const FLYER_EDDM_HEIGHT_IN = 11;
|
|
7
|
+
export const FLYER_EDDM_MIN_WIDTH_IN = 6.125;
|
|
8
|
+
export const FLYER_EDDM_MIN_HEIGHT_IN = 11;
|
|
9
|
+
|
|
10
|
+
/** USPS EDDM Retail per-piece postage (2026 — verify periodically). */
|
|
11
|
+
export const USPS_EDDM_RETAIL_PER_PIECE_CENTS = 24.7;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* USPS EDDM Retail minimum mailpieces per mailing (saturation).
|
|
15
|
+
* Aligns with starter tier lower bound in `FLYER_PRICING_TIERS`.
|
|
16
|
+
*/
|
|
17
|
+
export const USPS_EDDM_RETAIL_MIN_MAILPIECES = 200;
|
|
18
|
+
|
|
19
|
+
export type FlyerPricingTierId = "starter" | "neighborhood" | "district" | "metro";
|
|
20
|
+
|
|
21
|
+
export interface FlyerTierQuote {
|
|
22
|
+
tierId: FlyerPricingTierId;
|
|
23
|
+
label: string;
|
|
24
|
+
minHouseholds: number;
|
|
25
|
+
maxHouseholds: number;
|
|
26
|
+
hostPriceCents: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const FLYER_PRICING_TIERS: FlyerTierQuote[] = [
|
|
30
|
+
{
|
|
31
|
+
tierId: "starter",
|
|
32
|
+
label: "Starter",
|
|
33
|
+
minHouseholds: 200,
|
|
34
|
+
maxHouseholds: 499,
|
|
35
|
+
hostPriceCents: 14900,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
tierId: "neighborhood",
|
|
39
|
+
label: "Neighborhood",
|
|
40
|
+
minHouseholds: 500,
|
|
41
|
+
maxHouseholds: 1499,
|
|
42
|
+
hostPriceCents: 32900,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
tierId: "district",
|
|
46
|
+
label: "District",
|
|
47
|
+
minHouseholds: 1500,
|
|
48
|
+
maxHouseholds: 3999,
|
|
49
|
+
hostPriceCents: 69900,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
tierId: "metro",
|
|
53
|
+
label: "Metro",
|
|
54
|
+
minHouseholds: 4000,
|
|
55
|
+
maxHouseholds: 9999,
|
|
56
|
+
hostPriceCents: 139900,
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
/** Default sponsor slot list prices (Utah / intermountain — cents). */
|
|
61
|
+
export const FLYER_SPONSOR_DEFAULT_FEATURED_CENTS = 10000;
|
|
62
|
+
export const FLYER_SPONSOR_DEFAULT_SUPPORTING_CENTS = 5000;
|
|
63
|
+
|
|
64
|
+
export function isEddmFlatSizeCompliant(widthIn: number, heightIn: number): boolean {
|
|
65
|
+
const long = Math.max(widthIn, heightIn);
|
|
66
|
+
const short = Math.min(widthIn, heightIn);
|
|
67
|
+
return long >= FLYER_EDDM_MIN_HEIGHT_IN && short >= FLYER_EDDM_MIN_WIDTH_IN;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Map total selected households to a pricing tier. Returns null if below minimum (200 for EDDM Retail).
|
|
72
|
+
*/
|
|
73
|
+
export function quoteFlyerTierForHouseholds(
|
|
74
|
+
totalHouseholds: number
|
|
75
|
+
): FlyerTierQuote | null {
|
|
76
|
+
if (totalHouseholds < FLYER_PRICING_TIERS[0].minHouseholds) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
for (const tier of FLYER_PRICING_TIERS) {
|
|
80
|
+
if (
|
|
81
|
+
totalHouseholds >= tier.minHouseholds &&
|
|
82
|
+
totalHouseholds <= tier.maxHouseholds
|
|
83
|
+
) {
|
|
84
|
+
return tier;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Above metro max — use metro as ceiling for MVP
|
|
88
|
+
const last = FLYER_PRICING_TIERS[FLYER_PRICING_TIERS.length - 1];
|
|
89
|
+
if (totalHouseholds > last.maxHouseholds) {
|
|
90
|
+
return last;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function computeHostTotalAfterSponsors(
|
|
96
|
+
printPostageCostCents: number,
|
|
97
|
+
sponsorRevenueCents: number
|
|
98
|
+
): number {
|
|
99
|
+
return Math.max(0, printPostageCostCents - sponsorRevenueCents);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const EARTH_RADIUS_MI = 3958.8;
|
|
103
|
+
|
|
104
|
+
/** Haversine distance in miles between two WGS84 points. */
|
|
105
|
+
export function distanceMiles(
|
|
106
|
+
lat1: number,
|
|
107
|
+
lng1: number,
|
|
108
|
+
lat2: number,
|
|
109
|
+
lng2: number
|
|
110
|
+
): number {
|
|
111
|
+
const rlat1 = (lat1 * Math.PI) / 180;
|
|
112
|
+
const rlat2 = (lat2 * Math.PI) / 180;
|
|
113
|
+
const dLat = rlat2 - rlat1;
|
|
114
|
+
const dLng = ((lng2 - lng1) * Math.PI) / 180;
|
|
115
|
+
const a =
|
|
116
|
+
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
117
|
+
Math.cos(rlat1) * Math.cos(rlat2) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
|
118
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
119
|
+
return EARTH_RADIUS_MI * c;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** URL path for the public QR scan endpoint (prepend API origin in clients). */
|
|
123
|
+
export function flyerQrScanPath(campaignQrSlug: string): string {
|
|
124
|
+
return `/api/flyer/qr-scan?c=${encodeURIComponent(campaignQrSlug)}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Short slug for campaign QR (avoid ambiguous chars); unique enforced in DB.
|
|
129
|
+
*/
|
|
130
|
+
export function generateFlyerCampaignSlug(): string {
|
|
131
|
+
const alphabet = "23456789abcdefghjkmnpqrstuvwxyz";
|
|
132
|
+
let out = "fb";
|
|
133
|
+
for (let i = 0; i < 12; i++) {
|
|
134
|
+
out += alphabet[Math.floor(Math.random() * alphabet.length)];
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
package/src/utils/urlUtils.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isProduction } from "./apiUtils.js";
|
|
2
1
|
import { BASH_DETAIL_URL, STRIPE_CHECKOUT_TIMEOUT_MS, STRIPE_REDIRECT_TIMEOUT_MS } from "../definitions.js";
|
|
3
2
|
import { generateBashDetailUrl } from "./slugUtils.js";
|
|
4
3
|
|
|
@@ -17,11 +16,34 @@ function isCapacitorNativePlatform(): boolean {
|
|
|
17
16
|
return cap?.isNativePlatform?.() === true;
|
|
18
17
|
}
|
|
19
18
|
|
|
19
|
+
/** True when the current page is being served from a developer's local machine (not ngrok, not deployed). */
|
|
20
|
+
function isLocalDevHostname(hostname: string): boolean {
|
|
21
|
+
return (
|
|
22
|
+
hostname === "localhost" ||
|
|
23
|
+
hostname === "127.0.0.1" ||
|
|
24
|
+
hostname.startsWith("192.168.")
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Canonical frontend origin for share links, embeds, QR codes, etc.
|
|
30
|
+
*
|
|
31
|
+
* Browser-hostname-driven (NOT env-var driven) so the answer is correct in any
|
|
32
|
+
* deployed environment — including ones where `process.env.ENVIRONMENT` /
|
|
33
|
+
* `BASH_ENV` aren't inlined into the bundle (Vite only inlines REACT_APP_* /
|
|
34
|
+
* VITE_* / NODE_ENV / PUBLIC_URL). Previously this depended on `isProduction()`
|
|
35
|
+
* and produced URLs like `https://bash.community:3000/bash/...` in production,
|
|
36
|
+
* which broke Facebook/iMessage previews because `:3000` isn't publicly reachable.
|
|
37
|
+
*/
|
|
20
38
|
export function getFrontendHost(): string {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
39
|
+
if (typeof window === "undefined") {
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
const { protocol, hostname, host, port } = window.location;
|
|
43
|
+
if (isLocalDevHostname(hostname)) {
|
|
44
|
+
return `${protocol}//${hostname}:${port || "3000"}`;
|
|
45
|
+
}
|
|
46
|
+
return `${protocol}//${host}`;
|
|
25
47
|
}
|
|
26
48
|
|
|
27
49
|
export function getBackendHost(): string {
|
package/src/utils/userUtils.ts
CHANGED
|
@@ -1 +1,41 @@
|
|
|
1
|
+
import { Gender } from "@prisma/client";
|
|
2
|
+
|
|
1
3
|
export const MAX_USER_NAME_CHANGES = 3 as const;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Minimum shape needed by `getDisplayGender` — accepts any object that has the
|
|
7
|
+
* two relevant columns (e.g. `User`, `UserExt`, `PublicUser`, or a partial
|
|
8
|
+
* select). The free-text `genderIdentity` is the source of truth for *display*
|
|
9
|
+
* because it is user-self-described and may not map to a `Gender` enum value
|
|
10
|
+
* (e.g. "Non-binary", "Genderfluid").
|
|
11
|
+
*/
|
|
12
|
+
export interface GenderDisplayUser {
|
|
13
|
+
gender?: Gender | null;
|
|
14
|
+
genderIdentity?: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the gender label that should be **displayed in the UI** (profile
|
|
19
|
+
* header, badges, organizer cards, etc.) for a given user.
|
|
20
|
+
*
|
|
21
|
+
* Precedence:
|
|
22
|
+
* 1. `genderIdentity` (free-text self-description) when present and
|
|
23
|
+
* non-blank — wins over the enum so the user sees their own words.
|
|
24
|
+
* 2. `gender` (enum) when present.
|
|
25
|
+
* 3. `null` when neither is set.
|
|
26
|
+
*
|
|
27
|
+
* IMPORTANT: This helper is for **display only**. Analytics, demographics
|
|
28
|
+
* cohorts, target-audience matching, and anything that needs a stable
|
|
29
|
+
* categorical value should keep reading `user.gender` directly. Adding a
|
|
30
|
+
* comment at those sites (e.g. "intentional: analytics uses raw enum") makes
|
|
31
|
+
* the divergence explicit.
|
|
32
|
+
*
|
|
33
|
+
* @returns the user-facing string (free-text or enum value), or `null` if no
|
|
34
|
+
* gender info has been provided.
|
|
35
|
+
*/
|
|
36
|
+
export function getDisplayGender(user: GenderDisplayUser): string | null {
|
|
37
|
+
const trimmedIdentity = user.genderIdentity?.trim();
|
|
38
|
+
if (trimmedIdentity) return trimmedIdentity;
|
|
39
|
+
if (user.gender) return user.gender;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cancellationPolicyRefundResolver.test.d.ts","sourceRoot":"","sources":["../../../src/utils/__tests__/cancellationPolicyRefundResolver.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for cancellation refund resolution (single source of truth:
|
|
3
|
-
* SERVICE_CANCELLATION_POLICY_DATA).
|
|
4
|
-
*/
|
|
5
|
-
import { ServiceCancellationPolicy } from "@prisma/client";
|
|
6
|
-
import { cancellationRuleThresholdHours, getMatchingCancellationRefundRule, hoursUntilEventStart, resolveCancellationRefundFraction, } from "../service/cancellationPolicyRefundResolver.js";
|
|
7
|
-
describe("cancellationPolicyRefundResolver", () => {
|
|
8
|
-
const fixedNow = new Date("2026-01-01T12:00:00.000Z");
|
|
9
|
-
describe("hoursUntilEventStart", () => {
|
|
10
|
-
test("returns 0 when event is in the past", () => {
|
|
11
|
-
expect(hoursUntilEventStart(new Date("2025-12-01T12:00:00.000Z"), fixedNow)).toBe(0);
|
|
12
|
-
});
|
|
13
|
-
test("returns positive hours until start", () => {
|
|
14
|
-
const start = new Date("2026-01-03T12:00:00.000Z");
|
|
15
|
-
expect(hoursUntilEventStart(start, fixedNow)).toBe(48);
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
describe("cancellationRuleThresholdHours", () => {
|
|
19
|
-
test("converts days to hours", () => {
|
|
20
|
-
expect(cancellationRuleThresholdHours({ days: 7, refundPercentage: 100 })).toBe(168);
|
|
21
|
-
});
|
|
22
|
-
test("uses hours when set", () => {
|
|
23
|
-
expect(cancellationRuleThresholdHours({ hours: 24, refundPercentage: 50 })).toBe(24);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
describe("VendorStandard (14d full, 3d partial)", () => {
|
|
27
|
-
test("20 days before: 100%", () => {
|
|
28
|
-
const eventStart = new Date("2026-01-21T12:00:00.000Z");
|
|
29
|
-
expect(resolveCancellationRefundFraction(ServiceCancellationPolicy.VendorStandard, {
|
|
30
|
-
eventStart,
|
|
31
|
-
now: fixedNow,
|
|
32
|
-
})).toBe(1);
|
|
33
|
-
});
|
|
34
|
-
test("10 days before: 50%", () => {
|
|
35
|
-
const eventStart = new Date("2026-01-11T12:00:00.000Z");
|
|
36
|
-
expect(resolveCancellationRefundFraction(ServiceCancellationPolicy.VendorStandard, {
|
|
37
|
-
eventStart,
|
|
38
|
-
now: fixedNow,
|
|
39
|
-
})).toBe(0.5);
|
|
40
|
-
});
|
|
41
|
-
test("2 days before: 0%", () => {
|
|
42
|
-
const eventStart = new Date("2026-01-03T12:00:00.000Z");
|
|
43
|
-
expect(resolveCancellationRefundFraction(ServiceCancellationPolicy.VendorStandard, {
|
|
44
|
-
eventStart,
|
|
45
|
-
now: fixedNow,
|
|
46
|
-
})).toBe(0);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe("VendorFlexible (7d full, 24h partial)", () => {
|
|
50
|
-
test("8 days before: 100%", () => {
|
|
51
|
-
const eventStart = new Date("2026-01-09T12:00:00.000Z");
|
|
52
|
-
expect(resolveCancellationRefundFraction(ServiceCancellationPolicy.VendorFlexible, {
|
|
53
|
-
eventStart,
|
|
54
|
-
now: fixedNow,
|
|
55
|
-
})).toBe(1);
|
|
56
|
-
});
|
|
57
|
-
test("5 days before: 50%", () => {
|
|
58
|
-
const eventStart = new Date("2026-01-06T12:00:00.000Z");
|
|
59
|
-
expect(resolveCancellationRefundFraction(ServiceCancellationPolicy.VendorFlexible, {
|
|
60
|
-
eventStart,
|
|
61
|
-
now: fixedNow,
|
|
62
|
-
})).toBe(0.5);
|
|
63
|
-
});
|
|
64
|
-
test("12 hours before: 0%", () => {
|
|
65
|
-
const eventStart = new Date("2026-01-02T00:00:00.000Z");
|
|
66
|
-
expect(resolveCancellationRefundFraction(ServiceCancellationPolicy.VendorFlexible, {
|
|
67
|
-
eventStart,
|
|
68
|
-
now: fixedNow,
|
|
69
|
-
})).toBe(0);
|
|
70
|
-
});
|
|
71
|
-
test("6d 23h before (~167h): still 50% tier, not full 7d", () => {
|
|
72
|
-
const eventStart = new Date("2026-01-08T11:00:00.000Z");
|
|
73
|
-
expect(hoursUntilEventStart(eventStart, fixedNow)).toBeCloseTo(167, 0);
|
|
74
|
-
expect(resolveCancellationRefundFraction(ServiceCancellationPolicy.VendorFlexible, {
|
|
75
|
-
eventStart,
|
|
76
|
-
now: fixedNow,
|
|
77
|
-
})).toBe(0.5);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
describe("VeryFlexible (24h full only)", () => {
|
|
81
|
-
test("2 days before: 100%", () => {
|
|
82
|
-
const eventStart = new Date("2026-01-03T12:00:00.000Z");
|
|
83
|
-
expect(resolveCancellationRefundFraction(ServiceCancellationPolicy.VeryFlexible, {
|
|
84
|
-
eventStart,
|
|
85
|
-
now: fixedNow,
|
|
86
|
-
})).toBe(1);
|
|
87
|
-
});
|
|
88
|
-
test("12 hours before: 0%", () => {
|
|
89
|
-
const eventStart = new Date("2026-01-02T00:00:00.000Z");
|
|
90
|
-
expect(resolveCancellationRefundFraction(ServiceCancellationPolicy.VeryFlexible, {
|
|
91
|
-
eventStart,
|
|
92
|
-
now: fixedNow,
|
|
93
|
-
})).toBe(0);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
describe("getMatchingCancellationRefundRule", () => {
|
|
97
|
-
test("returns the winning tier rule object", () => {
|
|
98
|
-
const eventStart = new Date("2026-01-11T12:00:00.000Z");
|
|
99
|
-
const rule = getMatchingCancellationRefundRule(ServiceCancellationPolicy.VendorStandard, { eventStart, now: fixedNow });
|
|
100
|
-
expect(rule).toEqual({ days: 3, refundPercentage: 50 });
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
//# sourceMappingURL=cancellationPolicyRefundResolver.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cancellationPolicyRefundResolver.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/cancellationPolicyRefundResolver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=serviceRateDBUtils.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serviceRateDBUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/service/serviceRateDBUtils.ts"],"names":[],"mappings":""}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// import { ServiceRate } from "@prisma/client";
|
|
3
|
-
// import { ApiResult } from "../../definitions";
|
|
4
|
-
// import { ServiceSpecialRatesExt } from "../../extendedSchemas";
|
|
5
|
-
// import { convertDollarsToCents, convertCentsToDollars } from "../paymentUtils";
|
|
6
|
-
// export function specialRateConvertToDb(
|
|
7
|
-
// specialRate: Partial<ServiceSpecialRatesExt>
|
|
8
|
-
// ): Partial<ServiceSpecialRatesExt> {
|
|
9
|
-
// return specialRateConvertRatesToDB(
|
|
10
|
-
// specialRateConvertDateStringsToDb(structuredClone(specialRate))
|
|
11
|
-
// );
|
|
12
|
-
// }
|
|
13
|
-
// export function specialRateConvertFromDb(
|
|
14
|
-
// specialRate: ServiceSpecialRatesExt
|
|
15
|
-
// ): ServiceSpecialRatesExt {
|
|
16
|
-
// // console.log(`specialRateConvertFromDb`);
|
|
17
|
-
// return specialRateConvertRatesFromDB(
|
|
18
|
-
// specialRateConvertDateStringsFromDb(specialRate)
|
|
19
|
-
// );
|
|
20
|
-
// }
|
|
21
|
-
// function specialRateConvertDateStringsToDb(
|
|
22
|
-
// specialRate: Partial<ServiceSpecialRatesExt>
|
|
23
|
-
// ): Partial<ServiceSpecialRatesExt> {
|
|
24
|
-
// // const oldStart = new Date(specialRate.startDate!);
|
|
25
|
-
// // const oldEnd = new Date(specialRate.endDate!);
|
|
26
|
-
// // specialRate.startDate = dateTimeLocalToUTCDate(specialRate.startDate!);
|
|
27
|
-
// // specialRate.endDate = dateTimeLocalToUTCDate(specialRate.endDate!);
|
|
28
|
-
// // specialRate.startDate = normalizeDate(specialRate.startDate!);
|
|
29
|
-
// // specialRate.endDate = normalizeDate(specialRate.endDate!);
|
|
30
|
-
// // const localStart = dateTimeLocalFromUTCDate(new Date(specialRate.startDate!));
|
|
31
|
-
// // const localEnd = dateTimeLocalFromUTCDate(new Date(specialRate.endDate!));
|
|
32
|
-
// // console.log(
|
|
33
|
-
// // `specialRateConvertDateStringsToDb oldStart: ${oldStart?.toString()}`
|
|
34
|
-
// // );
|
|
35
|
-
// // console.log(
|
|
36
|
-
// // `specialRateConvertDateStringsToDb oldEnd: ${oldEnd?.toString()}`
|
|
37
|
-
// // );
|
|
38
|
-
// // console.log(
|
|
39
|
-
// // `specialRateConvertDateStringsToDb specialRate.startDate: ${specialRate.startDate?.toString()}`
|
|
40
|
-
// // );
|
|
41
|
-
// // console.log(
|
|
42
|
-
// // `specialRateConvertDateStringsToDb specialRate.endDate: ${specialRate.endDate?.toString()}`
|
|
43
|
-
// // );
|
|
44
|
-
// // console.log(
|
|
45
|
-
// // `specialRateConvertDateStringsToDb localStart: ${localStart?.toString()}`
|
|
46
|
-
// // );
|
|
47
|
-
// // console.log(
|
|
48
|
-
// // `specialRateConvertDateStringsToDb localEnd: ${localEnd?.toString()}`
|
|
49
|
-
// // );
|
|
50
|
-
// // const a = 3 + 4;
|
|
51
|
-
// // specialRate.startDate = specialRate.startDate.toISOString();
|
|
52
|
-
// // specialRate.endDate = specialRate.endDate.toISOString();
|
|
53
|
-
// // return specialRate;
|
|
54
|
-
// return specialRate;
|
|
55
|
-
// }
|
|
56
|
-
// function specialRateConvertDateStringsFromDb(
|
|
57
|
-
// specialRate: ServiceSpecialRatesExt
|
|
58
|
-
// ): ServiceSpecialRatesExt {
|
|
59
|
-
// // specialRate.startDate = localDateToUTC(new Date(specialRate.startDate));
|
|
60
|
-
// // specialRate.endDate = localDateToUTC(new Date(specialRate.endDate));
|
|
61
|
-
// //this shouldnt even be necessary as javascript dates are always stored in UTC timezone (they lack timezone information)
|
|
62
|
-
// // specialRate.startDate = ensureDateTimeIsLocalDateTime(
|
|
63
|
-
// // new Date(specialRate.startDate)
|
|
64
|
-
// // ).toDate();
|
|
65
|
-
// // specialRate.endDate = ensureDateTimeIsLocalDateTime(
|
|
66
|
-
// // new Date(specialRate.endDate)
|
|
67
|
-
// // ).toDate();
|
|
68
|
-
// specialRate.startDate = new Date(specialRate.startDate);
|
|
69
|
-
// specialRate.endDate = new Date(specialRate.endDate);
|
|
70
|
-
// // const oldStart = new Date(specialRate.startDate!);
|
|
71
|
-
// // const oldEnd = new Date(specialRate.endDate!);
|
|
72
|
-
// // specialRate.startDate = dateTimeLocalFromUTCDate(
|
|
73
|
-
// // new Date(specialRate.startDate)
|
|
74
|
-
// // );
|
|
75
|
-
// // specialRate.endDate = dateTimeLocalFromUTCDate(new Date(specialRate.endDate));
|
|
76
|
-
// // // specialRate.startDate = ensureDateTimeIsLocalDateTime(
|
|
77
|
-
// // // new Date(specialRate.startDate)
|
|
78
|
-
// // // ).toDate();
|
|
79
|
-
// // // specialRate.endDate = ensureDateTimeIsLocalDateTime(
|
|
80
|
-
// // // new Date(specialRate.endDate)
|
|
81
|
-
// // // ).toDate();
|
|
82
|
-
// // // specialRate.startDate = localDateToUTC(new Date(specialRate.startDate));
|
|
83
|
-
// // // specialRate.endDate = localDateToUTC(new Date(specialRate.endDate));
|
|
84
|
-
// // const date2 = normalizeDate(specialRate.startDate)!;
|
|
85
|
-
// // const date3 = normalizeDate(specialRate.endDate)!;
|
|
86
|
-
// // const a = 3 + 4;
|
|
87
|
-
// return specialRate;
|
|
88
|
-
// }
|
|
89
|
-
// function specialRateConvertRatesToDB(
|
|
90
|
-
// specialRate: Partial<ServiceSpecialRatesExt>
|
|
91
|
-
// ): Partial<ServiceSpecialRatesExt> {
|
|
92
|
-
// if (specialRate.serviceRate) {
|
|
93
|
-
// specialRate.serviceRate = generalRatesConvertRatesToDB(
|
|
94
|
-
// specialRate.serviceRate
|
|
95
|
-
// ) as ServiceRate;
|
|
96
|
-
// }
|
|
97
|
-
// return specialRate;
|
|
98
|
-
// }
|
|
99
|
-
// function specialRateConvertRatesFromDB(
|
|
100
|
-
// specialRate: ServiceSpecialRatesExt
|
|
101
|
-
// ): ServiceSpecialRatesExt {
|
|
102
|
-
// if (specialRate.serviceRate) {
|
|
103
|
-
// specialRate.serviceRate = generalRatesConvertRatesFromDB(
|
|
104
|
-
// specialRate.serviceRate
|
|
105
|
-
// );
|
|
106
|
-
// }
|
|
107
|
-
// return specialRate;
|
|
108
|
-
// }
|
|
109
|
-
// export function generalRatesConvertRatesToDB(
|
|
110
|
-
// generalRate: ServiceRate
|
|
111
|
-
// ): ServiceRate {
|
|
112
|
-
// return generalRatesConvertRatesToDBHelper(structuredClone(generalRate));
|
|
113
|
-
// }
|
|
114
|
-
// export function generalRatesConvertRatesToDBHelper(
|
|
115
|
-
// generalRate: ServiceRate
|
|
116
|
-
// ): ServiceRate {
|
|
117
|
-
// // console.log(
|
|
118
|
-
// // `generalRatesConvertRatesToDBHelper A: ${JSON.stringify(generalRate)}`
|
|
119
|
-
// // );
|
|
120
|
-
// if (generalRate?.dailyRate) {
|
|
121
|
-
// generalRate.dailyRate = convertDollarsToCents(generalRate.dailyRate);
|
|
122
|
-
// }
|
|
123
|
-
// if (generalRate?.hourlyRate) {
|
|
124
|
-
// generalRate.hourlyRate = convertDollarsToCents(generalRate.hourlyRate);
|
|
125
|
-
// }
|
|
126
|
-
// if (generalRate?.cleaningFeePerBooking) {
|
|
127
|
-
// generalRate.cleaningFeePerBooking = convertDollarsToCents(
|
|
128
|
-
// generalRate.cleaningFeePerBooking
|
|
129
|
-
// );
|
|
130
|
-
// }
|
|
131
|
-
// // console.log(
|
|
132
|
-
// // `generalRatesConvertRatesToDBHelper B: ${JSON.stringify(generalRate)}`
|
|
133
|
-
// // );
|
|
134
|
-
// return generalRate;
|
|
135
|
-
// }
|
|
136
|
-
// export function generalRatesConvertRatesFromDB(
|
|
137
|
-
// generalRate: ServiceRate
|
|
138
|
-
// ): ServiceRate {
|
|
139
|
-
// // console.log(
|
|
140
|
-
// // `generalRatesConvertRatesFromDB A: ${JSON.stringify(generalRate)}`
|
|
141
|
-
// // );
|
|
142
|
-
// // console.log(`specialRateConvertRatesFromDB`);
|
|
143
|
-
// if (generalRate?.dailyRate) {
|
|
144
|
-
// generalRate.dailyRate = convertCentsToDollars(generalRate.dailyRate);
|
|
145
|
-
// }
|
|
146
|
-
// if (generalRate?.hourlyRate) {
|
|
147
|
-
// generalRate.hourlyRate = convertCentsToDollars(generalRate.hourlyRate);
|
|
148
|
-
// }
|
|
149
|
-
// if (generalRate?.cleaningFeePerBooking) {
|
|
150
|
-
// generalRate.cleaningFeePerBooking = convertCentsToDollars(
|
|
151
|
-
// generalRate.cleaningFeePerBooking
|
|
152
|
-
// );
|
|
153
|
-
// }
|
|
154
|
-
// // console.log(
|
|
155
|
-
// // `generalRatesConvertRatesFromDB B: ${JSON.stringify(generalRate)}`
|
|
156
|
-
// // );
|
|
157
|
-
// return generalRate;
|
|
158
|
-
// }
|
|
159
|
-
//# sourceMappingURL=serviceRateDBUtils.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serviceRateDBUtils.js","sourceRoot":"","sources":["../../../src/utils/service/serviceRateDBUtils.ts"],"names":[],"mappings":";AAAA,gDAAgD;AAChD,iDAAiD;AACjD,kEAAkE;AAClE,kFAAkF;AAElF,0CAA0C;AAC1C,iDAAiD;AACjD,uCAAuC;AACvC,wCAAwC;AACxC,sEAAsE;AACtE,OAAO;AACP,IAAI;AAEJ,4CAA4C;AAC5C,wCAAwC;AACxC,8BAA8B;AAC9B,gDAAgD;AAChD,0CAA0C;AAC1C,uDAAuD;AACvD,OAAO;AACP,IAAI;AAEJ,8CAA8C;AAC9C,iDAAiD;AACjD,uCAAuC;AACvC,0DAA0D;AAC1D,sDAAsD;AAEtD,+EAA+E;AAC/E,2EAA2E;AAC3E,sEAAsE;AACtE,kEAAkE;AAElE,sFAAsF;AACtF,kFAAkF;AAElF,oBAAoB;AACpB,+EAA+E;AAC/E,UAAU;AACV,oBAAoB;AACpB,2EAA2E;AAC3E,UAAU;AACV,oBAAoB;AACpB,yGAAyG;AACzG,UAAU;AACV,oBAAoB;AACpB,qGAAqG;AACrG,UAAU;AACV,oBAAoB;AACpB,mFAAmF;AACnF,UAAU;AACV,oBAAoB;AACpB,+EAA+E;AAC/E,UAAU;AAEV,wBAAwB;AAExB,oEAAoE;AACpE,gEAAgE;AAChE,2BAA2B;AAC3B,wBAAwB;AACxB,IAAI;AAEJ,gDAAgD;AAChD,wCAAwC;AACxC,8BAA8B;AAC9B,gFAAgF;AAChF,4EAA4E;AAE5E,6HAA6H;AAC7H,8DAA8D;AAC9D,yCAAyC;AACzC,mBAAmB;AACnB,4DAA4D;AAC5D,uCAAuC;AACvC,mBAAmB;AAEnB,6DAA6D;AAC7D,yDAAyD;AAEzD,0DAA0D;AAC1D,sDAAsD;AAEtD,yDAAyD;AACzD,yCAAyC;AACzC,UAAU;AACV,sFAAsF;AAEtF,iEAAiE;AACjE,4CAA4C;AAC5C,sBAAsB;AACtB,+DAA+D;AAC/D,0CAA0C;AAC1C,sBAAsB;AAEtB,mFAAmF;AACnF,+EAA+E;AAE/E,4DAA4D;AAC5D,0DAA0D;AAE1D,wBAAwB;AACxB,wBAAwB;AACxB,IAAI;AAEJ,wCAAwC;AACxC,iDAAiD;AACjD,uCAAuC;AACvC,mCAAmC;AACnC,8DAA8D;AAC9D,gCAAgC;AAChC,wBAAwB;AACxB,MAAM;AACN,wBAAwB;AACxB,IAAI;AAEJ,0CAA0C;AAC1C,wCAAwC;AACxC,8BAA8B;AAC9B,mCAAmC;AACnC,gEAAgE;AAChE,gCAAgC;AAChC,SAAS;AACT,MAAM;AACN,wBAAwB;AACxB,IAAI;AAEJ,gDAAgD;AAChD,6BAA6B;AAC7B,mBAAmB;AACnB,6EAA6E;AAC7E,IAAI;AAEJ,sDAAsD;AACtD,6BAA6B;AAC7B,mBAAmB;AACnB,sBAAsB;AACtB,kFAAkF;AAClF,YAAY;AACZ,kCAAkC;AAClC,4EAA4E;AAC5E,MAAM;AACN,mCAAmC;AACnC,8EAA8E;AAC9E,MAAM;AACN,8CAA8C;AAC9C,iEAAiE;AACjE,0CAA0C;AAC1C,SAAS;AACT,MAAM;AACN,sBAAsB;AACtB,kFAAkF;AAClF,YAAY;AACZ,wBAAwB;AACxB,IAAI;AAEJ,kDAAkD;AAClD,6BAA6B;AAC7B,mBAAmB;AACnB,sBAAsB;AACtB,8EAA8E;AAC9E,YAAY;AACZ,qDAAqD;AACrD,kCAAkC;AAClC,4EAA4E;AAC5E,MAAM;AACN,mCAAmC;AACnC,8EAA8E;AAC9E,MAAM;AACN,8CAA8C;AAC9C,iEAAiE;AACjE,0CAA0C;AAC1C,SAAS;AACT,MAAM;AACN,sBAAsB;AACtB,8EAA8E;AAC9E,YAAY;AACZ,wBAAwB;AACxB,IAAI"}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
-- Migration: Create BashAvailability table and BashAvailabilityWindow enum
|
|
2
|
-
-- Required for the "Who's going out?" / LookingForSomethingSection feature.
|
|
3
|
-
-- Run this against QA and prod before (or alongside) deploying the API code.
|
|
4
|
-
|
|
5
|
-
DO $$ BEGIN
|
|
6
|
-
|
|
7
|
-
-- 1. Create the enum type if it doesn't exist
|
|
8
|
-
IF NOT EXISTS (
|
|
9
|
-
SELECT 1 FROM pg_type WHERE typname = 'BashAvailabilityWindow'
|
|
10
|
-
) THEN
|
|
11
|
-
CREATE TYPE "BashAvailabilityWindow" AS ENUM (
|
|
12
|
-
'Now',
|
|
13
|
-
'Tonight',
|
|
14
|
-
'ThisWeekend',
|
|
15
|
-
'Custom'
|
|
16
|
-
);
|
|
17
|
-
RAISE NOTICE 'Created enum BashAvailabilityWindow';
|
|
18
|
-
ELSE
|
|
19
|
-
RAISE NOTICE 'Enum BashAvailabilityWindow already exists — skipping';
|
|
20
|
-
END IF;
|
|
21
|
-
|
|
22
|
-
-- 2. Create the BashAvailability table if it doesn't exist
|
|
23
|
-
IF NOT EXISTS (
|
|
24
|
-
SELECT 1 FROM information_schema.tables
|
|
25
|
-
WHERE table_name = 'BashAvailability'
|
|
26
|
-
) THEN
|
|
27
|
-
CREATE TABLE "BashAvailability" (
|
|
28
|
-
"id" TEXT NOT NULL,
|
|
29
|
-
"userId" TEXT NOT NULL,
|
|
30
|
-
"window" "BashAvailabilityWindow" NOT NULL,
|
|
31
|
-
"city" TEXT NOT NULL,
|
|
32
|
-
"state" TEXT,
|
|
33
|
-
"customAt" TIMESTAMP(3),
|
|
34
|
-
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
35
|
-
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT NOW(),
|
|
36
|
-
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT NOW(),
|
|
37
|
-
|
|
38
|
-
CONSTRAINT "BashAvailability_pkey" PRIMARY KEY ("id"),
|
|
39
|
-
CONSTRAINT "BashAvailability_userId_key" UNIQUE ("userId"),
|
|
40
|
-
CONSTRAINT "BashAvailability_userId_fkey"
|
|
41
|
-
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
-- Composite index used by GET /availability?city=...&window=...
|
|
45
|
-
CREATE INDEX "BashAvailability_city_window_expiresAt_idx"
|
|
46
|
-
ON "BashAvailability" ("city", "window", "expiresAt");
|
|
47
|
-
|
|
48
|
-
RAISE NOTICE 'Created table BashAvailability';
|
|
49
|
-
ELSE
|
|
50
|
-
RAISE NOTICE 'Table BashAvailability already exists — skipping';
|
|
51
|
-
END IF;
|
|
52
|
-
|
|
53
|
-
END $$;
|