feat: enhance SuccessModal and News context for improved modal management and subscription flow

This commit is contained in:
Ardeman 2025-02-25 05:18:40 +08:00
parent fe9b8bc97a
commit 6b2ba85642
4 changed files with 49 additions and 42 deletions

View File

@ -11,45 +11,34 @@ import { LeftArrow } from '~/components/icons/left-arrow'
import { Button } from '~/components/ui/button' import { Button } from '~/components/ui/button'
import { APP } from '~/data/meta' import { APP } from '~/data/meta'
type ModalProperties = { export type ModalProperties = {
isOpen: boolean
onClose: () => void onClose: () => void
children?: ReactNode children?: ReactNode
type: isOpen?: 'error' | 'warning' | 'resetPassword' | 'register' | 'payment'
| 'success'
| 'error'
| 'warning'
| 'success-reset-password'
| 'success-register'
| 'success-payment'
} }
type DescriptionMap = { type DescriptionMap = {
[key in ModalProperties['type']]: string [key in Exclude<ModalProperties['isOpen'], undefined>]: string
} }
const DESCRIPTIONS: DescriptionMap = { const DESCRIPTIONS: DescriptionMap = {
'success-reset-password': resetPassword: 'Link Reset Password telah dikirimkan ke email anda',
'Link Reset Password telah dikirimkan ke email anda', register: 'Selamat! Pendaftaran anda berhasil!',
'success-register': 'Selamat! pendaftaran anda berhasil!', payment: 'Selamat! Pembayaran anda berhasil!',
'success-payment': 'Selamat! Pembayaran anda berhasil!',
warning: warning:
'Mohon maaf fitur berikut hanya untuk anggota yang sudah tersubscribe', 'Mohon maaf fitur berikut hanya untuk anggota yang sudah tersubscribe',
error: 'Terjadi kesalahan. Silakan coba lagi.', error: 'Terjadi kesalahan. Silakan coba lagi.',
success: '',
} }
export const SuccessModal = ({ export const SuccessModal = ({ isOpen, onClose }: ModalProperties) => {
isOpen,
onClose,
type = 'success-register',
}: ModalProperties) => {
if (!isOpen) return if (!isOpen) return
const message = DESCRIPTIONS[type] || 'Terjadi kesalahan. Silakan coba lagi.' const message =
DESCRIPTIONS[isOpen] || 'Terjadi kesalahan. Silakan coba lagi.'
return ( return (
<Dialog <Dialog
open={isOpen} open={!!isOpen}
onClose={onClose} onClose={onClose}
className="relative z-50" className="relative z-50"
transition transition
@ -84,11 +73,7 @@ export const SuccessModal = ({
<div className="relative"> <div className="relative">
<div className="relative flex flex-col items-center justify-center"> <div className="relative flex flex-col items-center justify-center">
<div className="mb-4 p-4 text-center"> <div className="mb-4 p-4 text-center">
{[ {['resetPassword', 'register', 'payment'].includes(isOpen) && (
'success-reset-password',
'success-register',
'success-payment',
].includes(type) && (
<div className="justify-center"> <div className="justify-center">
<img <img
src={'/images/back-to-home.svg'} src={'/images/back-to-home.svg'}
@ -104,7 +89,7 @@ export const SuccessModal = ({
</Button> </Button>
</div> </div>
)} )}
{type === 'warning' && ( {isOpen === 'warning' && (
<div className="justify-center"> <div className="justify-center">
<img <img
src={'/images/warning.svg'} src={'/images/warning.svg'}

View File

@ -7,15 +7,21 @@ import {
type SetStateAction, type SetStateAction,
} from 'react' } from 'react'
type NewsContextProperties = { import type { ModalProperties } from '~/components/popup/success-modal'
export type NewsContextProperties = {
isLoginOpen: boolean isLoginOpen: boolean
setIsLoginOpen: Dispatch<SetStateAction<boolean>> setIsLoginOpen: Dispatch<SetStateAction<boolean>>
isRegisterOpen: boolean isRegisterOpen: boolean
setIsRegisterOpen: Dispatch<SetStateAction<boolean>> setIsRegisterOpen: Dispatch<SetStateAction<boolean>>
isForgetOpen: boolean isForgetOpen: boolean
setForgetOpen: Dispatch<SetStateAction<boolean>> setForgetOpen: Dispatch<SetStateAction<boolean>>
isSuccessModalOpen: boolean isSuccessModalOpen?: ModalProperties['isOpen']
setIsSuccessModalOpen: Dispatch<SetStateAction<boolean>> setIsSuccessModalOpen?: Dispatch<
SetStateAction<ModalProperties['isOpen'] | undefined>
>
isInitSubscribeOpen: boolean
setIsInitSubscribeOpen: Dispatch<SetStateAction<boolean>>
} }
const NewsContext = createContext<NewsContextProperties | undefined>(undefined) const NewsContext = createContext<NewsContextProperties | undefined>(undefined)
@ -24,7 +30,9 @@ export const NewsProvider = ({ children }: PropsWithChildren) => {
const [isLoginOpen, setIsLoginOpen] = useState(false) const [isLoginOpen, setIsLoginOpen] = useState(false)
const [isRegisterOpen, setIsRegisterOpen] = useState(false) const [isRegisterOpen, setIsRegisterOpen] = useState(false)
const [isForgetOpen, setForgetOpen] = useState(false) const [isForgetOpen, setForgetOpen] = useState(false)
const [isSuccessModalOpen, setIsSuccessModalOpen] = useState(false) const [isSuccessModalOpen, setIsSuccessModalOpen] =
useState<ModalProperties['isOpen']>()
const [isInitSubscribeOpen, setIsInitSubscribeOpen] = useState(false)
return ( return (
<NewsContext.Provider <NewsContext.Provider
@ -37,6 +45,8 @@ export const NewsProvider = ({ children }: PropsWithChildren) => {
setForgetOpen, setForgetOpen,
isSuccessModalOpen, isSuccessModalOpen,
setIsSuccessModalOpen, setIsSuccessModalOpen,
isInitSubscribeOpen,
setIsInitSubscribeOpen,
}} }}
> >
{children} {children}

View File

@ -10,6 +10,7 @@ import { FormRegister } from '~/layouts/news/form-register'
import { FooterLinks } from './footer-links' import { FooterLinks } from './footer-links'
import { FooterNewsletter } from './footer-newsletter' import { FooterNewsletter } from './footer-newsletter'
import FormSubscription from './form-subscription'
import { HeaderMenu } from './header-menu' import { HeaderMenu } from './header-menu'
import { HeaderTop } from './header-top' import { HeaderTop } from './header-top'
@ -24,6 +25,8 @@ export const NewsDefaultLayout = (properties: PropsWithChildren) => {
setForgetOpen, setForgetOpen,
isSuccessModalOpen, isSuccessModalOpen,
setIsSuccessModalOpen, setIsSuccessModalOpen,
isInitSubscribeOpen,
setIsInitSubscribeOpen,
} = useNewsContext() } = useNewsContext()
return ( return (
<main className="relative min-h-dvh bg-[#ECECEC]"> <main className="relative min-h-dvh bg-[#ECECEC]">
@ -50,7 +53,7 @@ export const NewsDefaultLayout = (properties: PropsWithChildren) => {
setIsRegisterOpen={setIsRegisterOpen} setIsRegisterOpen={setIsRegisterOpen}
setIsLoginOpen={setIsLoginOpen} setIsLoginOpen={setIsLoginOpen}
setIsForgetOpen={setForgetOpen} setIsForgetOpen={setForgetOpen}
setIsSuccessModalOpen={setIsSuccessModalOpen} setIsInitSubscribeOpen={setIsInitSubscribeOpen}
/> />
</PopupModal> </PopupModal>
@ -70,12 +73,21 @@ export const NewsDefaultLayout = (properties: PropsWithChildren) => {
<FormForgotPassword /> <FormForgotPassword />
</PopupModal> </PopupModal>
<PopupModal
isOpen={isInitSubscribeOpen}
onClose={() => setIsInitSubscribeOpen(false)}
description="Selamat Datang, silakan Pilih Subscription Anda untuk melanjutkan!"
>
<FormSubscription />
</PopupModal>
<SuccessModal <SuccessModal
isOpen={isSuccessModalOpen} isOpen={isSuccessModalOpen}
onClose={() => { onClose={() => {
setIsSuccessModalOpen(false) if (setIsSuccessModalOpen) {
setIsSuccessModalOpen(undefined)
}
}} }}
type="warning"
/> />
</main> </main>
) )

View File

@ -1,11 +1,11 @@
// import { EyeIcon, EyeOffIcon } from 'lucide-react' // import { EyeIcon, EyeOffIcon } from 'lucide-react'
import { zodResolver } from '@hookform/resolvers/zod' import { zodResolver } from '@hookform/resolvers/zod'
import { type Dispatch, type SetStateAction } from 'react'
import { FormProvider, useForm } from 'react-hook-form' import { FormProvider, useForm } from 'react-hook-form'
import { z } from 'zod' import { z } from 'zod'
import { Button } from '~/components/ui/button' import { Button } from '~/components/ui/button'
import { Input } from '~/components/ui/input' import { Input } from '~/components/ui/input'
import type { NewsContextProperties } from '~/contexts/news'
const loginSchema = z.object({ const loginSchema = z.object({
email: z.string().email('Email tidak valid'), email: z.string().email('Email tidak valid'),
@ -15,10 +15,10 @@ const loginSchema = z.object({
type TLoginSchema = z.infer<typeof loginSchema> type TLoginSchema = z.infer<typeof loginSchema>
type TProperties = { type TProperties = {
setIsRegisterOpen: Dispatch<SetStateAction<boolean>> setIsRegisterOpen: NewsContextProperties['setIsRegisterOpen']
setIsLoginOpen: Dispatch<SetStateAction<boolean>> setIsLoginOpen: NewsContextProperties['setIsLoginOpen']
setIsForgetOpen: Dispatch<SetStateAction<boolean>> setIsForgetOpen: NewsContextProperties['setForgetOpen']
setIsSuccessModalOpen: Dispatch<SetStateAction<boolean>> setIsInitSubscribeOpen: NewsContextProperties['setIsInitSubscribeOpen']
} }
export const FormLogin = (properties: TProperties) => { export const FormLogin = (properties: TProperties) => {
@ -26,7 +26,7 @@ export const FormLogin = (properties: TProperties) => {
setIsRegisterOpen, setIsRegisterOpen,
setIsLoginOpen, setIsLoginOpen,
setIsForgetOpen, setIsForgetOpen,
setIsSuccessModalOpen, setIsInitSubscribeOpen,
} = properties } = properties
const formMethods = useForm<TLoginSchema>({ const formMethods = useForm<TLoginSchema>({
@ -37,7 +37,7 @@ export const FormLogin = (properties: TProperties) => {
const onSubmit = handleSubmit((data) => { const onSubmit = handleSubmit((data) => {
console.log('data', data) // eslint-disable-line no-console console.log('data', data) // eslint-disable-line no-console
setIsSuccessModalOpen(true) setIsInitSubscribeOpen(true)
setIsLoginOpen(false) setIsLoginOpen(false)
}) })