The four account panes now share a fixed frame, chart profiles open as list then detail, and membership lives in the homepage dialog instead of a separate page. Co-authored-by: Cursor <cursoragent@cursor.com>
41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
import type { BillingPaneTab, MembershipPlanAlias } from "@/lib/membership";
|
|
|
|
export const SETTINGS_QUERY_KEY = "settings";
|
|
|
|
export type SettingsQuery = {
|
|
readonly pane: "billing";
|
|
readonly source: string | null;
|
|
readonly plan: MembershipPlanAlias | null;
|
|
readonly tab: BillingPaneTab | null;
|
|
};
|
|
|
|
const PLAN_ALIASES: ReadonlySet<string> = new Set(["trial", "monthly", "yearly"]);
|
|
const BILLING_TABS: ReadonlySet<string> = new Set(["membership", "credits", "redeem", "orders"]);
|
|
|
|
function searchParams(search: string) {
|
|
return new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
|
|
}
|
|
|
|
export function parseSettingsQuery(search: string): SettingsQuery | null {
|
|
const params = searchParams(search);
|
|
if (params.get(SETTINGS_QUERY_KEY) !== "billing") return null;
|
|
const planRaw = params.get("plan");
|
|
const tabRaw = params.get("tab");
|
|
return {
|
|
pane: "billing",
|
|
source: params.get("source"),
|
|
plan: planRaw && PLAN_ALIASES.has(planRaw) ? planRaw as MembershipPlanAlias : null,
|
|
tab: tabRaw && BILLING_TABS.has(tabRaw) ? tabRaw as BillingPaneTab : null,
|
|
};
|
|
}
|
|
|
|
export function stripSettingsQuery(search: string, pathname = "/") {
|
|
const params = searchParams(search);
|
|
params.delete(SETTINGS_QUERY_KEY);
|
|
params.delete("source");
|
|
params.delete("plan");
|
|
params.delete("tab");
|
|
const query = params.toString();
|
|
return query ? `${pathname}?${query}` : pathname || "/";
|
|
}
|