feat: restructure routing and layouts for admin and news sections, update cookie paths

This commit is contained in:
Ardeman 2025-02-28 11:49:51 +08:00
parent 4b8fc0721e
commit 23e3493a3f
30 changed files with 55 additions and 64 deletions

View File

@ -136,7 +136,7 @@ export const Carousel = (properties: TNews) => {
}, },
to: '', to: '',
} }
: { as: Link, to: `/news/detail/${slug}` })} : { as: Link, to: `/detail/${slug}` })}
className={twMerge('', type === 'hero' ? '' : 'mb-5')} className={twMerge('', type === 'hero' ? '' : 'mb-5')}
> >
View More View More

View File

@ -14,7 +14,7 @@ type TMetaTitleConfig = {
export const META_TITLE_CONFIG: TMetaTitleConfig = [ export const META_TITLE_CONFIG: TMetaTitleConfig = [
{ {
path: '/news', path: '/',
title: 'Home', title: 'Home',
}, },
{ {

View File

@ -11,7 +11,7 @@ export const Navbar = () => {
return ( return (
<div className="flex h-20 items-center justify-between border-b border-[#ECECEC] bg-white px-10 py-5"> <div className="flex h-20 items-center justify-between border-b border-[#ECECEC] bg-white px-10 py-5">
<Link <Link
to="/news" to="/"
className="h-full" className="h-full"
> >
<img <img

View File

@ -9,7 +9,7 @@ export const FooterNewsletter = () => {
<div className="col-span-2 hidden gap-y-6 sm:grid"> <div className="col-span-2 hidden gap-y-6 sm:grid">
<div className="h-[75px] bg-white p-3"> <div className="h-[75px] bg-white p-3">
<Link <Link
to="/news" to="/"
className="h-full" className="h-full"
> >
<img <img
@ -44,7 +44,7 @@ export const FooterNewsletter = () => {
<div className="block sm:hidden"> <div className="block sm:hidden">
<div className="h-[60px] bg-white p-3"> <div className="h-[60px] bg-white p-3">
<Link <Link
to="/news" to="/"
className="h-full" className="h-full"
> >
<img <img

View File

@ -67,7 +67,7 @@ export const FormLogin = (properties: TProperties) => {
method="post" method="post"
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="space-y-4" className="space-y-4"
action="/actions/news/login" action="/actions/login"
> >
<Input <Input
id="email" id="email"

View File

@ -3,17 +3,17 @@ import { Link, useFetcher, useRouteLoaderData } from 'react-router'
import { Button } from '~/components/ui/button' import { Button } from '~/components/ui/button'
import { APP } from '~/configs/meta' import { APP } from '~/configs/meta'
import { useNewsContext } from '~/contexts/news' import { useNewsContext } from '~/contexts/news'
import type { loader } from '~/routes/_layout.news' import type { loader } from '~/routes/_layout'
export const HeaderTop = () => { export const HeaderTop = () => {
const { setIsLoginOpen } = useNewsContext() const { setIsLoginOpen } = useNewsContext()
const loaderData = useRouteLoaderData<typeof loader>('routes/_layout.news') const loaderData = useRouteLoaderData<typeof loader>('routes/_layout')
const fetcher = useFetcher() const fetcher = useFetcher()
return ( 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]"> <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]">
<Link <Link
to="/news" to="/"
className="mt-2 h-full py-2" className="mt-2 h-full py-2"
> >
<img <img
@ -32,7 +32,7 @@ export const HeaderTop = () => {
{loaderData?.userToken ? ( {loaderData?.userToken ? (
<fetcher.Form <fetcher.Form
method="POST" method="POST"
action="/actions/news/logout" action="/actions/logout"
> >
<Button <Button
variant="newsSecondary" variant="newsSecondary"

View File

@ -21,31 +21,31 @@ type TFooterMenu = {
export const MENU: TMenu[] = [ export const MENU: TMenu[] = [
{ {
title: 'Spotlight', title: 'Spotlight',
url: '/news/category/spotlight', url: '/category/spotlight',
}, },
{ {
title: 'Berita', title: 'Berita',
url: '/news/category/berita', url: '/category/berita',
}, },
{ {
title: 'Kasus', title: 'Kasus',
url: '/news/category/kasus', url: '/category/kasus',
}, },
{ {
title: 'Kajian', title: 'Kajian',
url: '/news/category/kajian', url: '/category/kajian',
}, },
{ {
title: 'Lifestyle', title: 'Lifestyle',
url: '/news/category/lifestyle', url: '/category/lifestyle',
}, },
{ {
title: 'Event', title: 'Event',
url: '/news/category/event', url: '/category/event',
}, },
{ {
title: 'Travel', title: 'Travel',
url: '/news/category/travel', url: '/category/travel',
}, },
] ]
@ -55,23 +55,23 @@ export const FOOTER_MENU: TFooterMenu[] = [
items: [ items: [
{ {
title: 'Popular', title: 'Popular',
url: '/news/list?sort=popular', url: '/list?sort=popular',
}, },
{ {
title: 'Trending', title: 'Trending',
url: '/news/list?sort=trending', url: '/list?sort=trending',
}, },
{ {
title: 'Contact', title: 'Contact',
url: '/news/contact', url: '/contact',
}, },
{ {
title: 'Support/Help', title: 'Support/Help',
url: '/news/support', url: '/support',
}, },
{ {
title: 'Rquest Topic', title: 'Rquest Topic',
url: '/news/request-topic', url: '/request-topic',
}, },
], ],
}, },
@ -80,23 +80,23 @@ export const FOOTER_MENU: TFooterMenu[] = [
items: [ items: [
{ {
title: 'FAQs', title: 'FAQs',
url: '/news/faq', url: '/faq',
}, },
{ {
title: 'Terms and Condition', title: 'Terms and Condition',
url: '/news/terms-condition', url: '/terms-condition',
}, },
{ {
title: 'Support', title: 'Support',
url: '/news/support', url: '/support',
}, },
{ {
title: 'Link Nine', title: 'Link Nine',
url: '/news', url: '/',
}, },
{ {
title: 'Link Ten', title: 'Link Ten',
url: '/news', url: '/',
}, },
], ],
}, },
@ -130,14 +130,14 @@ export const FOOTER_MENU: TFooterMenu[] = [
export const COPYRIGHT_MENU: TMenu[] = [ export const COPYRIGHT_MENU: TMenu[] = [
{ {
title: 'Privacy Policy', title: 'Privacy Policy',
url: '/news/privacy-policy', url: '/privacy-policy',
}, },
{ {
title: 'Terms of Service', title: 'Terms of Service',
url: '/news/terms-of-service', url: '/terms-of-service',
}, },
{ {
title: 'Cookies Settings', title: 'Cookies Settings',
url: '/news/cookies-settings', url: '/cookies-settings',
}, },
] ]

View File

@ -7,7 +7,7 @@ export const userTokenCookieConfig = createCookie(USER_COOKIES.token, {
sameSite: 'lax', sameSite: 'lax',
secure: process.env.NODE_ENV === 'production', secure: process.env.NODE_ENV === 'production',
secrets: [process.env.VITE_SALT_KEY || 'default-secret'], secrets: [process.env.VITE_SALT_KEY || 'default-secret'],
path: '/news', path: '/',
}) })
export const adminTokenCookieConfig = createCookie(ADMIN_COOKIES.token, { export const adminTokenCookieConfig = createCookie(ADMIN_COOKIES.token, {

View File

@ -4,7 +4,7 @@ export const setUserLogoutHeaders = () => {
const responseHeaders = new Headers() const responseHeaders = new Headers()
responseHeaders.append( responseHeaders.append(
'Set-Cookie', 'Set-Cookie',
`${USER_COOKIES.token}=; Path=/news; HttpOnly; SameSite=Strict; Max-Age=0`, `${USER_COOKIES.token}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0`,
) )
return responseHeaders return responseHeaders

View File

@ -6,7 +6,6 @@ import {
Outlet, Outlet,
Scripts, Scripts,
ScrollRestoration, ScrollRestoration,
type ShouldRevalidateFunctionArgs,
} from 'react-router' } from 'react-router'
import type { Route } from './+types/root' import type { Route } from './+types/root'
@ -37,16 +36,6 @@ export const meta = ({ location }: Route.MetaArgs) => {
] ]
} }
export const shouldRevalidate = ({
actionResult,
defaultShouldRevalidate,
}: ShouldRevalidateFunctionArgs) => {
if (actionResult?.success) {
return true
}
return defaultShouldRevalidate
}
export const Layout = ({ children }: { children: ReactNode }) => { export const Layout = ({ children }: { children: ReactNode }) => {
return ( return (
<html lang="en"> <html lang="en">

View File

@ -1,5 +1,7 @@
import { redirect } from 'react-router' import { NewsPage } from '~/pages/news'
export async function loader() { const NewsIndexLayout = () => {
return redirect('/news') return <NewsPage />
} }
export default NewsIndexLayout

View File

@ -1,7 +0,0 @@
import { NewsPage } from '~/pages/news'
const NewsIndexLayout = () => {
return <NewsPage />
}
export default NewsIndexLayout

View File

@ -4,7 +4,7 @@ import { NewsProvider } from '~/contexts/news'
import { NewsDefaultLayout } from '~/layouts/news/default' import { NewsDefaultLayout } from '~/layouts/news/default'
import { handleCookie } from '~/libs/cookies' import { handleCookie } from '~/libs/cookies'
import type { Route } from './+types/_layout.news' import type { Route } from './+types/_layout'
export const loader = async ({ request }: Route.LoaderArgs) => { export const loader = async ({ request }: Route.LoaderArgs) => {
const { userToken } = await handleCookie(request) const { userToken } = await handleCookie(request)

View File

@ -7,7 +7,7 @@ import { newsLoginRequest } from '~/apis/news/login'
import { loginSchema, type TLoginSchema } from '~/layouts/news/form-login' import { loginSchema, type TLoginSchema } from '~/layouts/news/form-login'
import { generateTokenCookie } from '~/utils/token' import { generateTokenCookie } from '~/utils/token'
import type { Route } from './+types/actions.news.login' import type { Route } from './+types/actions.login'
export const action = async ({ request }: Route.ActionArgs) => { export const action = async ({ request }: Route.ActionArgs) => {
try { try {
@ -46,14 +46,22 @@ export const action = async ({ request }: Route.ActionArgs) => {
) )
} catch (error) { } catch (error) {
if (error instanceof XiorError) { if (error instanceof XiorError) {
return data({ return data(
success: false, {
message: error?.response?.data?.error?.message || error.message, success: false,
}) message: error?.response?.data?.error?.message || error.message,
},
{
status: error?.response?.status || 500,
},
)
} }
return data({ return data(
success: false, {
message: 'Internal server error', success: false,
}) message: 'Internal server error',
},
{ status: 500 },
)
} }
} }

View File

@ -7,6 +7,5 @@ export default defineConfig({
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()], plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
build: { build: {
cssMinify: true, cssMinify: true,
ssr: false,
}, },
}) })