Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat]: add AlertDialog component #58

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
"dependencies": {
"@floating-ui/react-dom": "^2.0.8",
"@hapi/accept": "^6.0.3",
"@headlessui/react": "^1.7.2",
"@headlessui/react": "^1.7.18",
"@next/bundle-analyzer": "^12.2.1",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@resvg/resvg-js": "^2.6.2",
"@statsfm/statsfm.js": "^2.2.1",
"@tailwindcss/typography": "^0.5.7",
Expand Down
69 changes: 36 additions & 33 deletions src/components/Gift/Coupon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,48 @@ export const Coupon: FC<{ giftcode: GiftCode }> = ({ giftcode }) => {
const [open, setOpen] = useState(false);

return (
<div
className="max-h-max max-w-max rounded-2xl bg-foreground p-5"
onClick={() => {
setOpen(true);
}}
>
<span
className="w-max cursor-pointer select-none rounded-xl bg-primary/10 px-4 py-1 font-bold text-primary"
onClick={() => navigator.clipboard.writeText(giftcode.code)}
<>
<div
className="max-h-max max-w-max cursor-pointer rounded-2xl bg-foreground p-5"
onClick={() => setOpen(true)}
>
{giftcode.code}
</span>
{giftcode.claimedAt ? (
<p className="mt-3 text-center">
Claimed {dayjs(giftcode.claimedAt).fromNow()} by{' '}
<Link
legacyBehavior
href={`/user/${
giftcode.claimedBy ? giftcode.claimedBy.id : giftcode.claimedById
}`}
>
<a className="font-bold text-white hover:opacity-70">
{giftcode.claimedBy
? giftcode.claimedBy.displayName
: giftcode.claimedById}
</a>
</Link>
</p>
) : (
<p className="mb-0 mt-3 text-center">
Purchased {dayjs(giftcode.purchasedAt).fromNow()}
</p>
)}
<span
className="w-max cursor-pointer select-none rounded-xl bg-primary/10 px-4 py-1 font-bold text-primary"
onClick={() => navigator.clipboard.writeText(giftcode.code)}
>
{giftcode.code}
</span>
{giftcode.claimedAt ? (
<p className="mt-3 text-center">
Claimed {dayjs(giftcode.claimedAt).fromNow()} by{' '}
<Link
legacyBehavior
href={`/user/${
giftcode.claimedBy
? giftcode.claimedBy.id
: giftcode.claimedById
}`}
>
<a className="font-bold text-white hover:opacity-70">
{giftcode.claimedBy
? giftcode.claimedBy.displayName
: giftcode.claimedById}
</a>
</Link>
</p>
) : (
<p className="mb-0 mt-3 text-center">
Purchased {dayjs(giftcode.purchasedAt).fromNow()}
</p>
)}
</div>

{/* TODO: move this modal to a more central place bc this is not optimal for performance */}
<CouponModal
open={open}
onClose={() => setOpen(false)}
giftCode={giftcode}
/>
</div>
</>
);
};
194 changes: 112 additions & 82 deletions src/components/Gift/CouponModal.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useApi, useToaster } from '@/hooks';
import type { GiftCode } from '@/utils/statsfm';
import { Dialog } from '@headlessui/react';
import { Dialog, Transition } from '@headlessui/react';
import dayjs from 'dayjs';
import Link from 'next/link';
import type { FC } from 'react';
import { useCallback, useState } from 'react';
import { Fragment, useCallback, useState } from 'react';
import { Button } from '@/components/Button';
import { Divider } from '@/components/Divider';
import { Overlay } from '@/components/Overlay';
import { Textarea } from '@/components/Textarea';

export const CouponModal: FC<{
Expand Down Expand Up @@ -44,86 +43,117 @@ export const CouponModal: FC<{
}, [message]);

