'use client';
//===========================================
// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template
//===========================================
import { runAsynchronouslyWithAlert } from "@hexclave/shared/dist/utils/promises";
import { Skeleton, Typography } from '@hexclave/ui';
import { Contact, ShieldCheck, Bell, Monitor, Key, Settings, CirclePlus, CreditCard } from 'lucide-react';
import React, { Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useStackApp, useUser } from '..';
import { MaybeFullPage } from "../components/elements/maybe-full-page";
import { SidebarLayout } from '../components/elements/sidebar-layout';
import { TeamIcon } from '../components/team-icon';
import { useTranslation } from "../lib/translations";
import { ActiveSessionsPage } from "./account-settings/active-sessions/active-sessions-page";
import { ApiKeysPage } from "./account-settings/api-keys/api-keys-page";
import { EmailsAndAuthPage } from './account-settings/email-and-auth/email-and-auth-page';
import { NotificationsPage } from './account-settings/notifications/notifications-page';
import { ProfilePage } from "./account-settings/profile-page/profile-page";
import { PaymentsPage } from "./account-settings/payments/payments-page";
import { SettingsPage } from './account-settings/settings/settings-page';
import { TeamCreationPage } from './account-settings/teams/team-creation-page';
import { TeamPage } from './account-settings/teams/team-page';
const iconMap = {
Contact,
ShieldCheck,
Bell,
Monitor,
Key,
Settings,
CirclePlus,
CreditCard,
} as const;
const Icon = ({ name }: { name: keyof typeof iconMap }) => {
const LucideIcon = iconMap[name];
return ;
};
export function AccountSettings(props: {
fullPage?: boolean,
extraItems?: ({
title: string,
content: React.ReactNode,
id: string,
} & ({
icon?: React.ReactNode,
} | {
iconName?: keyof typeof iconMap,
}))[],
mockUser?: {
displayName?: string,
profileImageUrl?: string,
},
mockApiKeys?: Array<{
id: string,
description: string,
createdAt: string,
expiresAt?: string,
manuallyRevokedAt?: string,
}>,
mockProject?: {
config: {
allowUserApiKeys: boolean,
clientTeamCreationEnabled: boolean,
},
},
mockSessions?: Array<{
id: string,
isCurrentSession: boolean,
isImpersonation?: boolean,
createdAt: string,
lastUsedAt?: string,
geoInfo?: {
ip?: string,
cityName?: string,
},
}>,
}) {
const { t } = useTranslation();
const userFromHook = useUser({ or: props.mockUser ? 'return-null' : 'redirect' });
const hexclaveApp = useStackApp();
const projectFromHook = hexclaveApp.useProject();
// Use mock data if provided, otherwise use real data
const user = props.mockUser ? {
useTeams: () => [], // Mock empty teams for now
useBilling: () => ({ hasCustomer: false }), // Mock empty billing for now
} : userFromHook;
const project = props.mockProject || projectFromHook;
const teams = user?.useTeams() || [];
const billing = user?.useBilling() || null;
const teamsKey = useMemo(() => teams.map(team => team.id).join("|"), [teams]);
const teamsById = useMemo(() => teams, [teamsKey]);
const userRef = useRef(userFromHook ?? null);
const userId = userFromHook?.id ?? null;
const [paymentsAvailability, setPaymentsAvailability] = useState<{
userHasProducts: boolean,
teamIdsWithProducts: Set,
isReady: boolean,
}>(() => ({
userHasProducts: false,
teamIdsWithProducts: new Set(),
isReady: !!props.mockUser,
}));
useEffect(() => {
userRef.current = userFromHook ?? null;
}, [userFromHook]);
useEffect(() => {
if (props.mockUser || !userId) {
return;
}
let cancelled = false;
runAsynchronouslyWithAlert(async () => {
const currentUser = userRef.current;
if (!currentUser || currentUser.id !== userId) {
return;
}
const [userProducts, teamsWithProducts] = await Promise.all([
currentUser.listProducts({ limit: 1 }),
Promise.all(teamsById.map(async (team) => {
const isTeamAdmin = await currentUser.hasPermission(team, "team_admin");
if (!isTeamAdmin) {
return null;
}
const teamProducts = await team.listProducts({ limit: 1 });
const hasTeamProducts = teamProducts.some((product) => product.customerType === "team");
return hasTeamProducts ? team.id : null;
})),
]);
if (cancelled) {
return;
}
const userHasProducts = userProducts.some((product) => product.customerType === "user");
const teamIdsWithProducts = new Set(teamsWithProducts.filter((id): id is string => id !== null));
setPaymentsAvailability({
userHasProducts,
teamIdsWithProducts,
isReady: true,
});
});
return () => {
cancelled = true;
};
}, [props.mockUser, teamsById, userId]);
const teamsWithProducts = useMemo(
() => teamsById.filter(team => paymentsAvailability.teamIdsWithProducts.has(team.id)),
[paymentsAvailability.teamIdsWithProducts, teamsById],
);
const shouldShowPaymentsTab = props.mockUser
|| (paymentsAvailability.isReady
&& (paymentsAvailability.userHasProducts || teamsWithProducts.length > 0));
// If we're not in mock mode and don't have a user, the useUser hook will handle redirect
if (!props.mockUser && !userFromHook) {
return null;
}
return (
,
content:
,
},
{
title: t('Emails & Auth'),
type: 'item',
id: 'auth',
icon: ,
content: }>
,
},
{
title: t('Notifications'),
type: 'item',
id: 'notifications',
icon: ,
content: }>
,
},
{
title: t('Active Sessions'),
type: 'item',
id: 'sessions',
icon: ,
content: }>
,
},
...(project.config.allowUserApiKeys ? [{
title: t('API Keys'),
type: 'item',
id: 'api-keys',
icon: ,
content: }>
,
}] as const : []),
...(shouldShowPaymentsTab ? [{
title: t('Payments'),
type: 'item',
id: 'payments',
icon: ,
content: }>
,
}] as const : []),
{
title: t('Settings'),
type: 'item',
id: 'settings',
icon: ,
content: ,
},
...(props.extraItems?.map(item => ({
title: item.title,
type: 'item',
id: item.id,
icon: (() => {
const iconName = (item as any).iconName as keyof typeof iconMap | undefined;
if (iconName) {
return ;
} else if ((item as any).icon) {
return (item as any).icon;
}
return null;
})(),
content: item.content,
} as const)) || []),
...(teams.length > 0 || project.config.clientTeamCreationEnabled) ? [{
title: t('Teams'),
type: 'divider',
}] as const : [],
...teams.map(team => ({
title:
{team.displayName}
,
type: 'item',
id: `team-${team.id}`,
content: }>
,
} as const)),
...project.config.clientTeamCreationEnabled ? [{
title: t('Create a team'),
icon: ,
type: 'item',
id: 'team-creation',
content: }>
,
}] as const : [],
] as const).filter((p) => p.type === 'divider' || (p as any).content )}
title={t("Account Settings")}
/>
);
}
function PageLayout(props: { children: React.ReactNode }) {
return (
{props.children}
);
}
function EmailsAndAuthPageSkeleton() {
return
;
}
function ActiveSessionsPageSkeleton() {
return
;
}
function ApiKeysPageSkeleton() {
return
;
}
function PaymentsPageSkeleton() {
return
;
}
function TeamPageSkeleton() {
return
;
}
function TeamCreationSkeleton() {
return
;
}
function NotificationsPageSkeleton() {
return
;
}