Merge pull request #1 from ardeman/feature/slicing
style: slicing home page, adjust for mobile view
This commit is contained in:
commit
cb5255111c
25
app/components/ui/banner.tsx
Normal file
25
app/components/ui/banner.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import { APP } from '~/data/meta'
|
||||
|
||||
export const Banner = () => {
|
||||
return (
|
||||
<div className="min-h-[65px] bg-amber-400 sm:mx-10">
|
||||
<div className="relative">
|
||||
<Link
|
||||
to="/#"
|
||||
className="mt-2 h-full py-2"
|
||||
>
|
||||
<img
|
||||
src={'https://placehold.co/1200x70.png'}
|
||||
alt={APP.title}
|
||||
className="h-[70px] w-auto sm:h-full"
|
||||
/>
|
||||
</Link>
|
||||
<p className="absolute top-2 mx-10">
|
||||
Lorem ipsum dolor sit, amet consectetur
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,10 +1,5 @@
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
import type {
|
||||
ButtonHTMLAttributes,
|
||||
HTMLAttributes,
|
||||
MouseEventHandler,
|
||||
ReactNode,
|
||||
} from 'react'
|
||||
import type { ReactNode, ElementType, ComponentPropsWithoutRef } from 'react'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
const buttonVariants = cva(
|
||||
@ -16,21 +11,13 @@ const buttonVariants = cva(
|
||||
newsPrimaryOutline: 'border-[3px] border-white text-white text-lg',
|
||||
newsSecondary: 'border-[3px] border-[#2E2F7C] text-[#2E2F7C] text-lg',
|
||||
icon: '',
|
||||
// destructive:
|
||||
// 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||
// outline:
|
||||
// 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
// secondary:
|
||||
// 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
||||
// ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
// link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-[50px] w-[150px]',
|
||||
block: 'h-[50px] w-full',
|
||||
icon: 'h-9 w-9',
|
||||
// sm: 'h-8 rounded-md px-3 text-xs',
|
||||
// lg: 'h-10 rounded-md px-8',
|
||||
sm: 'h-8 rounded-md px-3 text-xs',
|
||||
lg: 'h-10 rounded-md px-8',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@ -40,34 +27,38 @@ const buttonVariants = cva(
|
||||
},
|
||||
)
|
||||
|
||||
type TProperties = {
|
||||
type?: ButtonHTMLAttributes<HTMLButtonElement>['type']
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>
|
||||
className?: HTMLAttributes<HTMLButtonElement>['className']
|
||||
disabled?: boolean
|
||||
type ButtonBaseProperties = {
|
||||
children: ReactNode
|
||||
variant?: VariantProps<typeof buttonVariants>['variant']
|
||||
size?: VariantProps<typeof buttonVariants>['size']
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const Button = (properties: TProperties) => {
|
||||
const {
|
||||
type = 'button',
|
||||
onClick,
|
||||
className,
|
||||
disabled,
|
||||
type PolymorphicReference<C extends ElementType> =
|
||||
ComponentPropsWithoutRef<C>['ref']
|
||||
|
||||
type ButtonProperties<C extends ElementType> = ButtonBaseProperties & {
|
||||
as?: C
|
||||
ref?: PolymorphicReference<C>
|
||||
} & Omit<ComponentPropsWithoutRef<C>, keyof ButtonBaseProperties>
|
||||
|
||||
export const Button = <C extends ElementType = 'button'>({
|
||||
as,
|
||||
children,
|
||||
variant,
|
||||
size,
|
||||
} = properties
|
||||
className,
|
||||
...properties
|
||||
}: ButtonProperties<C>) => {
|
||||
const Component = as || 'button'
|
||||
const classes = twMerge(buttonVariants({ variant, size, className }))
|
||||
|
||||
return (
|
||||
<button
|
||||
type={type}
|
||||
onClick={onClick}
|
||||
className={twMerge(buttonVariants({ variant, size, className }))}
|
||||
disabled={disabled}
|
||||
<Component
|
||||
className={classes}
|
||||
{...properties}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
</Component>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { Link } from 'react-router'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
import { CarouselNextIcon } from '~/components/icons/carousel-next'
|
||||
@ -9,37 +10,44 @@ import { Button } from './button'
|
||||
export const Carousel = (properties: TNews) => {
|
||||
const { title, description, items, type } = properties
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-[30px] flex items-center justify-between border-b border-black pb-[30px]">
|
||||
<div className="">
|
||||
<div className="mt-3 mb-3 flex items-center justify-between border-b border-black pb-3 sm:mb-[30px] sm:pb-[30px]">
|
||||
<div className="grid">
|
||||
<h2 className="text-4xl font-extrabold text-[#2E2F7C]">{title}</h2>
|
||||
<p className="text-2xl font-light text-[#777777] italic">
|
||||
<h2 className="text-2xl font-extrabold text-[#2E2F7C] sm:text-4xl">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-xl font-light text-[#777777] italic sm:text-2xl">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2.5">
|
||||
<CarouselPreviousIcon
|
||||
color="#DCDCDC"
|
||||
className="cursor-pointer"
|
||||
width={45}
|
||||
height={45}
|
||||
/>
|
||||
<CarouselNextIcon
|
||||
color="#2E2F7C"
|
||||
className="cursor-pointer"
|
||||
width={45}
|
||||
height={45}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={twMerge(
|
||||
'grid gap-x-8',
|
||||
type === 'hero' ? 'grid-cols-1' : 'grid-cols-3',
|
||||
'grid sm:grid sm:gap-x-8',
|
||||
type === 'hero' ? 'grid-cols-1' : 'sm:grid-cols-3',
|
||||
)}
|
||||
>
|
||||
{items.map(({ image, title, content }, index) => (
|
||||
{items.map(({ featured, title, content, tag, slug }, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={twMerge(
|
||||
'grid gap-x-8',
|
||||
type === 'hero' ? 'grid-cols-3' : '',
|
||||
'grid sm:gap-x-8',
|
||||
type === 'hero' ? 'grid-cols-1 sm:grid-cols-3' : '',
|
||||
)}
|
||||
>
|
||||
<img
|
||||
@ -47,9 +55,9 @@ export const Carousel = (properties: TNews) => {
|
||||
'w-full object-cover',
|
||||
type === 'hero'
|
||||
? 'col-span-2 aspect-[174/100]'
|
||||
: 'aspect-[5/4]',
|
||||
: 'aspect-[5/4] rounded-md',
|
||||
)}
|
||||
src={image}
|
||||
src={featured}
|
||||
alt={title}
|
||||
/>
|
||||
<div
|
||||
@ -58,18 +66,36 @@ export const Carousel = (properties: TNews) => {
|
||||
type === 'hero' ? 'gap-7' : 'gap-4',
|
||||
)}
|
||||
>
|
||||
<div className={`${type === 'hero' ? 'hidden' : ''} `}>
|
||||
{tag?.map((item) => (
|
||||
<span className="my-3 mr-2 inline-block rounded bg-gray-200 px-3 py-1">
|
||||
{item}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3
|
||||
className={twMerge(
|
||||
'font-bold',
|
||||
type === 'hero' ? 'text-4xl' : 'text-2xl',
|
||||
'mt-2 w-full font-bold lg:mt-0',
|
||||
type === 'hero'
|
||||
? 'text-2xl sm:text-4xl'
|
||||
: 'text-xl sm:text-2xl',
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-xl text-[#777777]">{content}</p>
|
||||
<p className="text-md mt-5 text-[#777777] sm:text-xl">
|
||||
{content}
|
||||
</p>
|
||||
</div>
|
||||
<Button size="block">View More</Button>
|
||||
<Button
|
||||
size="block"
|
||||
as={Link}
|
||||
to={slug}
|
||||
>
|
||||
View More
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
44
app/components/ui/newsletter.tsx
Normal file
44
app/components/ui/newsletter.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { Button } from '~/components/ui/button'
|
||||
import { APP } from '~/data/meta'
|
||||
|
||||
export const Newsletter = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="relative col-span-2 my-5 grid max-h-[400px] gap-y-6 bg-[#2E2F7C] p-5 px-10 text-white sm:grid-cols-2 sm:px-10">
|
||||
<div className="grid-1">
|
||||
<h2 className="text-2xl font-bold sm:text-4xl">
|
||||
Join Our Newsletter
|
||||
</h2>
|
||||
<p className="text:md sm:text-lg">
|
||||
Tidak ingin ketinggalan Berita Hukum terhangat? ingin mendapat
|
||||
informasi kajian dan networking terbaru? ikuti Newsletter kami and
|
||||
Stay up to Speed!
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-max-h-[400px] absolute right-0 bottom-0 w-auto sm:top-0">
|
||||
<img
|
||||
src={'https://placehold.co/800x200.png'}
|
||||
alt={APP.title}
|
||||
className="h-full w-auto"
|
||||
/>
|
||||
</div>
|
||||
<div className="z-10">
|
||||
<form className="grid gap-5">
|
||||
<input
|
||||
placeholder="Daftarkan Email Disini"
|
||||
className="h-[50px] flex-1 bg-white text-center text-lg font-light text-black placeholder:text-[#777777] focus:ring-0 focus:outline-none"
|
||||
size={1}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="newsPrimary"
|
||||
size="block"
|
||||
>
|
||||
Subscribe
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -6,20 +6,22 @@ import { COPYRIGHT_MENU, FOOTER_MENU } from './menu'
|
||||
|
||||
export const FooterLinks = () => {
|
||||
return (
|
||||
<div className="col-span-3 flex flex-col justify-between">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="col-span-1 flex flex-col justify-between sm:col-span-3">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
{FOOTER_MENU.map(({ group, items }, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex flex-col gap-3"
|
||||
className="flex w-full flex-col gap-3"
|
||||
>
|
||||
<h3 className="text-xl font-semibold sm:text-2xl">{group}</h3>
|
||||
<div
|
||||
className={`${group === 'Follow Us' ? 'flex-col-2 flex w-full flex-wrap' : ''}`}
|
||||
>
|
||||
<h3 className="text-2xl font-semibold">{group}</h3>
|
||||
<div>
|
||||
{items.map(({ url, icon: Icon, title }, subIndex) => (
|
||||
<Link
|
||||
key={subIndex}
|
||||
to={url}
|
||||
className="flex items-center gap-3 py-2"
|
||||
className="flex w-1/2 items-center gap-3 py-1 sm:w-full"
|
||||
>
|
||||
{Icon && (
|
||||
<Icon
|
||||
@ -35,21 +37,21 @@ export const FooterLinks = () => {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-between border-t border-white pt-8 text-sm">
|
||||
<div>
|
||||
{new Date().getFullYear()} {APP.title}. All rights reserved.
|
||||
</div>
|
||||
<div className="mt-8 justify-between border-t border-white pt-8 text-center text-xs sm:flex sm:text-sm">
|
||||
<div className="flex gap-6">
|
||||
{COPYRIGHT_MENU.map(({ url, title }, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
to={url}
|
||||
className="text-white underline"
|
||||
className={`w-full text-white underline`}
|
||||
>
|
||||
{title}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-2 sm:order-first">
|
||||
{new Date().getFullYear()} {APP.title}. All rights reserved.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -5,7 +5,8 @@ import { APP } from '~/data/meta'
|
||||
|
||||
export const FooterNewsletter = () => {
|
||||
return (
|
||||
<div className="col-span-2 grid gap-y-6">
|
||||
<>
|
||||
<div className="col-span-2 hidden gap-y-6 sm:grid">
|
||||
<div className="h-[75px] bg-white p-3">
|
||||
<Link
|
||||
to="/news"
|
||||
@ -18,11 +19,12 @@ export const FooterNewsletter = () => {
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<h2 className="text-4xl font-bold">Join Our Newsletter</h2>
|
||||
<p className="text-lg">
|
||||
Tidak ingin ketinggalan Berita Hukum terhangat? ingin mendapat informasi
|
||||
kajian dan networking terbaru? ikuti Newsletter kami and Stay up to
|
||||
Speed!
|
||||
Tidak ingin ketinggalan Berita Hukum terhangat? ingin mendapat
|
||||
informasi kajian dan networking terbaru? ikuti Newsletter kami and
|
||||
Stay up to Speed!
|
||||
</p>
|
||||
<form className="grid gap-5">
|
||||
<input
|
||||
@ -39,5 +41,20 @@ export const FooterNewsletter = () => {
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="block sm:hidden">
|
||||
<div className="h-[60px] bg-white p-3">
|
||||
<Link
|
||||
to="/news"
|
||||
className="h-full"
|
||||
>
|
||||
<img
|
||||
src={APP.logo}
|
||||
alt={APP.title}
|
||||
className="h-full w-auto"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
88
app/layouts/header-menu-mobile.tsx
Normal file
88
app/layouts/header-menu-mobile.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
import { useState } from 'react'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import { HeaderSearch } from '~/layouts/header-search'
|
||||
|
||||
import { MENU } from './menu'
|
||||
|
||||
export default function HeaderMenuMobile() {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
||||
|
||||
const handleToggleMenu = (): void => {
|
||||
setIsMenuOpen(!isMenuOpen)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="relative z-50 flex min-h-[65px] bg-[#2E2F7C] font-[sans-serif] tracking-wide text-white sm:hidden sm:px-10">
|
||||
<div className="mx-auto flex w-full max-w-screen-xl flex-wrap items-center gap-4 align-middle">
|
||||
{/* Menu */}
|
||||
<div
|
||||
className={`z-50 transition-transform duration-300 max-lg:fixed max-lg:top-0 max-lg:left-0 max-lg:h-full max-lg:w-full max-lg:overflow-auto max-lg:bg-[#2E2F7C] max-lg:p-6 max-lg:shadow-md ${
|
||||
isMenuOpen ? 'translate-x-0' : '-translate-x-full'
|
||||
}`}
|
||||
>
|
||||
{/* Tombol Close */}
|
||||
<button
|
||||
onClick={handleToggleMenu}
|
||||
className="fixed top-2 right-4 z-[100] flex h-9 w-9 items-center justify-center lg:hidden"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-3.5 w-3.5 fill-white"
|
||||
viewBox="0 0 320.591 320.591"
|
||||
>
|
||||
<path d="M30.391 318.583a30.37 30.37 0 0 1-21.56-7.288c-11.774-11.844-11.774-30.973 0-42.817L266.643 10.665c12.246-11.459 31.462-10.822 42.921 1.424 10.362 11.074 10.966 28.095 1.414 39.875L51.647 311.295a30.366 30.366 0 0 1-21.256 7.288z" />
|
||||
<path d="M287.9 318.583a30.37 30.37 0 0 1-21.257-8.806L8.83 51.963C-2.078 39.225-.595 20.055 12.143 9.146c11.369-9.736 28.136-9.736 39.504 0l259.331 257.813c12.243 11.462 12.876 30.679 1.414 42.922-.456.487-.927.958-1.414 1.414a30.368 30.368 0 0 1-23.078 7.288z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* List Menu */}
|
||||
<ul className="mx-10 max-lg:space-y-3 lg:ml-14 lg:flex lg:gap-x-5">
|
||||
{MENU.map((item) => (
|
||||
<li className="px-3 max-lg:border-b max-lg:py-3">
|
||||
<Link
|
||||
key={item.title}
|
||||
to={item.url}
|
||||
className={
|
||||
'flex h-full items-center justify-center border-white px-[35px] sm:border-r'
|
||||
}
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
|
||||
<button className="w-full bg-white px-[35px] py-3 text-center text-[#2E2F7C] sm:hidden">
|
||||
Akun
|
||||
</button>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Search dan Toggle Button */}
|
||||
<div className="align-center flex w-full justify-center">
|
||||
<button
|
||||
onClick={handleToggleMenu}
|
||||
className="h-[63px] border border-white px-4 lg:hidden"
|
||||
>
|
||||
<svg
|
||||
className="h-7 w-7"
|
||||
fill="#fff"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<div className="w-full py-3">
|
||||
<HeaderSearch></HeaderSearch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,11 +1,14 @@
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import HeaderMenuMobile from '~/layouts/header-menu-mobile'
|
||||
|
||||
import { HeaderSearch } from './header-search'
|
||||
import { MENU } from './menu'
|
||||
|
||||
export const HeaderMenu = () => {
|
||||
return (
|
||||
<div className="flex h-[60px] items-center justify-between bg-[#2E2F7C] text-xl font-medium text-white">
|
||||
<>
|
||||
<div className="hidden h-[60px] items-center justify-between bg-[#2E2F7C] text-xl font-medium text-white sm:flex">
|
||||
{MENU.map((item) => (
|
||||
<Link
|
||||
key={item.title}
|
||||
@ -19,5 +22,7 @@ export const HeaderMenu = () => {
|
||||
))}
|
||||
<HeaderSearch />
|
||||
</div>
|
||||
<HeaderMenuMobile />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -6,14 +6,14 @@ export const HeaderSearch = () => {
|
||||
<form className="flex flex-1 justify-between gap-[15px] px-[35px]">
|
||||
<input
|
||||
placeholder="Cari..."
|
||||
className="flex-1 placeholder:text-white focus:ring-0 focus:outline-none"
|
||||
className="flex-1 text-xl placeholder:text-white focus:ring-0 focus:outline-none sm:text-2xl"
|
||||
size={1}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="icon"
|
||||
size="icon"
|
||||
className="[&_svg]:size-[30px]"
|
||||
className="[&_svg]:size-[20px] sm:[&_svg]:size-[30px]"
|
||||
>
|
||||
<SearchIcon />
|
||||
</Button>
|
||||
|
||||
@ -5,24 +5,31 @@ import { APP } from '~/data/meta'
|
||||
|
||||
export const HeaderTop = () => {
|
||||
return (
|
||||
<div className="flex h-[100px] items-center justify-between gap-[15px] bg-white px-[50px] 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
|
||||
to="/news"
|
||||
className="h-full py-[5px]"
|
||||
className="mt-2 h-full py-2"
|
||||
>
|
||||
<img
|
||||
src={APP.logo}
|
||||
alt={APP.title}
|
||||
className="h-full w-auto"
|
||||
className="h-3/4 w-auto sm:h-full"
|
||||
/>
|
||||
</Link>
|
||||
<div className="flex h-full items-center py-1.5 font-light whitespace-pre-line">
|
||||
<div className="hidden h-full items-center py-1.5 font-light whitespace-pre-line sm:flex">
|
||||
{APP.description}
|
||||
</div>
|
||||
<div className="flex items-center gap-[15px]">
|
||||
<Button>About Us</Button>
|
||||
<Button variant="newsSecondary">Akun</Button>
|
||||
<div className="w-[60px]">
|
||||
<Button className="h-8 w-auto rounded-none px-3 text-xs sm:h-[50px] sm:w-[150px] sm:text-lg">
|
||||
About Us
|
||||
</Button>
|
||||
<Button
|
||||
variant="newsSecondary"
|
||||
className="hidden sm:block"
|
||||
>
|
||||
Akun
|
||||
</Button>
|
||||
<div className="w-[50px] sm:w-[60px]">
|
||||
<img
|
||||
alt="language"
|
||||
src="/flags/id.svg"
|
||||
|
||||
11
app/pages/news-detail/data.ts
Normal file
11
app/pages/news-detail/data.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { TNewsDetail } from '~/types/news'
|
||||
|
||||
export const CONTENT: TNewsDetail = {
|
||||
title: 'Hotman Paris Membuka Perpustakaan di tengah Diskotik',
|
||||
content:
|
||||
'Pengacara Kondang, Hotman Paris Hutapea, membuka sebuah perpustakaan baru di dalam diskotik nya yang berlokasi di daerah Jakarta Pusat, Hotman berkata Perpustakaan ini dibuka dengan harapan untuk meningkatkan gairah membaca masyarakat Indonesia, namun sayangnya..',
|
||||
featured: '/images/news-1.jpg',
|
||||
slug: 'hotman-paris-membuka-perpustakaan-di-tengah-diskotik',
|
||||
author: 'John Doe',
|
||||
date: new Date(),
|
||||
}
|
||||
17
app/pages/news-detail/index.tsx
Normal file
17
app/pages/news-detail/index.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { Card } from '~/components/ui/card'
|
||||
|
||||
import { CONTENT } from './data'
|
||||
|
||||
export const NewsDetailPage = () => {
|
||||
const { title } = CONTENT
|
||||
return (
|
||||
<div className="relative">
|
||||
<Card>
|
||||
<h2 className="text-2xl font-extrabold text-[#2E2F7C] sm:text-4xl">
|
||||
{title}
|
||||
</h2>
|
||||
News Detail
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -9,7 +9,8 @@ export const SPOTLIGHT: TNews = {
|
||||
title: 'Hotman Paris Membuka Perpustakaan di tengah Diskotik',
|
||||
content:
|
||||
'Pengacara Kondang, Hotman Paris Hutapea, membuka sebuah perpustakaan baru di dalam diskotik nya yang berlokasi di daerah Jakarta Pusat, Hotman berkata Perpustakaan ini dibuka dengan harapan untuk meningkatkan gairah membaca masyarakat Indonesia, namun sayangnya..',
|
||||
image: '/images/news-1.jpg',
|
||||
featured: '/images/news-1.jpg',
|
||||
slug: 'hotman-paris-membuka-perpustakaan-di-tengah-diskotik',
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -23,19 +24,25 @@ export const BERITA: TNews = {
|
||||
title: 'Travelling as a way of self-discovery and progress',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
image: '/images/news-2.jpg',
|
||||
featured: '/images/news-2.jpg',
|
||||
tag: ['Hukum Property'],
|
||||
slug: 'travelling-as-a-way-of-self-discovery-and-progress',
|
||||
},
|
||||
{
|
||||
title: 'How does writing influence your personal brand?',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
image: '/images/news-3.jpg',
|
||||
featured: '/images/news-3.jpg',
|
||||
tag: ['Hukum'],
|
||||
slug: 'how-does-writing-influence-your-personal-brand',
|
||||
},
|
||||
{
|
||||
title: 'Helping a local business reinvent itself',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
image: '/images/news-4.jpg',
|
||||
featured: '/images/news-4.jpg',
|
||||
tag: ['Hukum Property', 'PREMIUM CONTENT'],
|
||||
slug: 'helping-a-local-business-reinvent-itself',
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -49,19 +56,25 @@ export const KAJIAN: TNews = {
|
||||
title: 'Travelling as a way of self-discovery and progress',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
image: '/images/news-2.jpg',
|
||||
featured: '/images/news-2.jpg',
|
||||
tag: ['Hukum Property', 'PREMIUM CONTENT'],
|
||||
slug: 'travelling-as-a-way-of-self-discovery-and-progress',
|
||||
},
|
||||
{
|
||||
title: 'How does writing influence your personal brand?',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
image: '/images/news-3.jpg',
|
||||
featured: '/images/news-3.jpg',
|
||||
tag: ['Hukum Property', 'PREMIUM CONTENT'],
|
||||
slug: 'how-does-writing-influence-your-personal-brand',
|
||||
},
|
||||
{
|
||||
title: 'Helping a local business reinvent itself',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
image: '/images/news-4.jpg',
|
||||
featured: '/images/news-4.jpg',
|
||||
tag: ['Hukum Property', 'PREMIUM CONTENT'],
|
||||
slug: 'helping-a-local-business-reinvent-itself',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Card } from '~/components/ui/card'
|
||||
import { Carousel } from '~/components/ui/carousel'
|
||||
import { Newsletter } from '~/components/ui/newsletter'
|
||||
|
||||
import { BERITA, KAJIAN, SPOTLIGHT } from './data'
|
||||
import { Newsletter } from './newsletter'
|
||||
|
||||
export const NewsPage = () => {
|
||||
return (
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
export const Newsletter = () => {
|
||||
return <div>Newsletter</div>
|
||||
}
|
||||
7
app/routes/_layout.news.$slug.tsx
Normal file
7
app/routes/_layout.news.$slug.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import { NewsDetailPage } from '~/pages/news-detail'
|
||||
|
||||
const NewsDetailLayout = () => {
|
||||
return <NewsDetailPage />
|
||||
}
|
||||
|
||||
export default NewsDetailLayout
|
||||
@ -1,5 +1,6 @@
|
||||
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'
|
||||
@ -12,15 +13,12 @@ const NewsLayout = () => {
|
||||
<HeaderTop />
|
||||
<HeaderMenu />
|
||||
</header>
|
||||
<div className="mx-[50px] my-[25px] grid gap-y-[25px]">
|
||||
<img
|
||||
src="/images/banner.png"
|
||||
alt="banner"
|
||||
className="h-[100px] w-full object-contain"
|
||||
/>
|
||||
<div className="grid sm:mx-[50px] sm:my-[25px] sm:gap-y-[25px]">
|
||||
<Banner />
|
||||
<Outlet />
|
||||
</div>
|
||||
<footer className="grid w-full grid-cols-5 gap-16 bg-[#2E2F7C] px-16 py-20 text-white">
|
||||
|
||||
<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>
|
||||
|
||||
@ -2,9 +2,15 @@ export type TNews = {
|
||||
title: string
|
||||
description: string
|
||||
type: 'hero' | 'grid'
|
||||
items: {
|
||||
items: Pick<TNewsDetail, 'title' | 'content' | 'featured' | 'slug' | 'tag'>[]
|
||||
}
|
||||
|
||||
export type TNewsDetail = {
|
||||
title: string
|
||||
content: string
|
||||
image: string
|
||||
}[]
|
||||
featured: string
|
||||
author: string
|
||||
date: Date
|
||||
slug: string
|
||||
tag?: Array<string>
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user