return (
<Dialog
open={open}
onClose={onClose}
className="fixed inset-0 z-50 flex items-center justify-center"
>
<Overlay visible={open} className="fixed top-0 z-20" />
<Dialog.Panel className="z-40 max-h-max cursor-default rounded-2xl bg-foreground p-5 shadow-xl">
<Dialog.Title as="h1">
Coupon <span className="text-neutral-400">#{giftCode.id}</span>
</Dialog.Title>
<div className="mt-2 flex w-[90vw] flex-col gap-2 md:w-[60vw]">
<div className="flex justify-between gap-5">
<span>Purchased On</span>
<span className="text-white">
{dayjs(giftCode.purchasedAt).format('L')}
</span>
</div>
<div className="flex justify-between gap-5">
<span>Bought by</span>
<Link legacyBehavior href={`/user/${giftCode.boughtBy.id}`}>
<a className="font-bold text-primary">
{giftCode.boughtBy.displayName}
</a>
</Link>
</div>
<div className="flex justify-between gap-5">
<span>Claimed by</span>
{giftCode.claimedBy ? (
<Link legacyBehavior href={`/user/${giftCode.claimedBy.id}`}>
<a className="font-bold text-primary">
{giftCode.claimedBy.displayName}
</a>
</Link>
) : (
<span className="text-white">-</span>
)}
</div>
<div className="flex justify-between gap-5">
<span>Claimed on</span>
<span className="text-white">
{giftCode.claimedAt ? dayjs(giftCode.claimedAt).format('L') : '-'}
</span>
</div>
<div className="flex justify-between gap-5">
<span>Redeem code</span>
<span className="text-white">{formatCode(giftCode.code)}</span>
<Transition appear show={open} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={onClose}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black/25" />
</Transition.Child>

<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="z-40 max-h-max w-full max-w-md cursor-default overflow-hidden rounded-2xl bg-foreground p-5 text-left align-middle shadow-xl transition-all">
<Dialog.Title as="h1">
Coupon{' '}
<span className="text-neutral-400">#{giftCode.id}</span>
</Dialog.Title>
<div className="mt-2 flex flex-col gap-2">
<div className="flex justify-between gap-5">
<span>Purchased On</span>
<span className="text-white">
{dayjs(giftCode.purchasedAt).format('L')}
</span>
</div>
<div className="flex justify-between gap-5">
<span>Bought by</span>
<Link legacyBehavior href={`/user/${giftCode.boughtBy.id}`}>
<a className="font-bold text-primary">
{giftCode.boughtBy.displayName}
</a>
</Link>
</div>
<div className="flex justify-between gap-5">
<span>Claimed by</span>
{giftCode.claimedBy ? (
<Link
legacyBehavior
href={`/user/${giftCode.claimedBy.id}`}
>
<a className="font-bold text-primary">
{giftCode.claimedBy.displayName}
</a>
</Link>
) : (
<span className="text-white">-</span>
)}
</div>
<div className="flex justify-between gap-5">
<span>Claimed on</span>
<span className="text-white">
{giftCode.claimedAt
? dayjs(giftCode.claimedAt).format('L')
: '-'}
</span>
</div>
<div className="flex justify-between gap-5">
<span>Redeem code</span>
<span className="text-white">
{formatCode(giftCode.code)}
</span>
</div>
</div>
{!giftCode.claimedBy && (
<div className="pt-4">
<Textarea
name="about"
label="Edit Message"
placeholder="Enter message"
className="!bg-background !p-2"
maxLength={512}
value={message ?? ''}
onChange={(e) => setMessage(e.target.value)}
/>
</div>
)}
{giftCode.claimedBy ? (
<>
<Divider className="my-4 border-neutral-600" />
<Button className=" w-full" onClick={onClose}>
Close
</Button>
</>
) : (
<>
<Button onClick={save} className="mt-3 w-full">
{saving ? 'Saving...' : 'Save'}
</Button>
<Divider className="my-4 border-neutral-600" />
<Button onClick={copyLink} className="w-full">
Copy Link
</Button>
</>
)}
</Dialog.Panel>
</Transition.Child>
</div>
</div>
{!giftCode.claimedBy && (
<div className="pt-4">
<Textarea
name="about"
label="Edit Message"
placeholder="Enter message"
className="!bg-background !p-2"
maxLength={512}
value={message ?? ''}
onChange={(e) => setMessage(e.target.value)}
/>
</div>
)}
{giftCode.claimedBy ? (
<>
<Divider className="my-4 border-neutral-600" />
<Button className=" w-full" onClick={onClose}>
Close
</Button>
</>
) : (
<>
<Button onClick={save} className="mt-3 w-full">
{saving ? 'Saving...' : 'Save'}
</Button>
<Divider className="my-4 border-neutral-600" />
<Button onClick={copyLink} className="w-full">
Copy Link
</Button>
</>
)}
</Dialog.Panel>
</Dialog>
</Dialog>
</Transition>
);
};
43 changes: 43 additions & 0 deletions src/components/Import/DeleteItemConfirmDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { AlertDialog } from '@/components/ui/AlertDialog';
import { MdDeleteOutline } from 'react-icons/md';
import { Button } from '../Button';

type DeleteItemConfirmDialogProps = {
onDeleteImportItem: () => void;
streamCount: number;
};

export const DeleteItemConfirmDialog = ({
onDeleteImportItem,
streamCount,
}: DeleteItemConfirmDialogProps) => {
return (
<AlertDialog>
<AlertDialog.Trigger>
<MdDeleteOutline />
</AlertDialog.Trigger>

<AlertDialog.Portal>
<AlertDialog.Overlay />
<AlertDialog.Content>
<AlertDialog.Title>Are you sure?</AlertDialog.Title>
<AlertDialog.Description>
You are about to delete {streamCount.toLocaleString()} streams. This
action cannot be undone
</AlertDialog.Description>
<div className="mt-6 flex justify-end gap-2">
<AlertDialog.Cancel />
<AlertDialog.Action>
<Button
className="bg-red-500 text-white hover:bg-red-700"
onClick={onDeleteImportItem}
>
Confirm
</Button>
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog>
);
};
Loading