"use client"; export const dynamic = "force-dynamic"; import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { useAuth, useUser } from "@/auth/clerk"; import { useQueryClient } from "@tanstack/react-query"; import { Globe, Mail, RotateCcw, Save, Trash2, User } from "lucide-react"; import { useDeleteMeApiV1UsersMeDelete, getGetMeApiV1UsersMeGetQueryKey, type getMeApiV1UsersMeGetResponse, useGetMeApiV1UsersMeGet, useUpdateMeApiV1UsersMePatch, } from "@/api/generated/users/users"; import { ApiError } from "@/api/mutator"; import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout"; import { Button } from "@/components/ui/button"; import { ConfirmActionDialog } from "@/components/ui/confirm-action-dialog"; import { Input } from "@/components/ui/input"; import SearchableSelect from "@/components/ui/searchable-select"; import { getSupportedTimezones } from "@/lib/timezones"; type ClerkGlobal = { signOut?: (options?: { redirectUrl?: string }) => Promise | void; }; export default function SettingsPage() { const router = useRouter(); const queryClient = useQueryClient(); const { isSignedIn } = useAuth(); const { user } = useUser(); const [name, setName] = useState(""); const [timezone, setTimezone] = useState(null); const [nameEdited, setNameEdited] = useState(false); const [timezoneEdited, setTimezoneEdited] = useState(false); const [saveError, setSaveError] = useState(null); const [saveSuccess, setSaveSuccess] = useState(null); const [deleteError, setDeleteError] = useState(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const meQuery = useGetMeApiV1UsersMeGet< getMeApiV1UsersMeGetResponse, ApiError >({ query: { enabled: Boolean(isSignedIn), retry: false, refetchOnMount: "always", }, }); const meQueryKey = getGetMeApiV1UsersMeGetQueryKey(); const profile = meQuery.data?.status === 200 ? meQuery.data.data : null; const clerkFallbackName = user?.fullName ?? user?.firstName ?? user?.username ?? ""; const displayEmail = profile?.email ?? user?.primaryEmailAddress?.emailAddress ?? ""; const resolvedName = nameEdited ? name : (profile?.name ?? profile?.preferred_name ?? clerkFallbackName); const resolvedTimezone = timezoneEdited ? (timezone ?? "") : (profile?.timezone ?? ""); const timezones = useMemo(() => getSupportedTimezones(), []); const timezoneOptions = useMemo( () => timezones.map((value) => ({ value, label: value })), [timezones], ); const updateMeMutation = useUpdateMeApiV1UsersMePatch({ mutation: { onSuccess: async () => { setSaveError(null); setSaveSuccess("Settings saved."); await queryClient.invalidateQueries({ queryKey: meQueryKey }); }, onError: (error) => { setSaveSuccess(null); setSaveError(error.message || "Unable to save settings."); }, }, }); const deleteAccountMutation = useDeleteMeApiV1UsersMeDelete({ mutation: { onSuccess: async () => { setDeleteError(null); if (typeof window !== "undefined") { const clerk = (window as Window & { Clerk?: ClerkGlobal }).Clerk; if (clerk?.signOut) { try { await clerk.signOut({ redirectUrl: "/sign-in" }); return; } catch { // Fall through to local redirect. } } } router.replace("/sign-in"); }, onError: (error) => { setDeleteError(error.message || "Unable to delete account."); }, }, }); const handleSave = async (event: React.FormEvent) => { event.preventDefault(); if (!isSignedIn) return; if (!resolvedName.trim() || !resolvedTimezone.trim()) { setSaveSuccess(null); setSaveError("Name and timezone are required."); return; } setSaveError(null); setSaveSuccess(null); await updateMeMutation.mutateAsync({ data: { name: resolvedName.trim(), timezone: resolvedTimezone.trim(), }, }); }; const handleReset = () => { setName(""); setTimezone(null); setNameEdited(false); setTimezoneEdited(false); setSaveError(null); setSaveSuccess(null); }; const isSaving = updateMeMutation.isPending; return ( <>

Profile

Keep your identity and timezone up to date.

{ setName(event.target.value); setNameEdited(true); }} placeholder="Your name" disabled={isSaving} className="border-slate-300 text-slate-900 focus-visible:ring-blue-500" />
{ setTimezone(value); setTimezoneEdited(true); }} options={timezoneOptions} placeholder="Select timezone" searchPlaceholder="Search timezones..." emptyMessage="No matching timezones." disabled={isSaving} triggerClassName="w-full h-11 rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-medium text-slate-900 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-200" contentClassName="rounded-xl border border-slate-200 shadow-lg" itemClassName="px-4 py-3 text-sm text-slate-700 data-[selected=true]:bg-slate-50 data-[selected=true]:text-slate-900" />
{saveError ? (
{saveError}
) : null} {saveSuccess ? (
{saveSuccess}
) : null}

Delete account

This permanently removes your Pipeline account and related personal data. This action cannot be undone.

deleteAccountMutation.mutate()} isConfirming={deleteAccountMutation.isPending} errorMessage={deleteError} confirmLabel="Delete account" confirmingLabel="Deleting account…" ariaLabel="Delete account confirmation" /> ); }