Merge commit '9b13733205cf48354c86997c188063706657962a'

This commit is contained in:
Ardeman 2025-02-28 22:58:55 +08:00
commit 9bc7fbb902
11 changed files with 396 additions and 25 deletions

View File

@ -28,3 +28,27 @@ table.dataTable tbody > tr > td {
border-right: none !important;
border-bottom: 1px solid #ebebeb;
}
/* .embla.hero {
overflow: hidden;
}
.embla__container.hero {
display: flex;
background-color: yellow;
}
.embla__slide.hero {
flex: 0 0 100%;
min-width: 0;
} */
.embla__slide {
margin-right: 10px;
flex: 0 0 auto;
min-width: 0;
max-width: 100%;
}
@media (min-width: 768px) {
.embla__slide {
margin-right: 30px;
}
}

View File

@ -7,14 +7,14 @@ type BreadcrumbProperty = {
export const Breadcrumb = (property: BreadcrumbProperty) => {
const { slug } = property
return (
<div className="flex items-center gap-2">
<div className="mb-5 flex items-center gap-2">
<Link to={'#'}>Blog</Link>
<div>{'>'}</div>
<Link
to={'#'}
className="text-ellipsis md:text-clip ..."
>
{slug}
{slug.slice(0, 20) + '...'}
</Link>
</div>
)

View File

@ -0,0 +1,99 @@
import useEmblaCarousel from 'embla-carousel-react'
import { useCallback } from 'react'
import { Link } from 'react-router'
import { CarouselNextIcon } from '~/components/icons/carousel-next'
import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
import { Button } from '~/components/ui/button'
import { useNewsContext } from '~/contexts/news'
import type { TNews } from '~/types/news'
export const CarouselHero = (properties: TNews) => {
const { setIsSuccessOpen } = useNewsContext()
const { title, description, items } = properties
const [emblaReference, emblaApi] = useEmblaCarousel({ loop: true })
const previousSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev()
}, [emblaApi])
const nextSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollNext()
}, [emblaApi])
return (
<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-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}
onClick={previousSlide}
/>
<CarouselNextIcon
color="#2E2F7C"
className="cursor-pointer"
width={45}
height={45}
onClick={nextSlide}
/>
</div>
</div>
<div
className="embla hero overflow-hidden"
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>
</div>
<Button
size="block"
{...(isPremium
? {
onClick: () => {
setIsSuccessOpen('warning')
},
to: '',
}
: { as: Link, to: `/detail/${slug}` })}
>
View More
</Button>
</div>
</div>
</div>
))}
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,118 @@
import useEmblaCarousel from 'embla-carousel-react'
import { useCallback } from 'react'
import { Link } from 'react-router'
import { CarouselNextIcon } from '~/components/icons/carousel-next'
import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
import { Button } from '~/components/ui/button'
import { useNewsContext } from '~/contexts/news'
import type { TNews } from '~/types/news'
export const CarouselSection = (properties: TNews) => {
const { setIsSuccessOpen } = useNewsContext()
const { title, description, items } = properties
const [emblaReference, emblaApi] = useEmblaCarousel({ loop: false })
const previousSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev()
}, [emblaApi])
const nextSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollNext()
}, [emblaApi])
return (
<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-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}
onClick={previousSlide}
/>
<CarouselNextIcon
color="#2E2F7C"
className="cursor-pointer"
width={45}
height={45}
onClick={nextSlide}
/>
</div>
</div>
<div
className="embla overflow-hidden"
ref={emblaReference}
>
<div className="embla__container col-span-3 flex max-h-[586px]">
{items.map(
({ featured, title, content, tags, slug, isPremium }, index) => (
<div
className="embla__slide w-full min-w-0 flex-none sm:w-1/3"
key={index}
>
<div className="flex flex-col justify-between max-sm:mt-2">
<img
className="aspect-[5/4] max-h-[300px] w-full rounded-md object-cover"
src={featured}
alt={title}
/>
<div className={'flex flex-col justify-between gap-4'}>
<div className={'flex uppercase'}>
{tags?.map((item) => (
<span
key={index}
className="my-3 mr-2 inline-block rounded bg-[#F4F4F4] px-3 py-1 font-bold text-[#777777]"
>
{item}
</span>
))}
{isPremium && (
<span className="my-3 mr-2 inline-block rounded bg-[#D1C675] px-3 py-1 font-bold text-[#9D761D]">
Premium Content
</span>
)}
</div>
<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>
</div>
<Button
size="block"
{...(isPremium
? {
onClick: () => {
setIsSuccessOpen('warning')
},
to: '',
}
: { as: Link, to: `/news/detail/${slug}` })}
className="mb-5"
>
View More
</Button>
</div>
</div>
</div>
),
)}
</div>
</div>
</div>
)
}

132
app/data/contents.ts Normal file
View File

