Compare commits

..

No commits in common. "661437a2d33f3ad710f61bbd788f3f4c16f9e3a2" and "a635dc143132ab2f37ba6ffac86ea9a3d3d06ddf" have entirely different histories.

10 changed files with 153 additions and 135 deletions

View File

@ -2,31 +2,28 @@ import { z } from 'zod'
import { HttpServer, type THttpServer } from '~/libs/http-server'
const authorSchema = z.object({
id: z.string(),
name: z.string(),
profile_picture: z.string(),
})
const categoriesCodeSchema = z.array(
z.object({
id: z.string(),
name: z.string(),
code: z.string(),
}),
)
const newsSchema = z.object({
data: z.array(
z.object({
id: z.string(),
title: z.string(),
content: z.string(),
categories: categoriesCodeSchema,
categories: z.array(
z.object({
id: z.string(),
name: z.string(),
code: z.string(),
created_at: z.string(),
updated_at: z.string(),
}),
),
tags: z.array(
z.object({
id: z.string(),
name: z.string(),
code: z.string(),
created_at: z.string(),
updated_at: z.string(),
}),
),
is_premium: z.boolean(),
@ -36,14 +33,10 @@ const newsSchema = z.object({
live_at: z.string(),
created_at: z.string(),
updated_at: z.string(),
author: authorSchema,
}),
),
})
export type TAuthor = z.infer<typeof authorSchema>
export type TCategories = z.infer<typeof categoriesCodeSchema>
export const getNews = async (parameters: THttpServer) => {
try {
const { data } = await HttpServer(parameters).get(`/api/news`)

View File

@ -6,20 +6,9 @@ const userSchema = z.object({
data: z.object({
id: z.string(),
email: z.string(),
phone: z.string(),
subscribe: z.object({
id: z.string(),
subscribe_plan_id: z.string(),
start_date: z.string(),
end_date: z.string(),
status: z.string(),
auto_renew: z.boolean(),
subscribe_plan: z.object({
id: z.string(),
name: z.string(),
code: z.string(),
}),
}),
subscribe_plan_code: z.string(),
subscribe_plan_name: z.string(),
subscribe_status: z.string(),
}),
})

View File

@ -33,43 +33,26 @@ table.dataTable tbody > tr > td {
border-bottom: 1px solid #ebebeb;
}
nav[aria-label='pagination'] {
/* .embla.hero {
overflow: hidden;
}
.embla__container.hero {
display: flex;
justify-content: center;
}
/* Style untuk tombol aktif (current) */
.dt-paging-button.current {
background-color: #2e2f7c !important;
color: white !important;
background-color: yellow;
}
.embla__slide.hero {
flex: 0 0 100%;
min-width: 0;
} */
/* Style tombol aktif, kecuali jika disabled */
div.dt-container .dt-paging .dt-paging-button.current:not(.disabled),
div.dt-container .dt-paging .dt-paging-button.current:not(.disabled):hover {
color: white !important;
background-color: #2e2f7c !important;
min-width: 24px;
padding: 3px 6px;
}
/* Style tombol disabled */
div.dt-container .dt-paging .dt-paging-button.disabled {
background-color: transparent !important;
color: #ccc !important;
cursor: not-allowed;
pointer-events: none; /* Agar tidak bisa diklik */
}
/* Menghindari hover effect untuk tombol yang disabled */
div.dt-container .dt-paging .dt-paging-button:not(.disabled):hover {
background-color: #2e2f7c !important;
color: white !important;
}
/* Style default tombol */
div.dt-container .dt-paging .dt-paging-button {
min-width: 24px;
padding: 3px 6px;
background-color: transparent !important;
color: #2e2f7c !important;
/* .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

@ -3,17 +3,14 @@ import DataTable from 'datatables.net-react'
import React from 'react'
export type UiTableProperties = {
data: any // eslint-disable-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data: any[]
columns: ConfigColumns[]
slots?: any // eslint-disable-line @typescript-eslint/no-explicit-any
options?: Config
title: string
}
const renderPaginationIcon = (icon: string) => {
return `<div class="pagination-icon">${icon}</div>`
}
export const UiTable: React.FC<UiTableProperties> = ({
data,
columns,
@ -36,22 +33,6 @@ export const UiTable: React.FC<UiTableProperties> = ({
searching: true,
ordering: true,
info: true,
language: {
paginate: {
first: renderPaginationIcon(
`<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4 "><path stroke-linecap="round" stroke-linejoin="round" d="m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5" /></svg>`,
),
previous: renderPaginationIcon(
`<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" /></svg>`,
),
next: renderPaginationIcon(
`<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" /></svg>`,
),
last: renderPaginationIcon(
`<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"> <path stroke-linecap="round" stroke-linejoin="round" d="m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5" /> </svg>`,
),
},
},
...options,
}}
/>

View File

@ -95,6 +95,67 @@ export const BERITA: TNews = {
],
}
export const KAJIAN: TNews = {
title: 'KAJIAN',
description: DUMMY_DESCRIPTION,
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: '/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: '/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',
},
],
}
export const BANNER: TBanner[] = [
{
id: 1,

View File

@ -2,12 +2,10 @@ import DT from 'datatables.net-dt'
import DataTable from 'datatables.net-react'
import { Link, useRouteLoaderData } from 'react-router'
import type { TCategories } from '~/apis/admin/get-news'
import { Button } from '~/components/ui/button'
import { UiTable } from '~/components/ui/table'
import { TitleDashboard } from '~/components/ui/title-dashboard'
import type { loader } from '~/routes/_admin.lg-admin._dashboard.contents'
import { formatDate } from '~/utils/formatter'
export const ContentsPage = () => {
const loaderData = useRouteLoaderData<typeof loader>(
@ -18,22 +16,11 @@ export const ContentsPage = () => {
DataTable.use(DT)
const dataTable = newsData
const dataColumns = [
{
title: 'No',
data: undefined,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
render: function (data: any, type: any, row: any, meta: any) {
return meta.row + 1
},
},
{ title: 'Tanggal Konten', data: 'live_at' },
{
title: 'Nama Penulis',
data: 'author',
},
{ title: 'No', data: 'id' },
{ title: 'Tanggal Konten', data: 'created_at' },
{ title: 'Nama Penulis', data: 'author_id' },
{ title: 'Judul', data: 'title' },
{ title: 'Kategori', data: 'categories' },
// { title: 'Kategori', data: 'category' },
{
title: 'Tags',
data: 'is_premium',
@ -43,22 +30,12 @@ export const ContentsPage = () => {
: `<span class="bg-[#F5F5F5] text-[#4C5CA0] px-4 py-2 rounded-full">Normal</span>`
},
},
{
title: 'Action',
data: 'slug',
},
// {
// title: 'Action',
// data: 'id',
// },
]
const dataSlot = {
1: (value: string) => {
return formatDate(value)
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2: (value: any, type: any, data: any) =>
`<span>${value.name}</span> <br> <span class="text-sm text-[#7C7C7C]">ID: ${data.id.slice(0, 12)}</span>`,
4: (value: TCategories) => {
const categories = value.map((item) => item.name).join(', ')
return `${categories}`
},
6: (value: string | number) => {
return (
<Button

View File

@ -1,4 +1,5 @@
import type { TNewsDetail } from '~/types/news'
import { DUMMY_DESCRIPTION } from '~/data/contents'
import type { TNews, TNewsDetail } from '~/types/news'
export const CONTENT: TNewsDetail = {
title: 'Hotman Paris Membuka Perpustakaan di tengah Diskotik',
@ -37,3 +38,35 @@ export const CONTENT: TNewsDetail = {
categories: [],
tags: ['Category', 'Popular', 'Trending', 'Latest'],
}
export const BERITA: TNews = {
title: 'BERITA',
description: DUMMY_DESCRIPTION,
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'],
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',
},
],
}

View File

@ -34,7 +34,7 @@ export const NewsDetailPage = () => {
</p>
</div>
</div>
<IconsSocial className="flex-row" />
<IconsSocial className="flex-row-reverse" />
</div>
{/* end next planing create component for this section */}
<div className="w-full bg-amber-200">
@ -50,7 +50,7 @@ export const NewsDetailPage = () => {
{htmlParse(content)}
</article>
</div>
<div className="items-end justify-between border-b-gray-300 py-4 sm:flex">
<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" />
@ -66,6 +66,18 @@ export const NewsDetailPage = () => {
))}
</div>
</div>
<div className="mt-5 flex items-center gap-2 align-middle">
<img
src={'https://placehold.co/50x50.png'}
alt={title}
className="h-12 w-12 rounded-full"
/>
<div>
<h4 className="text-md">{author}</h4>
<p className="text-sm">Job title, Company name</p>
</div>
</div>
</div>
</Card>

View File

@ -1,11 +1,3 @@
export const formatNumberWithPeriods = (number: number) => {
return new Intl.NumberFormat('id-ID').format(number)
}
export const formatDate = (isoDate: string): string => {
const date = new Date(isoDate)
const day = date.getDate().toString().padStart(2, '0')
const month = (date.getMonth() + 1).toString().padStart(2, '0') // Month is zero-based
const year = date.getFullYear()
return `${day}/${month}/${year}`
}

View File

@ -12,10 +12,7 @@ type TGetPremiumAttribute = {
export const getPremiumAttribute = (parameters: TGetPremiumAttribute) => {
const { isPremium, slug, onClick, userData } = parameters
if (
isPremium &&
(!userData || userData?.subscribe.subscribe_plan.code === 'basic')
) {
if (isPremium && (!userData || userData?.subscribe_plan_code === 'basic')) {
return {
onClick,
to: '',