feat: restructure routing and layouts for admin and news sections, update cookie paths
This commit is contained in:
parent
4b8fc0721e
commit
23e3493a3f
@ -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
|
||||||
|
|||||||
@ -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',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@ -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, {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
11
app/root.tsx
11
app/root.tsx
@ -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">
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
import { NewsPage } from '~/pages/news'
|
|
||||||
|
|
||||||
const NewsIndexLayout = () => {
|
|
||||||
return <NewsPage />
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NewsIndexLayout
|
|
||||||
@ -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)
|
||||||
@ -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 },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,6 +7,5 @@ export default defineConfig({
|
|||||||
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
|
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
|
||||||
build: {
|
build: {
|
||||||
cssMinify: true,
|
cssMinify: true,
|
||||||
ssr: false,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user