Compare commits

..

No commits in common. "f67ebbd98a1365b76e69128f5903eadc0658e34f" and "4432c4e7f66bd0d37593a6f2f7b1b4420bc46467" have entirely different histories.

3 changed files with 167 additions and 194 deletions

View File

@ -477,9 +477,9 @@ export const EditIcon = ({ className }: { className?: string }) => {
return ( return (
<svg <svg
aria-hidden="true" aria-hidden="true"
width="13" width="16"
height="13" height="16"
viewBox="0 0 13 13" viewBox="0 0 16 16"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className={className}> className={className}>

View File

@ -1,73 +1,123 @@
import { cn } from "~/utils"; import { cn } from "~/utils";
import { Avatar } from "./ui/avatar"; import { Avatar } from "./ui/avatar";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { EditIcon, FingerPrintIcon } from "./icons"; import { AddIcon, EditIcon, FingerPrintIcon } from "./icons";
import { Dialog, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog";
import { DialogContent, Portal } from "@radix-ui/react-dialog";
const ManagementCardAvatar = ({ src }: { src?: string }) => { interface ManagementCardProps {
return ( title?: string;
<div className="flex justify-center"> value?: string;
<div className="relative w-fit h-fit"> subtitle?: string;
<Avatar className="border-2 border-ring h-28 w-28" /> isInviteCard?: boolean;
<Button isPasswordCard?: boolean;
variant="outline" isAvatarCard?: boolean;
className="absolute bottom-0 right-0 z-50 flex items-center w-10 h-10 p-0 border-white rounded-full justiyf-center hover:bg-secondary-2"> isDeleteCard?: boolean
<EditIcon /> buttonText?: string;
</Button> buttonOnClick?: () => void
</div> dialogNode?: React.ReactNode
</div> }
);
export const ManagementCard = ({
title,
isAvatarCard,
isInviteCard,
isPasswordCard,
isDeleteCard,
subtitle,
value,
buttonText,
buttonOnClick,
dialogNode
}: ManagementCardProps) => {
const buttonVariant: string = isInviteCard ? "accent" : isDeleteCard ? "destructive" : "default";
return (
<div
className={cn(
"rounded-lg p-8 border w-full",
isInviteCard && "border-ring",
isAvatarCard && "flex justify-center items-center"
)}
>
{isAvatarCard ? (
<div className="relative w-fit h-fit">
<Avatar className="border-2 border-ring h-28 w-28" />
<Button
variant="outline"
className="absolute bottom-0 right-0 z-50 flex items-center w-10 h-10 p-0 border-white rounded-full justiyf-center hover:bg-secondary-2"
>
<EditIcon className="mt-1 ml-1" />
</Button>
</div>
) : (
<>
<div className="flex items-center gap-x-2">
<FingerPrintIcon className="text-ring" />
<h4 className="font-bold">{title}</h4>
</div>
{subtitle && (
<span className="block mt-4 mb-8 text-sm text-primary-2">{subtitle}</span>
)}
{value && (
<span className="block mt-4 mb-8 text-sm font-bold text-ring">{value}</span>
)}
{isPasswordCard && <PasswordDots className="mt-6 mb-8 text-primary-2" />}
{!dialogNode ? (
<Button
onClick={buttonOnClick}
className={`h-12 gap-x-2`}
variant={buttonVariant}
>
<AddIcon />
{buttonText}
</Button>
): (
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
)}
</>
)}
</div>
);
}; };
const ManagementCardTitle = ({ const PasswordDots = ({ className }: { className?: string }) => {
children, return (
className, <svg
}: React.PropsWithChildren<{ className?: string }>) => { width="219"
return ( height="7"
<div className={cn("flex items-center gap-x-2 font-semibold", className)}> viewBox="0 0 219 7"
<FingerPrintIcon className="text-ring" /> fill="none"
{children} xmlns="http://www.w3.org/2000/svg"
</div> className={className}
); >
<circle cx="3.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="31.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="45.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="17.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="59.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="73.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="87.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="101.777" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="131.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="117.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="145.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="159.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="173.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="187.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="201.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="215.5" cy="3.5" r="3.5" fill="currentColor" />
</svg>
);
}; };
const ManagementCardContent = ({
children,
className,
}: React.PropsWithChildren<{ className?: string }>) => {
return (
<div className={cn("mt-4 mb-8 text-sm text-primary-2", className)}>
{children}
</div>
);
};
const ManagementCardFooter = ({
children,
className,
}: React.PropsWithChildren<{ className?: string }>) => {
return <div className={className}>{children}</div>;
};
const ManagementCard = ({
children,
variant,
}: React.PropsWithChildren<{ variant?: string }>) => {
return (
<div
className={cn(
"rounded-lg p-8 border w-full border-[--variant-color]",
!variant && "[--variant-color:theme(colors.border)]",
variant === "accent" && "[--variant-color:theme(colors.primary-1.DEFAULT)]",
)}>
{children}
</div>
);
};
export {
ManagementCard,
ManagementCardAvatar,
ManagementCardContent,
ManagementCardFooter,
ManagementCardTitle,
};

View File

@ -4,8 +4,8 @@ import { useState } from "react";
import { z } from "zod"; import { z } from "zod";
import { Field } from "~/components/forms"; import { Field } from "~/components/forms";
import { GeneralLayout } from "~/components/general-layout"; import { GeneralLayout } from "~/components/general-layout";
import { AddIcon, CloudIcon, CrownIcon } from "~/components/icons"; import { AddIcon, CloudIcon } from "~/components/icons";
import { ManagementCard, ManagementCardAvatar, ManagementCardContent, ManagementCardFooter, ManagementCardTitle } from "~/components/management-card"; import { ManagementCard } from "~/components/management-card";
import { Button } from "~/components/ui/button"; import { Button } from "~/components/ui/button";
import { import {
Dialog, Dialog,
@ -47,106 +47,58 @@ export default function MyAccount() {
/> />
<h2 className="font-bold my-8">Account Management</h2> <h2 className="font-bold my-8">Account Management</h2>
<div className="grid grid-cols-3 gap-x-8"> <div className="grid grid-cols-3 gap-x-8">
<ManagementCard> <ManagementCard isAvatarCard />
<ManagementCardAvatar /> <ManagementCard
</ManagementCard> title="Email Address"
<ManagementCard> value="bsimpson@springfield.oh.gov.com"
<ManagementCardTitle>Email Address</ManagementCardTitle> buttonText="Change Email Address"
<ManagementCardContent className="text-ring font-semibold"> buttonOnClick={() => setModal({ ...openModal, changeEmail: true })}
bsimpson@springfield.oh.gov.com />
</ManagementCardContent> <ManagementCard
<ManagementCardFooter> title="Account Type"
<Button className="h-12 gap-x-2" onClick={() => setModal({ ...openModal, changeEmail: true })}> value="Lite Premium Account"
<AddIcon /> buttonText="Upgrade to Premium"
Change Email Address />
</Button>
</ManagementCardFooter>
</ManagementCard>
<ManagementCard>
<ManagementCardTitle>Account Type</ManagementCardTitle>
<ManagementCardContent className="text-ring font-semibold flex gap-x-2">
Lite Premium Account
<CrownIcon />
</ManagementCardContent>
<ManagementCardFooter>
<Button className="h-12 gap-x-2">
<AddIcon />
Upgrade to Premium
</Button>
</ManagementCardFooter>
</ManagementCard>
</div> </div>
<h2 className="font-bold my-8">Security</h2> <h2 className="font-bold my-8">Security</h2>
<div className="grid grid-cols-3 gap-x-8"> <div className="grid grid-cols-3 gap-x-8">
<ManagementCard> <ManagementCard
<ManagementCardTitle>Password</ManagementCardTitle> isPasswordCard
<ManagementCardContent> title="Password"
<PasswordDots className="mt-6" /> buttonText="Change Password"
</ManagementCardContent> buttonOnClick={() => setModal({ ...openModal, changePassword: true })}
<ManagementCardFooter> />
<Button className="h-12 gap-x-2" onClick={() => setModal({ ...openModal, changePassword: true })}> <ManagementCard
<AddIcon /> title="Two-Factor Authentication"
Change Password subtitle="Improve security by enabling 2FA."
</Button> buttonText="Enable Two-Factor Authorization"
</ManagementCardFooter> buttonOnClick={() => setModal({ ...openModal, setupTwoFactor: true })}
</ManagementCard> />
<ManagementCard> <ManagementCard
<ManagementCardTitle>Two-Factor Authentication</ManagementCardTitle> title="Backup Key"
<ManagementCardContent> subtitle="Never share this code with anyone."
Improve security by enabling 2FA. buttonText="Export Backup Key"
</ManagementCardContent> />
<ManagementCardFooter>
<Button className="h-12 gap-x-2" onClick={() => setModal({ ...openModal, setupTwoFactor: true })}>
<AddIcon />
Enable Two-Factor Authorization
</Button>
</ManagementCardFooter>
</ManagementCard>
<ManagementCard>
<ManagementCardTitle>Backup Key</ManagementCardTitle>
<ManagementCardContent>
Never share this code with anyone.
</ManagementCardContent>
<ManagementCardFooter>
<Button className="h-12 gap-x-2">
<AddIcon />
Export Backup Key
</Button>
</ManagementCardFooter>
</ManagementCard>
</div> </div>
<h2 className="font-bold my-8">More</h2> <h2 className="font-bold my-8">More</h2>
<div className="grid grid-cols-3 gap-x-8"> <div className="grid grid-cols-3 gap-x-8">
<ManagementCard variant="accent"> <ManagementCard
<ManagementCardTitle>Invite a Friend</ManagementCardTitle> isInviteCard
<ManagementCardContent>Get 1 GB per friend invited for free (max 5 GB).</ManagementCardContent> title="Invite a Friend"
<ManagementCardFooter> subtitle="Get 1 GB per friend invited for free (max 5 GB)."
<Button variant="accent" className="h-12 gap-x-2"> buttonText="Send Invitation"
<AddIcon /> />
Send Invitation <ManagementCard
</Button> title="Read our Resources"
</ManagementCardFooter> subtitle="Navigate helpful articles or get assistance."
</ManagementCard> buttonText="Open Support Centre"
<ManagementCard> />
<ManagementCardTitle>Read our Resources</ManagementCardTitle> <ManagementCard
<ManagementCardContent>Navigate helpful articles or get assistance.</ManagementCardContent> isDeleteCard
<ManagementCardFooter> title="Delete Account"
<Button className="h-12 gap-x-2"> subtitle="Once initiated, this action cannot be undone."
<AddIcon /> buttonText="Delete my Account"
Open Support Centre />
</Button>
</ManagementCardFooter>
</ManagementCard>
<ManagementCard>
<ManagementCardTitle>Delete Account</ManagementCardTitle>
<ManagementCardContent>Once initiated, this action cannot be undone.</ManagementCardContent>
<ManagementCardFooter>
<Button className="h-12 gap-x-2" variant="destructive">
<AddIcon />
Delete my Account
</Button>
</ManagementCardFooter>
</ManagementCard>
</div> </div>
{/* Dialogs must be near to body as possible to open the modal, otherwise will be restricted to parent height-width */} {/* Dialogs must be near to body as possible to open the modal, otherwise will be restricted to parent height-width */}
<ChangeEmailForm <ChangeEmailForm
@ -342,32 +294,3 @@ const SetupTwoFactorDialog = ({
</Dialog> </Dialog>
); );
}; };
const PasswordDots = ({ className }: { className?: string }) => {
return (
<svg
width="219"
height="7"
viewBox="0 0 219 7"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}>
<circle cx="3.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="31.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="45.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="17.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="59.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="73.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="87.7771" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="101.777" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="131.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="117.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="145.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="159.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="173.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="187.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="201.5" cy="3.5" r="3.5" fill="currentColor" />
<circle cx="215.5" cy="3.5" r="3.5" fill="currentColor" />
</svg>
);
};