Docker-ready Next.js + Prisma/Postgres e-invitation app: themed invite pages, RSVP, comments, share links, email + SMS invites with reply-to-RSVP-by-text, co-hosts, photo dropbox, and browser push notifications.
78 lines
2.0 KiB
TypeScript
78 lines
2.0 KiB
TypeScript
import { z } from 'zod';
|
|
import { looksLikeEmail, normalizePhoneNumber } from '@/lib/phone';
|
|
|
|
const emailSchema = z.string().trim().email();
|
|
|
|
export type ParsedEmailList = {
|
|
validEmails: string[];
|
|
skipped: number;
|
|
};
|
|
|
|
/**
|
|
* Parses a free-form textarea of guest emails (comma and/or newline
|
|
* separated) into a deduplicated, lowercased list of valid addresses, plus
|
|
* a count of entries that didn't parse as a valid email.
|
|
*/
|
|
export function parseEmailList(raw: string): ParsedEmailList {
|
|
const rawEntries = raw
|
|
.split(/[\n,]/)
|
|
.map((entry) => entry.trim())
|
|
.filter(Boolean);
|
|
|
|
const validEmails = new Set<string>();
|
|
let skipped = 0;
|
|
for (const entry of rawEntries) {
|
|
const result = emailSchema.safeParse(entry);
|
|
if (result.success) {
|
|
validEmails.add(result.data.toLowerCase());
|
|
} else {
|
|
skipped++;
|
|
}
|
|
}
|
|
|
|
return { validEmails: [...validEmails], skipped };
|
|
}
|
|
|
|
export type ParsedGuestContactList = {
|
|
emails: string[];
|
|
phones: string[];
|
|
skipped: number;
|
|
};
|
|
|
|
/**
|
|
* Like parseEmailList, but for a guest list textarea that accepts a mix of
|
|
* emails and phone numbers - each entry is routed by whether it contains an
|
|
* '@', then validated/normalized accordingly. Anything that fails either
|
|
* check is counted as skipped rather than rejecting the whole batch.
|
|
*/
|
|
export function parseGuestContactList(raw: string): ParsedGuestContactList {
|
|
const rawEntries = raw
|
|
.split(/[\n,]/)
|
|
.map((entry) => entry.trim())
|
|
.filter(Boolean);
|
|
|
|
const emails = new Set<string>();
|
|
const phones = new Set<string>();
|
|
let skipped = 0;
|
|
|
|
for (const entry of rawEntries) {
|
|
if (looksLikeEmail(entry)) {
|
|
const result = emailSchema.safeParse(entry);
|
|
if (result.success) {
|
|
emails.add(result.data.toLowerCase());
|
|
} else {
|
|
skipped++;
|
|
}
|
|
} else {
|
|
const normalized = normalizePhoneNumber(entry);
|
|
if (normalized) {
|
|
phones.add(normalized);
|
|
} else {
|
|
skipped++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return { emails: [...emails], phones: [...phones], skipped };
|
|
}
|