import { computed, reactive, watch } from "vue"; import { auth, authFetch } from "../auth"; const state = reactive({ loading: false, loaded: false, status: "", onboardingUrl: "/onboarding", dashboardUrl: "/apps", accountUrl: "/account", accessAllowed: true, error: "", }); let watcherStarted = false; let requestToken = 0; function normalizeInternalUrl(value, fallback) { if (typeof value !== "string" || !value.trim()) return fallback; try { const url = new URL(value, window.location.origin); if (url.origin === window.location.origin) return `${url.pathname}${url.search}${url.hash}`; return value; } catch { return value.startsWith("/") ? value : fallback; } } function resetState() { state.loading = false; state.loaded = false; state.status = ""; state.onboardingUrl = "/onboarding"; state.dashboardUrl = "/apps"; state.accountUrl = "/account"; state.accessAllowed = true; state.error = ""; } async function refreshMemberState() { if (!auth.ready || !auth.authenticated) { resetState(); return; } const token = (requestToken += 1); state.loading = true; state.error = ""; try { const resp = await authFetch("/api/account/member-state", { headers: { Accept: "application/json" }, cache: "no-store", }); const data = await resp.json().catch(() => ({})); if (token !== requestToken) return; if (resp.status === 403) { state.loaded = true; state.status = "restricted"; state.accessAllowed = false; state.onboardingUrl = "/request-access"; state.dashboardUrl = "/apps"; state.accountUrl = "/account"; return; } if (!resp.ok) throw new Error(data.error || `status ${resp.status}`); state.loaded = true; state.accessAllowed = true; state.status = data.status || "unknown"; state.onboardingUrl = normalizeInternalUrl(data.onboarding_url, "/onboarding"); state.dashboardUrl = normalizeInternalUrl(data.dashboard_url, "/apps"); state.accountUrl = normalizeInternalUrl(data.account_url, "/account"); } catch (err) { if (token !== requestToken) return; state.loaded = true; state.status = "unknown"; state.accessAllowed = true; state.error = err?.message || "Member state unavailable"; } finally { if (token === requestToken) state.loading = false; } } function startWatcher() { if (watcherStarted) return; watcherStarted = true; watch( () => [auth.ready, auth.authenticated, auth.token], () => { refreshMemberState(); }, { immediate: true }, ); } function memberActionForState() { if (!auth.enabled) return null; if (!auth.ready) { return { kind: "loading", label: "Checking Session", to: "", secondary: "" }; } if (!auth.authenticated) { return { kind: "signed-out", label: "Sign In", to: "", secondary: "/request-access" }; } if (auth.emailVerified === false) { return { kind: "verify", label: "Continue Verification", to: normalizeInternalUrl(auth.accountUrl, "/request-access"), secondary: state.accountUrl, }; } if (state.loading && !state.loaded) { return { kind: "loading", label: "Checking Account", to: "", secondary: state.accountUrl }; } if (["pending_email_verification", "pending"].includes(state.status)) { return { kind: "pending", label: "Check Access Status", to: "/request-access", secondary: state.accountUrl }; } if (["accounts_building", "awaiting_onboarding"].includes(state.status)) { return { kind: "onboarding", label: "Continue Onboarding", to: state.onboardingUrl, secondary: state.accountUrl }; } if (state.status === "restricted" || !state.accessAllowed) { return { kind: "restricted", label: "Account Status", to: "/request-access", secondary: state.accountUrl }; } return { kind: "ready", label: "Open Services", to: state.dashboardUrl, secondary: state.accountUrl }; } export function useMemberAccessState() { startWatcher(); return { auth, memberState: state, memberAction: computed(memberActionForState), refreshMemberState, }; }