qudoco-fe/components/main/dashboard/dashboard-container.tsx

482 lines
16 KiB
TypeScript

"use client";
import Cookies from "js-cookie";
import Link from "next/link";
import { useEffect, useState } from "react";
import { Article } from "@/types/globals";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import "react-datepicker/dist/react-datepicker.css";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { Calendar } from "@/components/ui/calendar";
import ApexChartColumn from "@/components/main/dashboard/chart/column-chart";
import CustomPagination from "@/components/layout/custom-pagination";
import { motion } from "framer-motion";
import { Input } from "@/components/ui/input";
import {
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
Select,
} from "@/components/ui/select";
import { Badge } from "lucide-react";
type ArticleData = Article & {
no: number;
createdAt: string;
};
interface TopPages {
id: number;
no: number;
title: string;
viewCount: number;
}
interface PostCount {
userLevelId: number;
no: number;
userLevelName: string;
totalArticle: number;
}
export default function DashboardContainer() {
const [roleName, setRoleName] = useState<string | undefined>();
useEffect(() => {
const role = Cookies.get("roleName");
setRoleName(role);
}, []);
const username = Cookies.get("username");
const fullname = Cookies.get("ufne");
const [page, setPage] = useState(1);
const [totalPage, setTotalPage] = useState(1);
const [topPagesTotalPage, setTopPagesTotalPage] = useState(1);
const [article, setArticle] = useState<ArticleData[]>([]);
// const [analyticsView, setAnalyticView] = useState<string[]>(["comment", "view", "share"]);
// const [startDateValue, setStartDateValue] = useState(parseDate(convertDateFormatNoTimeV2(new Date())));
// const [postContentDate, setPostContentDate] = useState({
// startDate: parseDate(convertDateFormatNoTimeV2(new Date(new Date().setDate(new Date().getDate() - 7)))),
// endDate: parseDate(convertDateFormatNoTimeV2(new Date())),
// });
const [startDateValue, setStartDateValue] = useState(new Date());
const [analyticsView, setAnalyticView] = useState<string[]>([]);
const options = [
{ label: "Comment", value: "comment" },
{ label: "View", value: "view" },
{ label: "Share", value: "share" },
];
const handleChange = (value: string, checked: boolean) => {
if (checked) {
setAnalyticView([...analyticsView, value]);
} else {
setAnalyticView(analyticsView.filter((v) => v !== value));
}
};
const [postContentDate, setPostContentDate] = useState({
startDate: new Date(new Date().setDate(new Date().getDate() - 7)),
endDate: new Date(),
});
const [typeDate, setTypeDate] = useState("monthly");
const [summary, setSummary] = useState<any>();
const [topPages, setTopPages] = useState<TopPages[]>([]);
const [postCount, setPostCount] = useState<PostCount[]>([]);
// useEffect(() => {
// fetchSummary();
// }, []);
// useEffect(() => {
// initState();
// }, [page]);
// async function initState() {
// const req = {
// limit: "5",
// page: page,
// search: "",
// };
// const res = await getListArticle(req);
// setArticle(res.data?.data);
// setTotalPage(res?.data?.meta?.totalPage);
// }
// async function fetchSummary() {
// const res = await getStatisticSummary();
// setSummary(res?.data?.data);
// }
// useEffect(() => {
// fetchTopPages();
// }, [page]);
// async function fetchTopPages() {
// const req = {
// limit: "10",
// page: page,
// search: "",
// };
// const res = await getTopArticles(req);
// setTopPages(getTableNumber(10, res.data?.data));
// setTopPagesTotalPage(res?.data?.meta?.totalPage);
// }
// useEffect(() => {
// fetchPostCount();
// }, [postContentDate]);
// async function fetchPostCount() {
// const getDate = (data: any) => {
// return `${data.year}-${data.month < 10 ? `0${data.month}` : data.month}-${
// data.day < 10 ? `0${data.day}` : data.day
// }`;
// };
// const res = await getUserLevelDataStat(
// getDate(postContentDate.startDate),
// getDate(postContentDate.endDate)
// );
// setPostCount(getTableNumber(10, res?.data?.data));
// }
const getTableNumber = (limit: number, data: any) => {
if (data) {
const startIndex = limit * (page - 1);
let iterate = 0;
const newData = data.map((value: any) => {
iterate++;
value.no = startIndex + iterate;
return value;
});
return newData;
}
};
const getMonthYear = (date: any) => {
return date.month + " " + date.year;
};
const getMonthYearName = (date: any) => {
const newDate = new Date(date);
const months = [
"Januari",
"Februari",
"Maret",
"April",
"Mei",
"Juni",
"Juli",
"Agustus",
"September",
"Oktober",
"November",
"Desember",
];
const year = newDate.getFullYear();
const month = months[newDate.getMonth()];
return month + " " + year;
};
if (!roleName) return null;
const AdminDashboard = () => {
const tasks = [
{
id: "1",
title: "MediaHUB Content Aggregator",
author: "John Kontributor",
category: "Product",
date: "2026-02-13",
status: "OPEN",
},
{
id: "2",
title:
"Mudik Nyaman Bersama Pertamina: Layanan 24 Jam, Motoris, dan Fasilitas Lengkap",
author: "Jane Kontributor",
category: "Service",
date: "2026-02-13",
status: "OPEN",
},
{
id: "3",
title: "Artifintel Services Update",
author: "Alex Approver",
category: "Event",
date: "2026-02-13",
status: "CLOSED",
},
];
return (
<div className="space-y-8">
{/* HEADER */}
<div>
<h1 className="text-2xl font-bold text-slate-800">Admin Dashboard</h1>
<p className="text-slate-500">
Review and manage content submissions
</p>
</div>
{/* STAT CARDS */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{[
{ title: "Open Tasks", value: 2, color: "bg-yellow-500" },
{ title: "Closed Tasks", value: 2, color: "bg-green-600" },
{ title: "Total Submissions", value: 4, color: "bg-blue-600" },
{ title: "Rejected", value: 7, color: "bg-red-600" },
].map((card, i) => (
<div
key={i}
className="bg-white rounded-2xl shadow border p-6 flex justify-between items-center"
>
<div>
<p className="text-sm text-slate-500">{card.title}</p>
<h2 className="text-3xl font-bold text-slate-800 mt-2">
{card.value}
</h2>
</div>
<div className={`w-12 h-12 rounded-xl ${card.color}`} />
</div>
))}
</div>
{/* TASK LIST */}
<div className="bg-white rounded-2xl shadow border p-6 space-y-6">
{/* Title */}
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold">
Task List{" "}
<span className="ml-2 text-xs bg-amber-100 text-amber-600 px-3 py-1 rounded-full">
{tasks.length} Tasks
</span>
</h2>
</div>
{/* Filters */}
<div className="flex flex-wrap gap-4">
<Input type="date" className="w-[180px]" />
<Input type="date" className="w-[180px]" />
<Select>
<SelectTrigger className="w-[150px]">
<SelectValue placeholder="Status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="open">Open</SelectItem>
<SelectItem value="closed">Closed</SelectItem>
</SelectContent>
</Select>
</div>
{/* Accordion List */}
<Accordion type="single" collapsible className="space-y-4">
{tasks.map((task) => (
<AccordionItem
key={task.id}
value={task.id}
className="border rounded-xl px-4"
>
<AccordionTrigger className="hover:no-underline">
<div className="flex items-center justify-between w-full">
<div className="text-left">
<p className="font-medium text-slate-800">{task.title}</p>
<p className="text-sm text-slate-500 mt-1">
{task.author} {task.category} {task.date}
</p>
</div>
<div className="flex items-center gap-4">
<Badge
className={
task.status === "OPEN"
? "bg-yellow-100 text-yellow-600"
: "bg-green-100 text-green-600"
}
>
{task.status}
</Badge>
{/* Mini Progress */}
<div className="flex gap-1">
<div className="w-6 h-1 bg-green-500 rounded" />
<div className="w-6 h-1 bg-yellow-500 rounded" />
<div className="w-6 h-1 bg-gray-300 rounded" />
<div className="w-6 h-1 bg-gray-300 rounded" />
</div>
</div>
</div>
</AccordionTrigger>
<AccordionContent>
<div className="mt-4 bg-slate-50 rounded-xl border p-6 space-y-6">
{/* Title */}
<div className="flex items-center gap-2">
<div className="w-5 h-5 rounded-full border-2 border-amber-500 flex items-center justify-center">
<div className="w-2 h-2 bg-amber-500 rounded-full" />
</div>
<h3 className="font-semibold text-slate-700">
Document Flow Status
</h3>
</div>
{/* Stepper */}
<div className="relative">
{/* Line */}
<div className="absolute top-5 left-0 right-0 h-[2px] bg-slate-200" />
<div className="relative grid grid-cols-4 text-center">
{/* STEP 1 */}
<div className="flex flex-col items-center">
<div className="w-10 h-10 rounded-full bg-green-500 flex items-center justify-center text-white">
</div>
<p className="mt-2 text-sm font-medium text-slate-700">
Submission
</p>
<p className="text-xs text-slate-500">
Feb 13, 2026 09:00
</p>
</div>
{/* STEP 2 */}
<div className="flex flex-col items-center">
<div className="w-10 h-10 rounded-full bg-amber-500 flex items-center justify-center text-white">
</div>
<p className="mt-2 text-sm font-medium text-slate-700">
Technical Review
</p>
<p className="text-xs text-slate-500">
Feb 13, 2026 11:00
</p>
</div>
{/* STEP 3 */}
<div className="flex flex-col items-center">
<div className="w-10 h-10 rounded-full border-2 border-slate-300 bg-white" />
<p className="mt-2 text-sm font-medium text-slate-400">
Admin Verification
</p>
<p className="text-xs text-slate-400">Waiting</p>
</div>
{/* STEP 4 */}
<div className="flex flex-col items-center">
<div className="w-10 h-10 rounded-full border-2 border-slate-300 bg-white" />
<p className="mt-2 text-sm font-medium text-slate-400">
Final Approval
</p>
<p className="text-xs text-slate-400">Waiting</p>
</div>
</div>
</div>
</div>
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
</div>
);
};
const ContributorDashboard = () => {
return (
<div className="space-y-8">
<div>
<h1 className="text-2xl font-bold text-slate-800">Dashboard</h1>
</div>
{/* STAT CARDS */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{[
{
title: "Total Content",
value: 24,
color: "bg-blue-600",
growth: "+12%",
},
{
title: "Pending Approval",
value: 8,
color: "bg-yellow-500",
growth: "+3",
},
{
title: "Published",
value: 16,
color: "bg-green-600",
growth: "+5",
},
{
title: "Rejected",
value: 2,
color: "bg-red-600",
growth: "-1",
},
].map((card, i) => (
<div
key={i}
className="bg-white rounded-2xl shadow border p-6 flex justify-between items-center"
>
<div>
<p className="text-sm text-slate-500">{card.title}</p>
<h2 className="text-3xl font-bold text-slate-800 mt-2">
{card.value}
</h2>
</div>
<div className="text-right">
<p className="text-sm text-green-600 font-medium">
{card.growth}
</p>
<div className={`w-10 h-10 rounded-xl mt-2 ${card.color}`} />
</div>
</div>
))}
</div>
{/* CONTENT + QUICK ACTIONS */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2 bg-white rounded-2xl shadow border p-6">
<h2 className="font-semibold mb-4">Recent Content</h2>
{/* isi list content di sini */}
</div>
<div className="bg-amber-700 text-white rounded-2xl shadow p-6 space-y-4">
<h2 className="font-semibold">Quick Actions</h2>
<button className="w-full bg-amber-600 py-2 rounded-lg">
+ Create New Article
</button>
<button className="w-full bg-amber-600 py-2 rounded-lg">
+ Update Product
</button>
<button className="w-full bg-white text-amber-700 py-2 rounded-lg">
View All Actions
</button>
</div>
</div>
</div>
);
};
return (
<>{roleName === "Admin" ? <AdminDashboard /> : <ContributorDashboard />}</>
);
}