@ -0,0 +1,132 @@
import type { TNews } from '~/types/news'
export const SPOTLIGHT: TNews = {
title: 'SPOTLIGHT',
description: 'Berita Terhangat hari ini',
type: 'hero',
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: 'Berita Terhangat hari ini',
type: 'grid',
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: ['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: ['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: ['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: ['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: ['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: ['Hukum Property'],
isPremium: true,
slug: 'helping-a-local-business-reinvent-itself',
},
],
}
export const KAJIAN: TNews = {
title: 'KAJIAN',
description: 'Berita Terhangat hari ini',
type: 'grid',
items: [
{
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: '/images/news-2.jpg',
tags: ['Hukum Property'],
isPremium: true,
slug: 'travelling-as-a-way-of-self-discovery-and-progress',
},
{
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: ['Hukum Property'],
isPremium: true,
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: ['Hukum Property'],
isPremium: true,
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: ['Hukum Property'],
isPremium: true,
slug: 'helping-a-local-business-reinvent-itself',
},
],
}

View File

@ -34,7 +34,7 @@ export const NewsDefaultLayout = (properties: PropsWithChildren) => {
<HeaderTop />
<HeaderMenu />
</header>
<div className="grid sm:mx-[50px] sm:my-[25px] sm:gap-y-[25px]">
<div className="sm:mx-[50px] sm:my-[25px] sm:grid sm:gap-y-[25px]">
<Banner />
{children}
</div>

View File

@ -35,7 +35,7 @@ export const CONTENT: TNewsDetail = {
author: 'John Doe',
date: new Date(),
categories: [],
tags: ['Category', 'Popular'],
tags: ['Category', 'Popular', 'Trending', 'Latest'],
}
export const BERITA: TNews = {

View File

@ -2,7 +2,7 @@ import htmlParse from 'html-react-parser'
import { Breadcrumb } from '~/components/ui/breadcrumb'
import { Card } from '~/components/ui/card'
import { Carousel } from '~/components/ui/carousel'
import { CarouselSection } from '~/components/ui/carousel-section'
import { IconsSocial } from '~/components/ui/social-share'
import { BERITA, CONTENT } from './data'
@ -19,8 +19,8 @@ export const NewsDetailPage = () => {
</h2>
{/* next planing create component for this section */}
<div className="my-5 flex w-full items-center justify-between align-middle">
<div className="flex items-center gap-2 align-middle">
<div className="my-5 w-full items-center justify-between gap-2 align-middle sm:flex">
<div className="mb-2 flex items-center gap-2 align-middle">
<img
src={'https://placehold.co/50x50.png'}
alt={title}
@ -33,14 +33,14 @@ export const NewsDetailPage = () => {
</p>
</div>
</div>
<IconsSocial className="ml-auto" />
<IconsSocial className="flex-row-reverse" />
</div>
{/* end next planing create component for this section */}
<div className="w-full bg-amber-200">
<img
src={featured}
alt={title}
className="h-[600px] w-full object-cover object-center"
className="w-full object-cover object-center sm:h-[600px]"
/>
</div>
@ -49,16 +49,16 @@ export const NewsDetailPage = () => {
{htmlParse(content)}
</article>
</div>
<div className="flex items-end justify-between border-b-3 border-b-gray-300 py-4">
<div className="flex flex-col">
<div className="items-end justify-between border-b-3 border-b-gray-300 py-4 sm:flex">
<div className="flex flex-col max-sm:mb-3">
<p className="mb-2">Share this post</p>
<IconsSocial className="a" />
</div>
<div className="flex flex-wrap items-end">
<div className="flex flex-wrap items-end gap-2">
{tags?.map((tag) => (
<span
key={tag}
className="mx-2 rounded bg-gray-300 p-1"
className="rounded bg-gray-300 p-1"
>
{tag}
</span>
@ -80,8 +80,8 @@ export const NewsDetailPage = () => {
</div>
</Card>
<Card>
<Carousel {...BERITA} />
<Card className="hidden sm:block">
<CarouselSection {...BERITA} />
</Card>
</div>
)

View File

@ -16,7 +16,7 @@ export const SPOTLIGHT: TNews = {
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',
featured: 'https://placehold.co/800x500.png',
slug: 'hotman-paris-membuka-perpustakaan-di-tengah-diskotik',
},
{
@ -63,7 +63,7 @@ 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.',
featured: 'https://placehold.co/600x400.png',
featured: 'https://placehold.co/800x500.png',
tags: ['Hukum Property'],
slug: 'travelling-as-a-way-of-self-discovery-and-progress',
},

View File

@ -1,23 +1,21 @@
import { Card } from '~/components/ui/card'
import { Carousel } from '~/components/ui/carousel'
import { CarouselHero } from '~/components/ui/carousel-hero'
import { CarouselSection } from '~/components/ui/carousel-section'
import { Newsletter } from '~/components/ui/newsletter'
import { BERITA, KAJIAN, SPOTLIGHT } from './data'
import { SPOTLIGHT, BERITA } from './data'
export const NewsPage = () => {
return (
<div className="relative">
<Card>
<Carousel {...SPOTLIGHT} />
<CarouselHero {...SPOTLIGHT} />
</Card>
<div className="min-h-[400px] sm:min-h-[300px]">
<Newsletter className="mr-0 sm:-ml-14" />
</div>
<Card>
<Carousel {...BERITA} />
</Card>
<Card>
<Carousel {...KAJIAN} />
<CarouselSection {...BERITA} />
</Card>
</div>
)

View File

@ -4,7 +4,7 @@ import isIgnored from '@commitlint/is-ignored'
const Configuration = {
extends: ['@commitlint/config-conventional'],
formatter: '@commitlint/format',
ignores: [(commit) => isIgnored(commit)],
ignores: [(commit) => isIgnored(commit) || commit.startsWith('Merge')],
plugins: [
{
rules: {},