Invite people after review, then finish the Rule, with login before inviting and Invite your members on Profile.
This commit is contained in:
1 parent
88bfccd1e7
commit
70ceb73104
31 files changed
+1388
-401
No files matched your search
@@ -0,0 +1,43 @@
|
||||
import type { CreateFlowState } from "../../app/(app)/create/types";
|
||||
import { RULE_SECTION_CATEGORY } from "./ruleSectionsFromMethodSelections";
|
||||
import {
|
||||
membershipPresetFor,
|
||||
publishedMethodDisplayLabel,
|
||||
} from "./finalReviewChipPresets";
|
||||
|
||||
export type MembershipInvitePreview = {
|
||||
categoryName: string;
|
||||
title: string;
|
||||
eligibility: string;
|
||||
joiningProcess: string;
|
||||
};
|
||||
|
||||
/** First selected membership method, for the Invite your members card. */
|
||||
export function membershipInvitePreview(
|
||||
state: CreateFlowState,
|
||||
): MembershipInvitePreview | null {
|
||||
const ids = state.selectedMembershipMethodIds ?? [];
|
||||
const firstId = ids.find((id) => typeof id === "string" && id.trim() !== "");
|
||||
if (!firstId) return null;
|
||||
|
||||
const saved = state.membershipMethodDetailsById?.[firstId];
|
||||
const preset = membershipPresetFor(firstId);
|
||||
const eligibility = (saved?.eligibility ?? "").trim() || preset.eligibility;
|
||||
const joiningProcess =
|
||||
(saved?.joiningProcess ?? "").trim() || preset.joiningProcess;
|
||||
const title =
|
||||
publishedMethodDisplayLabel(
|
||||
"membership",
|
||||
firstId,
|
||||
state.customMethodCardMetaById,
|
||||
) || firstId;
|
||||
|
||||
if (!title && !eligibility && !joiningProcess) return null;
|
||||
|
||||
return {
|
||||
categoryName: RULE_SECTION_CATEGORY.membership,
|
||||
title,
|
||||
eligibility,
|
||||
joiningProcess,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/** Shared invite-field email check (create-flow Invite your members). */
|
||||
export const INVITE_EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
const COUNT_WORDS = [
|
||||
"zero",
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"four",
|
||||
"five",
|
||||
"six",
|
||||
"seven",
|
||||
"eight",
|
||||
"nine",
|
||||
"ten",
|
||||
] as const;
|
||||
|
||||
/** Lowercase count for paste-result copy (`five`, or digits past ten). */
|
||||
export function formatInviteCountWord(n: number): string {
|
||||
if (Number.isInteger(n) && n >= 0 && n <= 10) {
|
||||
return COUNT_WORDS[n] ?? String(n);
|
||||
}
|
||||
return String(n);
|
||||
}
|
||||
|
||||
export function splitInvitePasteTokens(raw: string): string[] {
|
||||
return raw
|
||||
.split(/[\s,;]+/)
|
||||
.map((token) => token.trim())
|
||||
.filter((token) => token.length > 0);
|
||||
}
|
||||
|
||||
export type ParseInvitePasteResult = {
|
||||
added: string[];
|
||||
invalidRemainder: string;
|
||||
skippedDuplicate: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Split a pasted list into valid new addresses and leftovers that stay in the
|
||||
* field. Tokens that are not emails, duplicates, or that would exceed `max`
|
||||
* remain in {@link ParseInvitePasteResult.invalidRemainder}.
|
||||
*/
|
||||
export function parseInvitePaste(
|
||||
raw: string,
|
||||
existing: readonly string[],
|
||||
max: number,
|
||||
): ParseInvitePasteResult {
|
||||
const tokens = splitInvitePasteTokens(raw);
|
||||
const added: string[] = [];
|
||||
const invalid: string[] = [];
|
||||
const seen = new Set(
|
||||
existing
|
||||
.map((email) => email.trim().toLowerCase())
|
||||
.filter((email) => email.length > 0),
|
||||
);
|
||||
let skippedDuplicate = 0;
|
||||
|
||||
for (const token of tokens) {
|
||||
const lower = token.toLowerCase();
|
||||
if (!INVITE_EMAIL_PATTERN.test(lower)) {
|
||||
invalid.push(token);
|
||||
continue;
|
||||
}
|
||||
if (seen.has(lower)) {
|
||||
skippedDuplicate += 1;
|
||||
continue;
|
||||
}
|
||||
if (existing.length + added.length >= max) {
|
||||
invalid.push(token);
|
||||
continue;
|
||||
}
|
||||
seen.add(lower);
|
||||
added.push(lower);
|
||||
}
|
||||
|
||||
return {
|
||||
added,
|
||||
invalidRemainder: invalid.join(", "),
|
||||
skippedDuplicate,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user