Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions apps/web/src/routes/_view/app/-account-access.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { useMutation } from "@tanstack/react-query";
import { useNavigate } from "@tanstack/react-router";
import { useState } from "react";

import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@hypr/ui/components/ui/accordion";

import { signOutFn } from "@/functions/auth";
import { deleteAccount } from "@/functions/billing";

export function AccountAccessSection() {
const navigate = useNavigate();
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);

const signOut = useMutation({
mutationFn: async () => {
const res = await signOutFn();
if (res.success) {
return true;
}

throw new Error(res.message);
},
onSuccess: () => {
navigate({ to: "/" });
},
onError: (error) => {
console.error(error);
navigate({ to: "/" });
},
});

const deleteAccountMutation = useMutation({
mutationFn: () => deleteAccount(),
onSuccess: () => {
navigate({ to: "/" });
},
});

return (
<div className="rounded-xs border border-neutral-100">
<div className="p-4">
<h3 className="mb-2 font-serif text-lg font-semibold">Access</h3>
<p className="text-sm text-neutral-600">
Session controls and destructive account actions
</p>
</div>

<div className="flex flex-col gap-4 border-t border-neutral-100 p-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<div className="text-sm font-medium text-neutral-900">Sign out</div>
<p className="text-sm text-neutral-600">
End your current session on this device
</p>
</div>

<button
onClick={() => signOut.mutate()}
disabled={signOut.isPending}
className="flex h-8 cursor-pointer items-center justify-center rounded-full border border-neutral-300 bg-linear-to-b from-white to-stone-50 px-4 text-sm text-neutral-700 shadow-xs transition-all hover:scale-[102%] hover:shadow-md active:scale-[98%] disabled:opacity-50 disabled:hover:scale-100"
>
{signOut.isPending ? "Signing out..." : "Sign out"}
</button>
</div>

<div className="border-t border-neutral-100 px-4">
<Accordion
type="single"
collapsible
onValueChange={(value) => {
if (!value) {
setShowDeleteConfirm(false);
deleteAccountMutation.reset();
}
}}
>
<AccordionItem value="delete-account" className="border-none">
<AccordionTrigger className="py-4 text-sm font-medium text-red-700 hover:text-red-800 hover:no-underline">
Delete account
</AccordionTrigger>
<AccordionContent className="pb-4">
<div className="rounded-md border border-red-200 bg-red-50 p-4">
<p className="text-sm text-red-900">
Char is a local-first app. Your notes, transcripts, and
meeting data stay on your device. Deleting your account only
removes cloud-stored data.
</p>

{showDeleteConfirm ? (
<div className="mt-4 space-y-3">
<p className="text-sm text-red-800">
This permanently deletes your account and cloud data.
</p>

{deleteAccountMutation.isError && (
<p className="text-sm text-red-600">
{deleteAccountMutation.error?.message ||
"Failed to delete account"}
</p>
)}

<div className="flex flex-wrap gap-2">
<button
onClick={() => deleteAccountMutation.mutate()}
disabled={deleteAccountMutation.isPending}
className="flex h-8 items-center rounded-full bg-red-600 px-4 text-sm text-white shadow-md transition-all hover:scale-[102%] hover:shadow-lg active:scale-[98%] disabled:opacity-50 disabled:hover:scale-100"
>
{deleteAccountMutation.isPending
? "Deleting..."
: "Yes, delete my account"}
</button>
<button
onClick={() => {
setShowDeleteConfirm(false);
deleteAccountMutation.reset();
}}
disabled={deleteAccountMutation.isPending}
className="flex h-8 items-center rounded-full border border-red-200 bg-white px-4 text-sm text-red-700 transition-all hover:border-red-300 hover:text-red-800 disabled:opacity-50"
>
Cancel
</button>
</div>
</div>
) : (
<button
onClick={() => setShowDeleteConfirm(true)}
className="mt-4 flex h-8 cursor-pointer items-center rounded-full border border-red-200 bg-white px-4 text-sm text-red-700 transition-all hover:border-red-300 hover:text-red-800"
>
Continue
</button>
)}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
</div>
);
}
74 changes: 0 additions & 74 deletions apps/web/src/routes/_view/app/-account-delete.tsx

This file was deleted.

45 changes: 0 additions & 45 deletions apps/web/src/routes/_view/app/-account-sign-out.tsx

This file was deleted.

49 changes: 40 additions & 9 deletions apps/web/src/routes/_view/app/account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { z } from "zod";

import { desktopSchemeSchema } from "@/functions/desktop-flow";

import { DeleteAccountSection } from "./-account-delete";
import { AccountAccessSection } from "./-account-access";
import { IntegrationsSettingsCard } from "./-account-integrations";
import { ProfileInfoSection } from "./-account-profile-info";
import { AccountSettingsCard } from "./-account-settings";
import { SignOutSection } from "./-account-sign-out";

const validateSearch = z
.object({
Expand Down Expand Up @@ -43,16 +42,48 @@ function Component() {
</h1>
</div>

<div className="mx-auto mt-8 flex max-w-4xl flex-col gap-6 px-4 pb-20">
<ProfileInfoSection email={user?.email} />
<div className="mx-auto mt-8 flex max-w-4xl flex-col gap-10 px-4 pb-20">
<section className="space-y-4">
<div className="space-y-2 px-1">
<p className="text-xs font-medium tracking-[0.18em] text-neutral-400 uppercase">
Account
</p>
<div>
<h2 className="font-serif text-2xl font-medium text-stone-950">
Profile, billing, and connected services
</h2>
<p className="text-sm text-neutral-600">
Update the essentials without burying routine settings behind
destructive actions.
</p>
</div>
</div>

<AccountSettingsCard />
<div className="space-y-6">
<ProfileInfoSection email={user?.email} />
<AccountSettingsCard />
<IntegrationsSettingsCard />
</div>
</section>

<IntegrationsSettingsCard />
<section className="space-y-4">
<div className="space-y-2 px-1">
<p className="text-xs font-medium tracking-[0.18em] text-neutral-400 uppercase">
Access
</p>
<div>
<h2 className="font-serif text-2xl font-medium text-stone-950">
Session controls
</h2>
<p className="text-sm text-neutral-600">
Sign out quickly, while keeping account deletion tucked behind
an extra deliberate step.
</p>
</div>
</div>

<DeleteAccountSection />

<SignOutSection />
<AccountAccessSection />
</section>
</div>
</div>
</div>
Expand Down
Loading