From b146e4c3a89e548d18d5ca6581620889ea93a108 Mon Sep 17 00:00:00 2001 From: Aarnav Tale Date: Sat, 30 Mar 2024 18:39:46 -0400 Subject: [PATCH] feat: add confirmation modal --- app/components/Modal.tsx | 128 ++++++++++++++++++++++++++ app/routes/_data.dns._index/magic.tsx | 63 ++++--------- 2 files changed, 146 insertions(+), 45 deletions(-) create mode 100644 app/components/Modal.tsx diff --git a/app/components/Modal.tsx b/app/components/Modal.tsx new file mode 100644 index 0000000..d17e5f0 --- /dev/null +++ b/app/components/Modal.tsx @@ -0,0 +1,128 @@ +import { Dialog, Transition } from '@headlessui/react' +import { XMarkIcon } from '@heroicons/react/24/outline' +import clsx from 'clsx' +import { Fragment, type SetStateAction, useState } from 'react' + +import Button from './Button' + +type HookParameters = { + title: string; + description?: string; + buttonText?: string; + variant?: 'danger' | 'confirm'; + + // Optional because the button submits + onConfirm?: () => void | Promise; +} + +type Properties = { + readonly isOpen: boolean; + readonly setIsOpen: (value: SetStateAction) => void; + readonly parameters: HookParameters; +} + +export default function useModal(properties: HookParameters) { + const [isOpen, setIsOpen] = useState(false) + return { + Modal: ( + + ), + + open: () => { + setIsOpen(true) + }, + + close: () => { + setIsOpen(false) + } + } +} + +function Modal({ parameters, isOpen, setIsOpen }: Properties) { + return ( + + { + setIsOpen(false) + }} + > + + + + ) +} diff --git a/app/routes/_data.dns._index/magic.tsx b/app/routes/_data.dns._index/magic.tsx index 1f9f6f6..d4eabac 100644 --- a/app/routes/_data.dns._index/magic.tsx +++ b/app/routes/_data.dns._index/magic.tsx @@ -1,9 +1,7 @@ -import { Dialog } from '@headlessui/react' import { useFetcher } from '@remix-run/react' -import clsx from 'clsx' -import { useState } from 'react' import Button from '~/components/Button' +import useModal from '~/components/Modal' import Spinner from '~/components/Spinner' type Properties = { @@ -13,8 +11,22 @@ type Properties = { } export default function Modal({ isEnabled, disabled }: Properties) { - const [isOpen, setIsOpen] = useState(false) const fetcher = useFetcher() + const { Modal, open } = useModal({ + title: `${isEnabled ? 'Disable' : 'Enable'} Magic DNS`, + variant: isEnabled ? 'danger' : 'confirm', + buttonText: `${isEnabled ? 'Disable' : 'Enable'} Magic DNS`, + description: 'Devices will no longer be accessible via your tailnet domain. The search domain will also be disabled.', + onConfirm: () => { + fetcher.submit({ + // eslint-disable-next-line @typescript-eslint/naming-convention + 'dns_config.magic_dns': !isEnabled + }, { + method: 'PATCH', + encType: 'application/json' + }) + } + }) return ( <> @@ -23,7 +35,7 @@ export default function Modal({ isEnabled, disabled }: Properties) { className='w-fit text-sm' disabled={disabled} onClick={() => { - setIsOpen(true) + open() }} > {fetcher.state === 'idle' ? undefined : ( @@ -31,46 +43,7 @@ export default function Modal({ isEnabled, disabled }: Properties) { )} {isEnabled ? 'Disable' : 'Enable'} Magic DNS - { - setIsOpen(false) - }} - > - + {Modal} ) }