refactor: update news types and enhance news data handling in components
This commit is contained in:
parent
9f82779456
commit
7afabdaa03
@ -1,6 +1,7 @@
|
||||
import useEmblaCarousel from 'embla-carousel-react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useRouteLoaderData } from 'react-router'
|
||||
import { stripHtml } from 'string-strip-html'
|
||||
|
||||
import { Button } from '~/components/ui/button'
|
||||
import { CarouselButton } from '~/components/ui/button-slide'
|
||||
@ -71,41 +72,43 @@ export const CarouselHero = (properties: TNews) => {
|
||||
ref={emblaReference}
|
||||
>
|
||||
<div className="embla__container hero flex sm:gap-x-8">
|
||||
{items.map(({ featured, title, content, slug, isPremium }, index) => (
|
||||
<div
|
||||
className="embla__slide hero w-full min-w-0 flex-none"
|
||||
key={index}
|
||||
>
|
||||
<div className="max-sm:mt-2 sm:flex">
|
||||
<img
|
||||
className="col-span-2 aspect-[174/100] object-cover"
|
||||
src={featured}
|
||||
alt={title}
|
||||
/>
|
||||
<div className="flex h-full flex-col justify-between gap-7 sm:px-5">
|
||||
<div>
|
||||
<h3 className="mt-2 w-full text-2xl font-bold sm:mt-0 sm:text-4xl">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-md mt-5 text-[#777777] sm:text-xl">
|
||||
{content}
|
||||
</p>
|
||||
{items.map(
|
||||
({ featured_image, title, content, slug, is_premium }, index) => (
|
||||
<div
|
||||
className="embla__slide hero w-full min-w-0 flex-none"
|
||||
key={index}
|
||||
>
|
||||
<div className="max-sm:mt-2 sm:flex">
|
||||
<img
|
||||
className="col-span-2 aspect-[174/100] object-cover"
|
||||
src={featured_image}
|
||||
alt={title}
|
||||
/>
|
||||
<div className="flex h-full flex-col justify-between gap-7 sm:px-5">
|
||||
<div>
|
||||
<h3 className="mt-2 w-full text-2xl font-bold sm:mt-0 sm:text-4xl">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-md mt-5 line-clamp-10 text-[#777777] sm:text-xl">
|
||||
{stripHtml(content).result}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
size="block"
|
||||
{...getPremiumAttribute({
|
||||
isPremium: is_premium,
|
||||
slug,
|
||||
onClick: () => setIsSuccessOpen('warning'),
|
||||
userData,
|
||||
})}
|
||||
>
|
||||
View More
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
size="block"
|
||||
{...getPremiumAttribute({
|
||||
isPremium,
|
||||
slug,
|
||||
onClick: () => setIsSuccessOpen('warning'),
|
||||
userData,
|
||||
})}
|
||||
>
|
||||
View More
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import useEmblaCarousel from 'embla-carousel-react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useRouteLoaderData } from 'react-router'
|
||||
import { stripHtml } from 'string-strip-html'
|
||||
|
||||
import { Button } from '~/components/ui/button'
|
||||
import { CarouselButton } from '~/components/ui/button-slide'
|
||||
@ -79,7 +80,10 @@ export const CarouselSection = (properties: TNews) => {
|
||||
>
|
||||
<div className="embla__container col-span-3 flex max-h-[586px] sm:gap-x-8">
|
||||
{items.map(
|
||||
({ featured, title, content, tags, slug, isPremium }, index) => (
|
||||
(
|
||||
{ featured_image, title, content, tags, slug, is_premium },
|
||||
index,
|
||||
) => (
|
||||
<div
|
||||
className="embla__slide w-full min-w-0 flex-none sm:w-1/3"
|
||||
key={index}
|
||||
@ -87,27 +91,27 @@ export const CarouselSection = (properties: TNews) => {
|
||||
<div className="flex flex-col justify-between gap-3">
|
||||
<img
|
||||
className="aspect-[174/100] max-h-[280px] w-full rounded-md object-cover sm:aspect-[5/4]"
|
||||
src={featured}
|
||||
src={featured_image}
|
||||
alt={title}
|
||||
/>
|
||||
<div className={'flex flex-col justify-between gap-4'}>
|
||||
<Tags
|
||||
tags={tags || []}
|
||||
is_premium={isPremium}
|
||||
is_premium={is_premium}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<h3 className="mt-2 w-full text-xl font-bold sm:text-2xl lg:mt-0">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-md mt-5 text-[#777777] sm:text-xl">
|
||||
{content}
|
||||
<p className="text-md mt-5 line-clamp-3 text-[#777777] sm:text-xl">
|
||||
{stripHtml(content).result}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
size="block"
|
||||
{...getPremiumAttribute({
|
||||
isPremium,
|
||||
isPremium: is_premium,
|
||||
slug,
|
||||
onClick: () => setIsSuccessOpen('warning'),
|
||||
userData,
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { TNews } from '~/types/news'
|
||||
type TBanner = {
|
||||
id: number
|
||||
urlImage: string
|
||||
@ -8,93 +7,6 @@ type TBanner = {
|
||||
createdAt?: string
|
||||
}
|
||||
|
||||
const DUMMY_DESCRIPTION = 'Berita Terhangat hari ini'
|
||||
|
||||
export const SPOTLIGHT: TNews = {
|
||||
title: 'SPOTLIGHT',
|
||||
description: DUMMY_DESCRIPTION,
|
||||
items: [
|
||||
{
|
||||
title: '01 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',
|
||||
},
|
||||
{
|
||||
title: '02 Travelling as a way of self-discovery and progress',
|
||||
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: 'https://placehold.co/600x400.png',
|
||||
slug: 'hotman-paris-membuka-perpustakaan-di-tengah-diskotik',
|
||||
},
|
||||
{
|
||||
title: '03 Travelling as a way of self-discovery and progress',
|
||||
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',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const BERITA: TNews = {
|
||||
title: 'BERITA',
|
||||
description: DUMMY_DESCRIPTION,
|
||||
items: [
|
||||
{
|
||||
title: '01 Travelling as a way of self-discovery and progress ',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
featured: '/images/news-2.jpg',
|
||||
tags: [{ id: '1', code: 'hukum-property', name: 'Hukum Property' }],
|
||||
slug: 'travelling-as-a-way-of-self-discovery-and-progress',
|
||||
},
|
||||
{
|
||||
title: '02 How does writing influence your personal brand?',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
featured: '/images/news-3.jpg',
|
||||
tags: [{ id: '2', code: 'hukum', name: 'Hukum' }],
|
||||
slug: 'how-does-writing-influence-your-personal-brand',
|
||||
},
|
||||
{
|
||||
title: '03 Helping a local business reinvent itself',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
featured: '/images/news-4.jpg',
|
||||
tags: [{ id: '3', code: 'hukum-property', name: 'Hukum Property' }],
|
||||
isPremium: true,
|
||||
slug: 'helping-a-local-business-reinvent-itself',
|
||||
},
|
||||
{
|
||||
title: 'Travelling as a way of self-discovery and progress',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.',
|
||||
featured: 'https://placehold.co/600x400.png',
|
||||
tags: [{ id: '1', code: 'hukum-property', name: '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.',
|
||||
featured: '/images/news-3.jpg',
|
||||
tags: [{ id: '2', code: 'hukum', name: '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.',
|
||||
featured: '/images/news-4.jpg',
|
||||
tags: [{ id: '3', code: 'hukum-property', name: 'Hukum Property' }],
|
||||
isPremium: true,
|
||||
slug: 'helping-a-local-business-reinvent-itself',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const BANNER: TBanner[] = [
|
||||
{
|
||||
id: 1,
|
||||
|
||||
@ -7,13 +7,18 @@ import { CarouselSection } from '~/components/ui/carousel-section'
|
||||
import { NewsAuthor } from '~/components/ui/news-author'
|
||||
import { SocialShareButtons } from '~/components/ui/social-share'
|
||||
import { Tags } from '~/components/ui/tags'
|
||||
import { BERITA } from '~/data/contents'
|
||||
import type { loader } from '~/routes/_news.detail.$slug'
|
||||
import type { TNews } from '~/types/news'
|
||||
|
||||
export const NewsDetailPage = () => {
|
||||
const loaderData = useRouteLoaderData<typeof loader>(
|
||||
'routes/_news.detail.$slug',
|
||||
)
|
||||
const berita: TNews = {
|
||||
title: loaderData?.beritaCategory?.name || '',
|
||||
description: loaderData?.beritaCategory?.description || '',
|
||||
items: loaderData?.beritaNews || [],
|
||||
}
|
||||
const currentUrl = globalThis.location
|
||||
const { title, content, featured_image, author, live_at, tags } =
|
||||
loaderData?.newsDetailData || {}
|
||||
@ -69,7 +74,7 @@ export const NewsDetailPage = () => {
|
||||
</Card>
|
||||
|
||||
<Card className="bg-white p-5 max-sm:hidden">
|
||||
<CarouselSection {...BERITA} />
|
||||
<CarouselSection {...berita} />
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,21 +1,36 @@
|
||||
import { useRouteLoaderData } from 'react-router'
|
||||
|
||||
import { Card } from '~/components/ui/card'
|
||||
import { CarouselHero } from '~/components/ui/carousel-hero'
|
||||
import { CarouselSection } from '~/components/ui/carousel-section'
|
||||
import { Newsletter } from '~/components/ui/newsletter'
|
||||
import { BERITA, SPOTLIGHT } from '~/data/contents'
|
||||
import type { loader } from '~/routes/_news._index'
|
||||
import type { TNews } from '~/types/news'
|
||||
|
||||
export const NewsPage = () => {
|
||||
const loaderData = useRouteLoaderData<typeof loader>('routes/_news._index')
|
||||
const spotlight: TNews = {
|
||||
title: loaderData?.spotlightCategory?.name || '',
|
||||
description: loaderData?.spotlightCategory?.description || '',
|
||||
items: loaderData?.spotlightNews || [],
|
||||
}
|
||||
const berita: TNews = {
|
||||
title: loaderData?.beritaCategory?.name || '',
|
||||
description: loaderData?.beritaCategory?.description || '',
|
||||
items: loaderData?.beritaNews || [],
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<Card>
|
||||
<CarouselHero {...SPOTLIGHT} />
|
||||
<CarouselHero {...spotlight} />
|
||||
</Card>
|
||||
<div className="min-h-[400px] sm:min-h-[300px]">
|
||||
<Newsletter className="mr-0 sm:-ml-14" />
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CarouselSection {...BERITA} />
|
||||
<CarouselSection {...berita} />
|
||||
</Card>
|
||||
{/* <Card>
|
||||
<CarouselSection {...KAJIAN} />
|
||||
|
||||
@ -1,5 +1,24 @@
|
||||
import { getCategories } from '~/apis/common/get-categories'
|
||||
import { getNews } from '~/apis/common/get-news'
|
||||
import { NewsPage } from '~/pages/news'
|
||||
|
||||
import type { Route } from './+types/_news._index'
|
||||
|
||||
export const loader = async ({}: Route.LoaderArgs) => {
|
||||
const { data: categoriesData } = await getCategories()
|
||||
const spotlightCode = 'spotlight'
|
||||
const spotlightCategory = categoriesData.find(
|
||||
(category) => category.code === spotlightCode,
|
||||
)
|
||||
const { data: spotlightNews } = await getNews({ categories: [spotlightCode] })
|
||||
const beritaCode = 'berita'
|
||||
const beritaCategory = categoriesData.find(
|
||||
(category) => category.code === beritaCode,
|
||||
)
|
||||
const { data: beritaNews } = await getNews({ categories: [beritaCode] })
|
||||
return { spotlightCategory, spotlightNews, beritaCategory, beritaNews }
|
||||
}
|
||||
|
||||
const NewsIndexLayout = () => <NewsPage />
|
||||
|
||||
export default NewsIndexLayout
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { getCategories } from '~/apis/common/get-categories'
|
||||
import { getNews } from '~/apis/common/get-news'
|
||||
import { getNewsBySlug } from '~/apis/common/get-news-by-slug'
|
||||
import { APP } from '~/configs/meta'
|
||||
import { handleCookie } from '~/libs/cookies'
|
||||
@ -11,9 +13,17 @@ export const loader = async ({ request, params }: Route.LoaderArgs) => {
|
||||
slug: params.slug,
|
||||
accessToken: userToken,
|
||||
})
|
||||
const { data: categoriesData } = await getCategories()
|
||||
const beritaCode = 'berita'
|
||||
const beritaCategory = categoriesData.find(
|
||||
(category) => category.code === beritaCode,
|
||||
)
|
||||
const { data: beritaNews } = await getNews({ categories: [beritaCode] })
|
||||
|
||||
return {
|
||||
newsDetailData,
|
||||
beritaCategory,
|
||||
beritaNews,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,22 +1,7 @@
|
||||
import type { TTagResponse } from '~/apis/common/get-tags'
|
||||
import type { TNewsResponse } from '~/apis/common/get-news'
|
||||
|
||||
export type TNews = {
|
||||
title: string
|
||||
description: string
|
||||
items: Pick<
|
||||
TNewsDetail,
|
||||
'title' | 'content' | 'featured' | 'slug' | 'tags' | 'isPremium'
|
||||
>[]
|
||||
}
|
||||
|
||||
type TNewsDetail = {
|
||||
title: string
|
||||
content: string
|
||||
featured: string
|
||||
author: string
|
||||
date: Date
|
||||
slug: string
|
||||
tags?: TTagResponse[]
|
||||
isPremium?: boolean
|
||||
categories?: Array<string>
|
||||
items: TNewsResponse[]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user