'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 ; }