forked from Mike/freno-dev
126 lines
4.8 KiB
TypeScript
126 lines
4.8 KiB
TypeScript
/**
|
|
* Per-site contact form configuration — per-subdomain contact pages.
|
|
*
|
|
* Pure module — imports NOTHING from solid-js / @solidjs/router / @solidjs/meta —
|
|
* so it can be unit-tested in `bun:test` without spinning up the router / Meta
|
|
* provider, mirroring the testability pattern established by `page-head-meta.ts`
|
|
* and `nav-config.ts`.
|
|
*
|
|
* The shared `<ContactForm>` (`src/components/ContactForm.tsx`) reads the active
|
|
* `site` via `useSite()` and derives its `subjectPrefix`, recipient label,
|
|
* heading copy, and PageHead description from this map. Call sites may still
|
|
* override these defaults via props (e.g. to inject a site-specific subline or
|
|
* render a `children` block such as the Life-and-Lineage Q&A accordion).
|
|
*
|
|
* Email routing contract:
|
|
* - `subjectPrefix` is the bare prefix token placed in front of `" Contact
|
|
* Request"`. The main site keeps `"freno.me"` (no brackets) so the existing
|
|
* `"freno.me Contact Request"` subject is byte-identical after the refactor
|
|
* (backwards compatibility for any inbox filters / saved searches). Each
|
|
* product subdomain uses a bracketed token (`"[Nessa]"`, `"[Lineage]"`,
|
|
* `"[Gaze]"`, `"[InputHalo]"`) so inbound mail can be
|
|
* routed / triaged by source product.
|
|
* - All mail is delivered to `michael@freno.me` (single owner across every
|
|
* product); `recipientLabel` is a display-only affordance, not an alternate
|
|
* SMTP recipient.
|
|
* - The tRPC `misc.sendContactRequest` mutation and the no-JS server action
|
|
* both receive this prefix and emit the identical subject — a single source
|
|
* of truth lives here.
|
|
*/
|
|
import type { SiteId } from "~/lib/site-context";
|
|
|
|
/** Canonical recipient for every contact submission (single product owner). */
|
|
export const CONTACT_RECIPIENT_EMAIL = "michael@freno.me";
|
|
/** Canonical sender identity shown on outbound contact mail. */
|
|
export const CONTACT_SENDER = {
|
|
name: "freno.me",
|
|
email: CONTACT_RECIPIENT_EMAIL
|
|
};
|
|
|
|
export interface ContactContext {
|
|
siteId: SiteId;
|
|
/**
|
|
* Prefix token prepended to the outbound email subject. Bare `"freno.me"`
|
|
* for the main site (preserves the historical subject verbatim); bracketed
|
|
* `[Nessa]` / `[Lineage]` / `[Gaze]` / `[InputHalo]` for the product
|
|
* subdomains so mail can be triaged by source.
|
|
*/
|
|
subjectPrefix: string;
|
|
/** Display-only label for who receives the message (no SMTP routing effect). */
|
|
recipientLabel: string;
|
|
/** `<h1>` heading rendered at the top of the contact form. */
|
|
heading: string;
|
|
/** `<PageHead description>` for the per-site `/contact` page. */
|
|
description: string;
|
|
/**
|
|
* Page title passed to `<PageHead>`. Composes with the site `titleSuffix`
|
|
* (e.g. `"Contact" | Life and Lineage`). The main site keeps the bare
|
|
* `"Contact"` so its title remains `"Contact | Michael Freno"`.
|
|
*/
|
|
pageTitle: string;
|
|
}
|
|
|
|
export const CONTACT_CONTEXT: Record<SiteId, ContactContext> = {
|
|
main: {
|
|
siteId: "main",
|
|
subjectPrefix: "freno.me",
|
|
recipientLabel: "Michael Freno",
|
|
heading: "Contact",
|
|
description: "Contact Me",
|
|
pageTitle: "Contact"
|
|
},
|
|
nessa: {
|
|
siteId: "nessa",
|
|
subjectPrefix: "[Nessa]",
|
|
recipientLabel: "the Nessa team",
|
|
heading: "Contact",
|
|
description:
|
|
"Get in touch with the Nessa community platform — questions about clubs, challenges, the social feed, or events.",
|
|
pageTitle: "Contact"
|
|
},
|
|
lineage: {
|
|
siteId: "lineage",
|
|
subjectPrefix: "[Lineage]",
|
|
recipientLabel: "the Life and Lineage team",
|
|
heading: "Contact",
|
|
description:
|
|
"Contact the Life and Lineage team — questions about gameplay, remote backups, cross-device play, or account deletion.",
|
|
pageTitle: "Contact"
|
|
},
|
|
gaze: {
|
|
siteId: "gaze",
|
|
subjectPrefix: "[Gaze]",
|
|
recipientLabel: "the Gaze team",
|
|
heading: "Contact",
|
|
description:
|
|
"Get in touch with the Gaze team — questions, feedback, or support for the Gaze macOS app.",
|
|
pageTitle: "Contact"
|
|
},
|
|
inputhalo: {
|
|
siteId: "inputhalo",
|
|
subjectPrefix: "[InputHalo]",
|
|
recipientLabel: "the InputHalo team",
|
|
heading: "Contact",
|
|
description:
|
|
"Get in touch with the InputHalo team — questions, feedback, or support for the InputHalo app.",
|
|
pageTitle: "Contact"
|
|
}
|
|
};
|
|
|
|
/** Resolve the contact context for a given site id. */
|
|
export function getContactContext(siteId: SiteId): ContactContext {
|
|
return CONTACT_CONTEXT[siteId];
|
|
}
|
|
|
|
/**
|
|
* Build the outbound contact email subject for a given prefix token.
|
|
*
|
|
* Kept pure + exported so the tRPC mutation (`misc.sendContactRequest`) and the
|
|
* no-JS server action in `ContactForm.tsx` emit byte-identical subjects — and
|
|
* so the unit tests can assert the per-subdomain subject strings without
|
|
* driving the network.
|
|
*/
|
|
export function buildContactSubject(subjectPrefix: string): string {
|
|
return `${subjectPrefix} Contact Request`;
|
|
}
|