feat: implement news layout components and context for improved structure and functionality

This commit is contained in:
Ardeman 2025-02-22 17:34:25 +08:00
parent d9c36c5ff0
commit 77c9843267
11 changed files with 110 additions and 49 deletions

36
app/contexts/news.tsx Normal file
View File

@ -0,0 +1,36 @@
import React, {
createContext,
useState,
useContext,
type PropsWithChildren,
} from 'react'
type NewsContextProperties = {
isLoginOpen: boolean
setIsLoginOpen: React.Dispatch<React.SetStateAction<boolean>>
isRegisterOpen: boolean
setIsRegisterOpen: React.Dispatch<React.SetStateAction<boolean>>
}
const NewsContext = createContext<NewsContextProperties | undefined>(undefined)
export const NewsProvider = ({ children }: PropsWithChildren) => {
const [isLoginOpen, setIsLoginOpen] = useState(false)
const [isRegisterOpen, setIsRegisterOpen] = useState(false)
return (
<NewsContext.Provider
value={{ isLoginOpen, setIsLoginOpen, isRegisterOpen, setIsRegisterOpen }}
>
{children}
</NewsContext.Provider>
)
}
export const useNewsContext = (): NewsContextProperties => {
const context = useContext(NewsContext)
if (!context) {
throw new Error('useNewsContext must be used within a NewsProvider')
}
return context
}

View File

@ -0,0 +1,54 @@
import { type PropsWithChildren } from 'react'
import { PopupModal } from '~/components/popup/modal'
import Banner from '~/components/ui/banner'
import { FormLogin } from '~/components/ui/form-login'
import { FormRegister } from '~/components/ui/form-register'
import { useNewsContext } from '~/contexts/news'
import { FooterLinks } from './footer-links'
import { FooterNewsletter } from './footer-newsletter'
import { HeaderMenu } from './header-menu'
import { HeaderTop } from './header-top'
export const NewsDefaultLayout = (properties: PropsWithChildren) => {
const { children } = properties
const { isLoginOpen, setIsLoginOpen, isRegisterOpen, setIsRegisterOpen } =
useNewsContext()
return (
<main className="relative min-h-dvh bg-[#ECECEC]">
<header>
<HeaderTop />
<HeaderMenu />
</header>
<div className="grid sm:mx-[50px] sm:my-[25px] sm:gap-y-[25px]">
<Banner />
{children}
</div>
<footer className="grid w-full grid-cols-1 gap-6 bg-[#2E2F7C] px-5 py-20 text-white sm:grid-cols-5 sm:gap-16 sm:px-16">
<FooterNewsletter />
<FooterLinks />
</footer>
<PopupModal
isOpen={isLoginOpen}
onClose={() => setIsLoginOpen(false)}
description="Selamat Datang, silakan daftarkan akun Anda untuk melanjutkan!"
>
<FormLogin
setIsRegisterOpen={setIsRegisterOpen}
setIsLoginOpen={setIsLoginOpen}
/>
</PopupModal>
<PopupModal
isOpen={isRegisterOpen}
onClose={() => setIsRegisterOpen(false)}
description="Selamat Datang, silakan daftarkan akun Anda untuk melanjutkan!"
>
<FormRegister />
</PopupModal>
</main>
)
}

View File

@ -3,12 +3,14 @@ import { Link } from 'react-router'
import { CloseIcon } from '~/components/icons/close'
import { MenuIcon } from '~/components/icons/menu'
import { HeaderSearch } from '~/layouts/header-search'
import { useNewsContext } from '~/contexts/news'
import { HeaderSearch } from '~/layouts/news/header-search'
import { MENU } from './menu'
export default function HeaderMenuMobile() {
const [isMenuOpen, setIsMenuOpen] = useState(false)
const { setIsLoginOpen } = useNewsContext()
const handleToggleMenu = (): void => {
setIsMenuOpen(!isMenuOpen)
@ -53,7 +55,13 @@ export default function HeaderMenuMobile() {
</li>
))}
<button className="w-full bg-white px-[35px] py-3 text-center text-[#2E2F7C] sm:hidden">
<button
className="w-full bg-white px-[35px] py-3 text-center text-[#2E2F7C] sm:hidden"
onClick={() => {
setIsLoginOpen(true)
setIsMenuOpen(false)
}}
>
Akun
</button>
</ul>

View File

@ -1,6 +1,6 @@
import { Link } from 'react-router'
import HeaderMenuMobile from '~/layouts/header-menu-mobile'
import HeaderMenuMobile from '~/layouts/news/header-menu-mobile'
import { HeaderSearch } from './header-search'
import { MENU } from './menu'

View File

@ -1,16 +1,11 @@
import { useState } from 'react'
import { Link } from 'react-router'
import { PopupModal } from '~/components/popup/modal'
import { Button } from '~/components/ui/button'
import { FormLogin } from '~/components/ui/form-login'
import { FormRegister } from '~/components/ui/form-register'
import { useNewsContext } from '~/contexts/news'
import { APP } from '~/data/meta'
export const HeaderTop = () => {
const [isLoginOpen, setIsLoginOpen] = useState(false)
const [isRegisterOpen, setIsRegisterOpen] = useState(false)
const { setIsLoginOpen } = useNewsContext()
return (
<>
<div className="flex h-[60px] items-center justify-between bg-white px-5 align-middle sm:h-[100px] sm:gap-[15px] sm:px-[50px] sm:py-[20px]">
@ -47,25 +42,6 @@ export const HeaderTop = () => {
</div>
</div>
</div>
<PopupModal
isOpen={isLoginOpen}
onClose={() => setIsLoginOpen(false)}
description="Selamat Datang, silakan daftarkan akun Anda untuk melanjutkan!"
>
<FormLogin
setIsRegisterOpen={setIsRegisterOpen}
setIsLoginOpen={setIsLoginOpen}
/>
</PopupModal>
<PopupModal
isOpen={isRegisterOpen}
onClose={() => setIsRegisterOpen(false)}
description="Selamat Datang, silakan daftarkan akun Anda untuk melanjutkan!"
>
<FormRegister />
</PopupModal>
</>
)
}

View File

@ -1,28 +1,15 @@
import { Outlet } from 'react-router'
import Banner from '~/components/ui/banner'
import { FooterLinks } from '~/layouts/footer-links'
import { FooterNewsletter } from '~/layouts/footer-newsletter'
import { HeaderMenu } from '~/layouts/header-menu'
import { HeaderTop } from '~/layouts/header-top'
import { NewsProvider } from '~/contexts/news'
import { NewsDefaultLayout } from '~/layouts/news/default'
const NewsLayout = () => {
return (
<main className="relative min-h-dvh bg-[#ECECEC]">
<header>
<HeaderTop />
<HeaderMenu />
</header>
<div className="grid sm:mx-[50px] sm:my-[25px] sm:gap-y-[25px]">
<Banner />
<NewsProvider>
<NewsDefaultLayout>
<Outlet />
</div>
<footer className="grid w-full grid-cols-1 gap-6 bg-[#2E2F7C] px-5 py-20 text-white sm:grid-cols-5 sm:gap-16 sm:px-16">
<FooterNewsletter />
<FooterLinks />
</footer>
</main>
</NewsDefaultLayout>
</NewsProvider>
)
}

View File

@ -82,7 +82,7 @@ export default tseslint.config(
'~/factories/*/*',
'~/pages/*/*',
'~/hooks/*/*',
'~/layouts/*/*/*',
'~/layouts/*/*/*/*',
'~/schemas/*/*/*',
'~/types/*/*',
'~/utils/*/**',