+ {/* Quick stats (lightweight “premium” feel) */}
+ {/*
+
+
+
+
+
*/}
+
+
+
+ Read-only (for now)
+
+ }
+ >
+
+
+
+
+
+
-
-
- Email:{" "}
- {user.email}
-
-
- Display name:{" "}
- {user.displayName || "—"}
-
-
- Role:{" "}
- {user.role}
-
+
+
+ Coming soon
+
+ }
+ >
+
+
+
+
+
+
+
+
+
+
+ Coming soon
+
+ }
+ >
+
+
+
2FA
+
+ Not enabled yet (on the roadmap).
+
+
+
+
+
+ Active sessions
+
+
+ We’ll show devices + allow logout everywhere.
+
+
+
+
+
+
+
+
+
+
+
+ Delete account
+
+
+ Removes your account and builds. (We’ll add this flow later.)
+
+
+
+
+
);
diff --git a/app/(account)/layout.tsx b/app/(account)/layout.tsx
index a77905a..53d7393 100644
--- a/app/(account)/layout.tsx
+++ b/app/(account)/layout.tsx
@@ -1,56 +1,57 @@
// app/(account)/layout.tsx
import Link from "next/link";
+import AccountChrome from "./AccountChrome";
const nav = [
{ href: "/account", label: "My Account" },
{ href: "/account/settings", label: "Settings" },
+ { href: "/vault", label: "My Vault" },
];
-export default function AccountLayout({
- children,
-}: {
- children: React.ReactNode;
-}) {
+export default function AccountLayout({ children }: { children: React.ReactNode }) {
return (
-
+
+
+
{/* Header */}
-
+
-
Account
-
+
+ Account
+
+
+ Settings & Profile
+
+
Profile, password, and account settings.
-
+
← Back to Builder
-
-
);
-}
\ No newline at end of file
+}
+export default TopNav;
\ No newline at end of file
diff --git a/context/AuthContext.tsx b/context/AuthContext.tsx
index 48c923a..3d91305 100644
--- a/context/AuthContext.tsx
+++ b/context/AuthContext.tsx
@@ -30,6 +30,19 @@ type AuthContextValue = {
}) => Promise
;
logout: () => void;
getAuthHeaders: () => HeadersInit;
+
+ /**
+ * Used for non-password auth flows (ex: magic link).
+ * Persists session exactly like login/register.
+ */
+ setSession: (
+ token: string,
+ user: {
+ email: string;
+ displayName: string | null;
+ role: string;
+ }
+ ) => void;
};
const AuthContext = createContext(undefined);
@@ -42,6 +55,25 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
+ const persistAuth = useCallback(
+ (nextToken: string | null, nextUser: AuthUser) => {
+ if (typeof window === "undefined") return;
+
+ if (nextToken) {
+ window.localStorage.setItem(TOKEN_KEY, nextToken);
+ } else {
+ window.localStorage.removeItem(TOKEN_KEY);
+ }
+
+ if (nextUser) {
+ window.localStorage.setItem(USER_KEY, JSON.stringify(nextUser));
+ } else {
+ window.localStorage.removeItem(USER_KEY);
+ }
+ },
+ []
+ );
+
// Hydrate from localStorage on first load
useEffect(() => {
if (typeof window === "undefined") return;
@@ -49,15 +81,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const storedToken = window.localStorage.getItem(TOKEN_KEY);
const storedUser = window.localStorage.getItem(USER_KEY);
- if (storedToken) {
- setToken(storedToken);
- }
+ if (storedToken) setToken(storedToken);
if (storedUser) {
try {
setUser(JSON.parse(storedUser));
} catch {
- // bad JSON? wipe it
window.localStorage.removeItem(USER_KEY);
}
}
@@ -65,21 +94,17 @@ export function AuthProvider({ children }: { children: ReactNode }) {
setLoading(false);
}, []);
- const persistAuth = useCallback((nextToken: string | null, nextUser: AuthUser) => {
- if (typeof window === "undefined") return;
-
- if (nextToken) {
- window.localStorage.setItem(TOKEN_KEY, nextToken);
- } else {
- window.localStorage.removeItem(TOKEN_KEY);
- }
-
- if (nextUser) {
- window.localStorage.setItem(USER_KEY, JSON.stringify(nextUser));
- } else {
- window.localStorage.removeItem(USER_KEY);
- }
- }, []);
+ const setSession = useCallback(
+ (
+ nextToken: string,
+ nextUser: { email: string; displayName: string | null; role: string }
+ ) => {
+ setToken(nextToken);
+ setUser(nextUser);
+ persistAuth(nextToken, nextUser);
+ },
+ [persistAuth]
+ );
const login = useCallback(
async ({ email, password }: { email: string; password: string }) => {
@@ -98,7 +123,6 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const data = await res.json();
- // Adjust these to match your backend response shape
const nextToken: string = data.token ?? data.accessToken;
const nextUser: AuthUser =
data.user ??
@@ -108,14 +132,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
role: data.role ?? "USER",
} as AuthUser);
- setToken(nextToken);
- setUser(nextUser);
- persistAuth(nextToken, nextUser);
+ setSession(nextToken, nextUser as NonNullable);
} finally {
setLoading(false);
}
},
- [persistAuth]
+ [setSession]
);
const register = useCallback(
@@ -152,14 +174,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
role: data.role ?? "USER",
} as AuthUser);
- setToken(nextToken);
- setUser(nextUser);
- persistAuth(nextToken, nextUser);
+ setSession(nextToken, nextUser as NonNullable);
} finally {
setLoading(false);
}
},
- [persistAuth]
+ [setSession]
);
const logout = useCallback(() => {
@@ -180,12 +200,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
register,
logout,
getAuthHeaders,
+ setSession, // ✅ important
};
return {children};
}
-// 🔑 This is what your useApi hook imports
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext);
if (!ctx) {