feat: add backup recovery, setup wizard and user-view guards
Magent CI/CD / verify (push) Successful in 5m20s
Magent CI/CD / deploy-beta (push) Successful in 1m41s

This commit is contained in:
2026-09-18 17:23:03 +12:00
parent a6a4a9aa24
commit fd6671cf7e
44 changed files with 4650 additions and 114 deletions
+10 -5
View File
@@ -1,12 +1,11 @@
"use client";
import InviteDeliveryChoice from "../../ui/InviteDeliveryChoice";
import PageHeading from "../../ui/PageHeading";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react";
import { authFetch, clearToken, getApiBase, getToken } from "../../lib/auth";
import { useEffectiveRole } from "../../lib/viewMode";
import InviteDeliveryChoice from "../../ui/InviteDeliveryChoice";
import PageHeading from "../../ui/PageHeading";
type ProfileInfo = { username: string; role: string; invite_management_enabled?: boolean };
type OwnedInvite = {
@@ -77,6 +76,10 @@ export default function ProfileInvitesPage() {
const [deliveryMethod, setDeliveryMethod] = useState<DeliveryMethod>("");
const [inviteForm, setInviteForm] = useState<InviteForm>(defaultInviteForm());
const [createdInvite, setCreatedInvite] = useState<OwnedInvite | null>(null);
const effectiveRole = useEffectiveRole(profile?.role);
const canManageInvites =
effectiveRole === "admin" ||
(profile?.role === "admin" ? Boolean(profile.invite_management_enabled) : inviteAccessEnabled);
const signupBaseUrl = useMemo(() => {
if (typeof window === "undefined") return "/signup";
@@ -158,6 +161,7 @@ export default function ProfileInvitesPage() {
const saveInvite = async (event: React.FormEvent) => {
event.preventDefault();
if (!canManageInvites) return;
const inviteName = inviteForm.label.trim();
const recipientEmail = inviteForm.recipient_email.trim();
if (!inviteName) {
@@ -225,6 +229,7 @@ export default function ProfileInvitesPage() {
};
const deleteInvite = async (invite: OwnedInvite) => {
if (!canManageInvites) return;
if (!window.confirm(`Delete invite “${invite.label || invite.code}”?`)) return;
setError(null);
try {
@@ -240,6 +245,7 @@ export default function ProfileInvitesPage() {
};
const copyInviteLink = async (invite: OwnedInvite) => {
if (!canManageInvites) return;
try {
let usableInvite = invite;
if (!invite.code_available) {
@@ -263,7 +269,6 @@ export default function ProfileInvitesPage() {
const codeCharacters = inviteForm.code.replace(/[^a-z0-9]/gi, "");
const identityReady = Boolean(inviteForm.label.trim() && (!useCustomCode || codeCharacters.length >= 6));
const canManageInvites = profile?.role === "admin" || inviteAccessEnabled;
const createdInviteUrl = createdInvite ? `${signupBaseUrl}?code=${encodeURIComponent(createdInvite.code)}` : "";
if (loading) return <main className="card">Loading invite workspace</main>;