Compare commits

..

4 Commits

5 changed files with 341 additions and 56 deletions

227
app/components/ui/chart.tsx Normal file
View File

@ -0,0 +1,227 @@
import {
Chart as ChartJS,
ArcElement,
Tooltip,
Legend,
CategoryScale,
LinearScale,
BarElement,
Title,
type ChartOptions,
type ChartEvent,
type ActiveElement,
} from 'chart.js'
import { useState } from 'react'
import { Bar, Doughnut, Pie } from 'react-chartjs-2'
ChartJS.register(
ArcElement,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
)
type HandleChartClick = (event: ChartEvent, elements: ActiveElement[]) => void
export const UiChartPie = () => {
const data = {
labels: [
'Pidana',
'Perdata',
'Perceraian',
'Surat Bisnis',
'Surat Tanah',
'Lainnya',
],
datasets: [
{
data: [33.7, 13, 22.8, 9.3, 9.3, 21.2],
backgroundColor: [
'#FFB300',
'#4CAF50',
'#3F51B5',
'#F44336',
'#2196F3',
'#FF9800',
],
hoverOffset: 4,
},
],
}
const options: ChartOptions<'pie'> = {
maintainAspectRatio: true,
responsive: false,
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
pointStyle: 'circle',
padding: 20,
},
},
},
layout: {
padding: 0,
},
}
return (
<div className="h-[300px] w-full items-center justify-center rounded-lg bg-white p-5 text-center">
<h2 className="text-xl font-bold">Top 5 Konten</h2>
<Pie
height={225}
width={450}
data={data}
options={options}
/>
</div>
)
}
export const UiChartBar = () => {
const yearlyData = {
labels: ['2022', '2023', '2024'],
datasets: [
{
label: 'Total Sales',
data: [800, 950, 1200],
backgroundColor: '#1E3A8A',
},
],
}
const monthlyData = {
labels: [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
],
datasets: [
{
label: 'Monthly Sales',
data: [70, 90, 750, 80, 90, 95, 75, 85, 78, 88, 95, 0],
backgroundColor: '#2E2F7C',
},
],
}
const [view, setView] = useState('month') // Default tampil bulanan
const [selectedYear, setSelectedYear] = useState('')
const handleChartClick: HandleChartClick = (event, elements) => {
if (elements.length > 0 && view === 'year') {
const clickedIndex = elements[0].index
const year = yearlyData.labels[clickedIndex]
setSelectedYear(year)
setView('month')
}
}
const handleBackToYear = () => {
setView('year')
setSelectedYear('')
}
const options = {
responsive: true,
maintainAspectRatio: false,
onClick: handleChartClick,
scales: {
y: {
beginAtZero: true,
max: view == 'month' ? 100 : 1500,
ticks: {
stepSize: 25,
},
},
},
}
return (
<div className="rounded-xl bg-white p-6 shadow-lg">
<div className="mb-4 flex items-center justify-between">
<h2 className="text-xl font-bold">
{view === 'year'
? 'Penjualan Tahunan'
: `Penjualan Bulanan ${selectedYear || '2024'}`}
</h2>
{view === 'month' && (
<button
className="rounded-lg bg-gray-200 px-4 py-2"
onClick={handleBackToYear}
>
Tahun
</button>
)}
</div>
<div>
<Bar
data={view === 'year' ? yearlyData : monthlyData}
options={options}
/>
</div>
</div>
)
}
export const ChartSubscription = () => {
const data = {
labels: ['Selesai', 'Belum Selesai'],
datasets: [
{
data: [70, 30],
backgroundColor: ['#1e3a8a', '#e5e7eb'],
borderWidth: 0,
cutout: '70%',
circumference: 180,
rotation: 270,
},
],
}
const options: ChartOptions<'doughnut'> = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
pointStyle: 'circle',
boxWidth: 10,
},
},
tooltip: {
enabled: false,
},
},
}
return (
<div className="rounded-xl bg-white p-6 shadow-lg">
<h2 className="mb-4 text-[20px]">Subscription Selesai</h2>
<div className="flex items-center justify-between">
<div style={{ height: '200px', width: '100%' }}>
<Doughnut
data={data}
options={options}
/>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,21 @@
type TSubscriptions = {
id: number
createdAt: string
date: string
name: string
email: string
category: string
status: string
}
export const CONTENTS: TSubscriptions[] = [
{
id: 1,
createdAt: '24/10/2024',
date: '24/10/2024',
name: 'John Doe',
email: 'test@test.com',
category: 'Education',
status: 'Published',
},
]

View File

@ -0,0 +1,36 @@
import DT from 'datatables.net-dt'
import DataTable from 'datatables.net-react'
import { TitleDashboard } from '~/components/ui/title-dashboard'
import { CONTENTS } from './data'
export const SubscriptionsPage = () => {
DataTable.use(DT)
const columns = [
{ title: 'No', data: 'id' },
{ title: 'Tanggal Subscribe', data: 'date' },
{ title: 'Nama User', data: 'name' },
{ title: 'Email', data: 'email' },
{ title: 'Kategori', data: 'category' },
{ title: 'Status', data: 'status' },
{ title: 'Action', data: 'id', render: () => 'Subscribed' },
]
return (
<div className="relative">
<TitleDashboard title="Subscription" />
<DataTable
className="cell-border"
data={CONTENTS}
columns={columns}
options={{
paging: true,
searching: true,
ordering: true,
info: true,
}}
></DataTable>
</div>
)
}

View File

@ -1,68 +1,67 @@
import { CardReport } from '~/components/ui/card-report' import { CardReport } from '~/components/ui/card-report'
import {
ChartSubscription,
UiChartBar,
UiChartPie,
} from '~/components/ui/chart'
import { HISTORY, REPORT } from './data' import { HISTORY, REPORT } from './data'
export const DashboardPage = () => { export const DashboardPage = () => {
return ( return (
<div className="relative"> <div className="relative">
<div className="container mx-auto"> <section className="mb-5 flex items-center justify-between">
<section className="mb-5 flex items-center justify-between"> <h1 className="text-xl font-bold">Dashboard</h1>
<h1 className="text-xl font-bold">Dashboard</h1> <div className="flex items-center gap-2">
<div className="flex items-center gap-2"> <span>Tanggal:</span>
<span>Tanggal:</span> <input
<input type="date"
type="date" className="rounded border p-2"
className="rounded border p-2" />
/> <input
<input type="date"
type="date" className="rounded border p-2"
className="rounded border p-2" />
/>
</div>
</section>
<div className="mb-5 grid grid-cols-3 gap-4">
{REPORT.map(({ title, amount, icon }, index) => (
<CardReport
key={index}
title={title}
amount={amount}
icon={icon}
/>
))}
</div> </div>
</section>
<div className="mt-5 grid grid-cols-1 grid-rows-1 gap-6 sm:grid-cols-3">
{REPORT.map(({ title, amount, icon }, index) => (
<CardReport
key={index}
title={title}
amount={amount}
icon={icon}
/>
))}
</div>
<div className="grid grid-cols-3 gap-x-4 gap-y-4"> <div className="mt-5 grid grid-cols-1 gap-6 sm:grid-cols-3 sm:grid-rows-2">
<div className="grid gap-y-4"> {HISTORY.map(({ title, amount, icon, counter }, index) => (
{HISTORY.map(({ title, amount, icon, counter }, index) => ( <CardReport
<CardReport key={index}
key={index} title={title}
title={title} amount={amount}
amount={amount} icon={icon}
icon={icon} counter={counter}
counter={counter} />
/> ))}
))} <div className="max-h-[300px] sm:col-span-2 sm:col-start-2 sm:row-span-2 sm:row-start-1">
</div> <UiChartPie />
<div className="col-span-2"> </div>
<img </div>
src="/images/dummy-chart-2.svg"
alt="" <div className="mt-5 grid max-h-[280px] grid-cols-1 grid-rows-2 gap-6 sm:grid-cols-5 sm:grid-rows-1">
/> <div className="sm:col-span-3">
<div className="h-30 w-full">
<div className="shadow-sm">
<UiChartBar />
</div>
</div> </div>
</div> </div>
<div className="sm:col-span-2 sm:col-start-4">
<div className="mt-5 flex justify-between gap-5"> <div className="h-30 w-full">
<div className="w-[600px] shadow-sm"> <div className="shadow-sm">
<img <ChartSubscription />
src="/images/dummy-chart-1.svg" </div>
alt=""
/>
</div>
<div className="w-[480px] shadow-sm">
<img
src="/images/dummy-chart-3.svg"
alt=""
/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,4 +1,6 @@
import { SubscriptionsPage } from '~/pages/dashboard-subscriptions'
const DashboardSubscriptionsLayout = () => { const DashboardSubscriptionsLayout = () => {
return <div>Subscriptions Page</div> return <SubscriptionsPage />
} }
export default DashboardSubscriptionsLayout export default DashboardSubscriptionsLayout