diff --git a/app/components/ui/banner.tsx b/app/components/ui/banner.tsx index 354f5e7..0ba09ec 100644 --- a/app/components/ui/banner.tsx +++ b/app/components/ui/banner.tsx @@ -1,10 +1,11 @@ +import Autoplay from 'embla-carousel-autoplay' import useEmblaCarousel from 'embla-carousel-react' import { Link } from 'react-router' import { BANNER } from '~/data/contents' export const Banner = () => { - const [emblaReference] = useEmblaCarousel({ loop: false }) + const [emblaReference] = useEmblaCarousel({ loop: true }, [Autoplay()]) return (
diff --git a/app/components/ui/chart.tsx b/app/components/ui/chart.tsx index 25c38dd..3915571 100644 --- a/app/components/ui/chart.tsx +++ b/app/components/ui/chart.tsx @@ -71,7 +71,7 @@ export const UiChartPie = () => { return (
-

Top 5 Konten

+

Top 5 Artikel

= Omit< + ComponentProps<'input'>, + 'size' +> & { + id: string + label?: ReactNode + name: Path + rules?: RegisterOptions + containerClassName?: string + labelClassName?: string +} + +export const InputFile = >( + properties: TInputProperties, +) => { + const { + id, + label, + name, + rules, + placeholder, + disabled, + className, + containerClassName, + labelClassName, + ...restProperties + } = properties + + const { + register, + formState: { errors }, + } = useRemixFormContext() + + const error: FieldError = get(errors, name) + + return ( + + + + + + ) +} diff --git a/app/components/ui/input.tsx b/app/components/ui/input.tsx index f4d73f1..ee6fb6a 100644 --- a/app/components/ui/input.tsx +++ b/app/components/ui/input.tsx @@ -75,7 +75,7 @@ export const Input = >( type="button" variant="icon" size="fit" - className="absolute right-3 h-[42px] text-gray-500" + className="absolute right-3 h-[42px]" onClick={() => setInputType(inputType === 'password' ? 'text' : 'password') } diff --git a/app/components/ui/table.tsx b/app/components/ui/table.tsx index ab29799..3fb8d2f 100644 --- a/app/components/ui/table.tsx +++ b/app/components/ui/table.tsx @@ -1,11 +1,11 @@ import DT, { type Config, type ConfigColumns } from 'datatables.net-dt' -import DataTable from 'datatables.net-react' +import DataTable, { type DataTableSlots } from 'datatables.net-react' import React from 'react' type UiTableProperties = { - data: any // eslint-disable-line @typescript-eslint/no-explicit-any + data: any[] // eslint-disable-line @typescript-eslint/no-explicit-any columns: ConfigColumns[] - slots?: any // eslint-disable-line @typescript-eslint/no-explicit-any + slots?: DataTableSlots options?: Config title: string } diff --git a/app/configs/meta.ts b/app/configs/meta.ts index bf3d7aa..c5ecec7 100644 --- a/app/configs/meta.ts +++ b/app/configs/meta.ts @@ -1,4 +1,4 @@ -import { MENU as ADMIN_MENU } from '~/layouts/admin/menu' +import { MENU as ADMIN_MENU, SUB_MENU } from '~/layouts/admin/menu' export const APP = { title: 'LegalGo', @@ -23,4 +23,5 @@ export const META_TITLE_CONFIG: TMetaTitleConfig = [ ...ADMIN_MENU.flatMap((menu) => menu.items.map((item) => ({ path: item.url, title: item.title })), ), + ...SUB_MENU, ] diff --git a/app/layouts/admin/menu.ts b/app/layouts/admin/menu.ts index 7705ac0..e898d93 100644 --- a/app/layouts/admin/menu.ts +++ b/app/layouts/admin/menu.ts @@ -30,7 +30,7 @@ export const MENU: TMenu[] = [ icon: DocumentIcon, }, { - title: 'Konten', + title: 'Artikel', url: '/lg-admin/contents', icon: ChatIcon, }, @@ -67,3 +67,30 @@ export const MENU: TMenu[] = [ ], }, ] + +export const SUB_MENU = [ + { + title: 'Buat Artikel', + path: '/lg-admin/contents/create', + }, + { + title: 'Update Artikel', + path: '/lg-admin/contents/update', + }, + { + title: 'Buat Kategori', + path: '/lg-admin/categories/create', + }, + { + title: 'Update Kategori', + path: '/lg-admin/categories/update', + }, + { + title: 'Buat Tag', + path: '/lg-admin/tags/create', + }, + { + title: 'Update Tag', + path: '/lg-admin/tags/update', + }, +] diff --git a/app/pages/dashboard-advertisements/index.tsx b/app/pages/dashboard-advertisements/index.tsx index 6a596e6..95def65 100644 --- a/app/pages/dashboard-advertisements/index.tsx +++ b/app/pages/dashboard-advertisements/index.tsx @@ -1,5 +1,7 @@ import { Field, Input, Label, Select } from '@headlessui/react' import { MagnifyingGlassIcon } from '@heroicons/react/20/solid' +import type { ConfigColumns } from 'datatables.net-dt' +import type { DataTableSlots } from 'datatables.net-react' import { useState } from 'react' import { twMerge } from 'tailwind-merge' @@ -36,14 +38,14 @@ export const AdvertisementsPage = ({ } const dataBanner = BANNER - const dataColumns = [ + const dataColumns: ConfigColumns[] = [ { title: 'No', data: 'id' }, { title: 'Banner', data: 'urlImage' }, { title: 'Link', data: 'link' }, { title: 'Tgl Create', data: 'createdAt' }, { title: 'Status', data: 'status' }, ] - const dataSlot = { + const dataSlot: DataTableSlots = { 1: (value: string) => { return (
diff --git a/app/pages/dashboard-categories/index.tsx b/app/pages/dashboard-categories/index.tsx index 66056d2..46e8c97 100644 --- a/app/pages/dashboard-categories/index.tsx +++ b/app/pages/dashboard-categories/index.tsx @@ -1,5 +1,5 @@ -import DT from 'datatables.net-dt' -import DataTable from 'datatables.net-react' +import DT, { type Config, type ConfigColumns } from 'datatables.net-dt' +import DataTable, { type DataTableSlots } from 'datatables.net-react' import { Link, useRouteLoaderData } from 'react-router' import type { TCategoryResponse } from '~/apis/common/get-categories' @@ -13,17 +13,18 @@ export const CategoriesPage = () => { ) DataTable.use(DT) - const dataTable = loaderData?.categoriesData?.sort((a, b) => { - if (a.sequence === null) return 1 - if (b.sequence === null) return -1 - return a.sequence - b.sequence - }) - const dataColumns = [ + const dataTable = + loaderData?.categoriesData?.sort((a, b) => { + if (a.sequence === null) return 1 + if (b.sequence === null) return -1 + return a.sequence - b.sequence + }) || [] + const dataColumns: ConfigColumns[] = [ { title: 'No', render: ( - data: unknown, - type: unknown, + _data: unknown, + _type: unknown, row: TCategoryResponse, meta: { row: number }, ) => { @@ -46,7 +47,7 @@ export const CategoriesPage = () => { data: 'id', }, ] - const dataSlot = { + const dataSlot: DataTableSlots = { 1: (_value: unknown, _type: unknown, data: TCategoryResponse) => (
{data.name}
@@ -64,7 +65,7 @@ export const CategoriesPage = () => { ), } - const dataOptions = { + const dataOptions: Config = { paging: true, searching: true, ordering: true, diff --git a/app/pages/dashboard-contents/index.tsx b/app/pages/dashboard-contents/index.tsx index f6092fc..fb13630 100644 --- a/app/pages/dashboard-contents/index.tsx +++ b/app/pages/dashboard-contents/index.tsx @@ -1,5 +1,5 @@ -import DT from 'datatables.net-dt' -import DataTable from 'datatables.net-react' +import DT, { type Config, type ConfigColumns } from 'datatables.net-dt' +import DataTable, { type DataTableSlots } from 'datatables.net-react' import { Link, useRouteLoaderData } from 'react-router' import type { TCategoryResponse } from '~/apis/common/get-categories' @@ -17,16 +17,17 @@ export const ContentsPage = () => { ) DataTable.use(DT) - const dataTable = loaderData?.newsData?.sort( - (a, b) => new Date(b.live_at).getTime() - new Date(a.live_at).getTime(), - ) - const dataColumns = [ + const dataTable = + loaderData?.newsData?.sort( + (a, b) => new Date(b.live_at).getTime() - new Date(a.live_at).getTime(), + ) || [] + const dataColumns: ConfigColumns[] = [ { title: 'No', render: ( - data: unknown, - type: unknown, - row: unknown, + _data: unknown, + _type: unknown, + _row: unknown, meta: { row: number }, ) => { return meta.row + 1 @@ -54,7 +55,7 @@ export const ContentsPage = () => { data: 'slug', }, ] - const dataSlot = { + const dataSlot: DataTableSlots = { 1: (value: string) => formatDate(value), 2: (_value: unknown, _type: unknown, data: TNewsResponse) => (
@@ -62,6 +63,7 @@ export const ContentsPage = () => {
ID: {data.id.slice(0, 8)}
), + 3: (value: string) => {value}, 4: (value: TCategoryResponse[]) => (
{value.map((item) => item.name).join(', ')}
), @@ -89,7 +91,7 @@ export const ContentsPage = () => { ), } - const dataOptions = { + const dataOptions: Config = { paging: true, searching: true, ordering: true, @@ -98,7 +100,7 @@ export const ContentsPage = () => { return (
- +
{/* TODO: Filter */}
) diff --git a/app/pages/dashboard-tags/index.tsx b/app/pages/dashboard-tags/index.tsx index 8d6bddc..06225ed 100644 --- a/app/pages/dashboard-tags/index.tsx +++ b/app/pages/dashboard-tags/index.tsx @@ -1,5 +1,5 @@ -import DT from 'datatables.net-dt' -import DataTable from 'datatables.net-react' +import DT, { type Config, type ConfigColumns } from 'datatables.net-dt' +import DataTable, { type DataTableSlots } from 'datatables.net-react' import { Link, useRouteLoaderData } from 'react-router' import { Button } from '~/components/ui/button' @@ -14,13 +14,13 @@ export const TagsPage = () => { const { tagsData: dataTable } = loaderData || {} DataTable.use(DT) - const dataColumns = [ + const dataColumns: ConfigColumns[] = [ { title: 'No', render: ( - data: unknown, - type: unknown, - row: unknown, + _data: unknown, + _type: unknown, + _row: unknown, meta: { row: number }, ) => { return meta.row + 1 @@ -39,15 +39,7 @@ export const TagsPage = () => { data: 'id', }, ] - - const dataOptions = { - paging: true, - searching: true, - ordering: true, - info: true, - } - - const dataSlot = { + const dataSlot: DataTableSlots = { 3: (value: string) => ( ), } + const dataOptions: Config = { + paging: true, + searching: true, + ordering: true, + info: true, + } + return (
@@ -75,7 +74,7 @@ export const TagsPage = () => {
{ containerClassName="flex-1" disabled={!!newsData} /> - { label="Konten" placeholder="Masukkan Konten" className="shadow" - inputClassName="bg-white focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none border-0" + inputClassName="bg-white focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none border-0 min-h-[42px]" labelClassName="text-sm font-medium text-[#363636]" category="content" /> diff --git a/app/pages/news-detail/index.tsx b/app/pages/news-detail/index.tsx index 84fb4c6..8bcd236 100644 --- a/app/pages/news-detail/index.tsx +++ b/app/pages/news-detail/index.tsx @@ -29,9 +29,7 @@ export const NewsDetailPage = () => {
-

- {title} -

+

{title}

{ description: loaderData?.beritaCategory?.description || '', items: loaderData?.beritaNews || [], } + const kajian: TNews = { + title: loaderData?.kajianCategory?.name || '', + description: loaderData?.kajianCategory?.description || '', + items: loaderData?.kajianNews || [], + } return (
@@ -32,9 +37,9 @@ export const NewsPage = () => { - {/* - - */} + + +
) } diff --git a/app/routes/_news._index.tsx b/app/routes/_news._index.tsx index 3ac1ba8..6b1e187 100644 --- a/app/routes/_news._index.tsx +++ b/app/routes/_news._index.tsx @@ -6,17 +6,33 @@ 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 kajianCode = 'kajian' + const kajianCategory = categoriesData.find( + (category) => category.code === kajianCode, + ) + const { data: kajianNews } = await getNews({ categories: [kajianCode] }) + + return { + spotlightCategory, + spotlightNews, + beritaCategory, + beritaNews, + kajianCategory, + kajianNews, + } } const NewsIndexLayout = () => diff --git a/package.json b/package.json index 5f93d18..f94c5fa 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "class-variance-authority": "^0.7.1", "datatables.net-dt": "^2.2.2", "datatables.net-react": "^1.0.0", + "embla-carousel-autoplay": "^8.5.2", "embla-carousel-react": "^8.5.2", "html-react-parser": "^5.2.2", "isbot": "^5.1.17", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c10c9f..973cf7c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,6 +65,9 @@ importers: datatables.net-react: specifier: ^1.0.0 version: 1.0.0 + embla-carousel-autoplay: + specifier: ^8.5.2 + version: 8.5.2(embla-carousel@8.5.2) embla-carousel-react: specifier: ^8.5.2 version: 8.5.2(react@19.0.0) @@ -2331,6 +2334,11 @@ packages: electron-to-chromium@1.5.90: resolution: {integrity: sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug==} + embla-carousel-autoplay@8.5.2: + resolution: {integrity: sha512-27emJ0px3q/c0kCHCjwRrEbYcyYUPfGO3g5IBWF1i7714TTzE6L9P81V6PHLoSMAKJ1aHoT2e7YFOsuFKCbyag==} + peerDependencies: + embla-carousel: 8.5.2 + embla-carousel-react@8.5.2: resolution: {integrity: sha512-Tmx+uY3MqseIGdwp0ScyUuxpBgx5jX1f7od4Cm5mDwg/dptEiTKf9xp6tw0lZN2VA9JbnVMl/aikmbc53c6QFA==} peerDependencies: @@ -6990,6 +6998,10 @@ snapshots: electron-to-chromium@1.5.90: {} + embla-carousel-autoplay@8.5.2(embla-carousel@8.5.2): + dependencies: + embla-carousel: 8.5.2 + embla-carousel-react@8.5.2(react@19.0.0): dependencies: embla-carousel: 8.5.2