2025-07-13 07:48:15 +00:00
|
|
|
|
"use client";
|
|
|
|
|
|
import Cookies from "js-cookie";
|
|
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
|
|
import { Article } from "@/types/globals";
|
|
|
|
|
|
import {
|
|
|
|
|
|
getListArticle,
|
|
|
|
|
|
getStatisticSummary,
|
|
|
|
|
|
getTopArticles,
|
|
|
|
|
|
getUserLevelDataStat,
|
|
|
|
|
|
} from "@/service/article";
|
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
|
import "react-datepicker/dist/react-datepicker.css";
|
|
|
|
|
|
import { motion } from "framer-motion";
|
2026-01-07 08:06:07 +00:00
|
|
|
|
import {
|
|
|
|
|
|
Blocks,
|
|
|
|
|
|
Check,
|
|
|
|
|
|
CheckCircle,
|
|
|
|
|
|
CheckCircle2,
|
|
|
|
|
|
Eye,
|
|
|
|
|
|
Info,
|
|
|
|
|
|
TimerIcon,
|
|
|
|
|
|
Upload,
|
|
|
|
|
|
} from "lucide-react";
|
|
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
|
|
|
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
|
|
|
|
import {
|
|
|
|
|
|
Table,
|
|
|
|
|
|
TableBody,
|
|
|
|
|
|
TableCell,
|
|
|
|
|
|
TableHead,
|
|
|
|
|
|
TableHeader,
|
|
|
|
|
|
TableRow,
|
|
|
|
|
|
} from "@/components/ui/table";
|
|
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
|
|
|
|
import {
|
|
|
|
|
|
Dialog,
|
|
|
|
|
|
DialogClose,
|
|
|
|
|
|
DialogContent,
|
|
|
|
|
|
DialogFooter,
|
|
|
|
|
|
DialogTitle,
|
|
|
|
|
|
} from "@/components/ui/dialog";
|
2025-07-13 07:48:15 +00:00
|
|
|
|
|
|
|
|
|
|
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 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[]>([]);
|
2026-01-07 08:06:07 +00:00
|
|
|
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
|
|
|
|
const [selectedItem, setSelectedItem] = useState<any>(null);
|
2025-07-13 07:48:15 +00:00
|
|
|
|
const options = [
|
|
|
|
|
|
{ label: "Comment", value: "comment" },
|
|
|
|
|
|
{ label: "View", value: "view" },
|
|
|
|
|
|
{ label: "Share", value: "share" },
|
|
|
|
|
|
];
|
2026-01-07 08:06:07 +00:00
|
|
|
|
|
|
|
|
|
|
const activities = [
|
|
|
|
|
|
{
|
|
|
|
|
|
no: 1,
|
|
|
|
|
|
tanggal: "10/11/2024",
|
|
|
|
|
|
jenis: "Banner",
|
|
|
|
|
|
judul: "New Promo JAECOO",
|
|
|
|
|
|
status: "Menunggu",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
no: 2,
|
|
|
|
|
|
tanggal: "10/11/2024",
|
|
|
|
|
|
jenis: "Agen",
|
|
|
|
|
|
judul: "Foto Budi Santoso",
|
|
|
|
|
|
status: "Disetujui",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
no: 3,
|
|
|
|
|
|
tanggal: "09/11/2024",
|
|
|
|
|
|
jenis: "Produk",
|
|
|
|
|
|
judul: "JAECOO J7 AWD Update",
|
|
|
|
|
|
status: "Disetujui",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
no: 4,
|
|
|
|
|
|
tanggal: "09/11/2024",
|
|
|
|
|
|
jenis: "Dealer",
|
|
|
|
|
|
judul: "Dealer Jakarta Selatan",
|
|
|
|
|
|
status: "Disetujui",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
no: 5,
|
|
|
|
|
|
tanggal: "08/11/2024",
|
|
|
|
|
|
jenis: "Dokumen",
|
|
|
|
|
|
judul: "Brosur JAECOO J8",
|
|
|
|
|
|
status: "Ditolak",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
no: 6,
|
|
|
|
|
|
tanggal: "08/11/2024",
|
|
|
|
|
|
jenis: "Banner",
|
|
|
|
|
|
judul: "Hero Banner Akhir Tahun",
|
|
|
|
|
|
status: "Menunggu",
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const notifications = [
|
|
|
|
|
|
{
|
|
|
|
|
|
icon: "✅",
|
|
|
|
|
|
text: 'Upload "JAECOO J7 AWD Update" telah disetujui oleh Admin Manager',
|
|
|
|
|
|
time: "2 jam yang lalu",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
icon: "❌",
|
|
|
|
|
|
text: 'Upload "Brosur JAECOO J8" ditolak. Alasan: Resolusi gambar terlalu rendah.',
|
|
|
|
|
|
time: "2 jam yang lalu",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
icon: "✅",
|
|
|
|
|
|
text: 'Update "Dealer Jakarta Selatan" telah disetujui',
|
|
|
|
|
|
time: "1 hari yang lalu",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
icon: "✅",
|
|
|
|
|
|
text: 'Upload "Foto Budi Santoso" telah disetujui',
|
|
|
|
|
|
time: "1 hari yang lalu",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
icon: "ℹ️",
|
|
|
|
|
|
text: "Sistem akan maintenance pada Minggu, 12 November 2024 pukul 00.00 - 04.00 WIB",
|
|
|
|
|
|
time: "1 hari yang lalu",
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
2025-07-13 07:48:15 +00:00
|
|
|
|
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: "4",
|
|
|
|
|
|
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;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="space-y-8">
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div className="pl-3">
|
|
|
|
|
|
<h1 className="text-[#1F6779] text-2xl font-semibold">
|
|
|
|
|
|
Dashboard Utama
|
|
|
|
|
|
</h1>
|
|
|
|
|
|
<p>Ringkasan status aktivitas dan upload anda</p>
|
|
|
|
|
|
</div>
|
2025-07-13 07:48:15 +00:00
|
|
|
|
{/* Stats Cards */}
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-8 gap-6">
|
2025-07-13 07:48:15 +00:00
|
|
|
|
{/* User Profile Card */}
|
|
|
|
|
|
<motion.div
|
|
|
|
|
|
className="col-span-1 md:col-span-2 bg-white rounded-2xl shadow-lg border border-slate-200/60 p-6 hover:shadow-xl transition-all duration-300"
|
|
|
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
|
|
|
transition={{ delay: 0.1 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="flex justify-between items-start">
|
|
|
|
|
|
<div className="space-y-2">
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<h3 className="text-xl font-bold text-slate-800">
|
|
|
|
|
|
Total Upload Hari ini
|
|
|
|
|
|
</h3>
|
|
|
|
|
|
<p className="text-slate-600 text-lg">2</p>
|
|
|
|
|
|
{/* <div className="flex space-x-6 pt-2">
|
2025-07-13 07:48:15 +00:00
|
|
|
|
<div className="text-center">
|
|
|
|
|
|
<p className="text-2xl font-bold text-blue-600">
|
|
|
|
|
|
{summary?.totalToday}
|
|
|
|
|
|
</p>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<p className="text-sm text-slate-500">2</p>
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
</div> */}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div className="p-3 bg-gradient-to-br from-blue-50 to-purple-50 rounded-xl">
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<Upload size={50} className="text-black" />
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</motion.div>
|
|
|
|
|
|
<motion.div
|
2026-01-07 08:06:07 +00:00
|
|
|
|
className="col-span-1 md:col-span-2 bg-white rounded-2xl shadow-lg border border-slate-200/60 p-6 hover:shadow-xl transition-all duration-300"
|
2025-07-13 07:48:15 +00:00
|
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
2026-01-07 08:06:07 +00:00
|
|
|
|
transition={{ delay: 0.1 }}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div className="flex justify-between items-start">
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<h3 className="text-xl font-bold text-slate-800">
|
|
|
|
|
|
Menunggu Persetujuan
|
|
|
|
|
|
</h3>
|
|
|
|
|
|
<p className="text-slate-600 text-lg">2</p>
|
|
|
|
|
|
{/* <div className="flex space-x-6 pt-2">
|
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
|
<p className="text-2xl font-bold text-blue-600">
|
|
|
|
|
|
{summary?.totalToday}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p className="text-sm text-slate-500">2</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div> */}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div className="p-3 bg-gradient-to-br from-yellow-100 to-yellow-100 rounded-xl">
|
|
|
|
|
|
<TimerIcon size={50} className="text-black" />
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</motion.div>
|
|
|
|
|
|
<motion.div
|
2026-01-07 08:06:07 +00:00
|
|
|
|
className="col-span-1 md:col-span-2 bg-white rounded-2xl shadow-lg border border-slate-200/60 p-6 hover:shadow-xl transition-all duration-300"
|
2025-07-13 07:48:15 +00:00
|
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
2026-01-07 08:06:07 +00:00
|
|
|
|
transition={{ delay: 0.1 }}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div className="flex justify-between items-start">
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<h3 className="text-xl font-bold text-slate-800">Disetujui</h3>
|
|
|
|
|
|
<p className="text-slate-600 text-lg">2</p>
|
|
|
|
|
|
{/* <div className="flex space-x-6 pt-2">
|
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
|
<p className="text-2xl font-bold text-blue-600">
|
|
|
|
|
|
{summary?.totalToday}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p className="text-sm text-slate-500">2</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div> */}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div className="p-3 bg-gradient-to-br from-green-100 to-green-100 rounded-xl">
|
|
|
|
|
|
<CheckCircle size={50} className="text-black" />
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</motion.div>
|
|
|
|
|
|
<motion.div
|
2026-01-07 08:06:07 +00:00
|
|
|
|
className="col-span-1 md:col-span-2 bg-white rounded-2xl shadow-lg border border-slate-200/60 p-6 hover:shadow-xl transition-all duration-300"
|
2025-07-13 07:48:15 +00:00
|
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
2026-01-07 08:06:07 +00:00
|
|
|
|
transition={{ delay: 0.1 }}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div className="flex justify-between items-start">
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<h3 className="text-xl font-bold text-slate-800">Ditolak</h3>
|
|
|
|
|
|
<p className="text-slate-600 text-lg">2</p>
|
|
|
|
|
|
{/* <div className="flex space-x-6 pt-2">
|
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
|
<p className="text-2xl font-bold text-blue-600">
|
|
|
|
|
|
{summary?.totalToday}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p className="text-sm text-slate-500">2</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div> */}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div className="p-3 bg-gradient-to-br from-red-100 to-red-100 rounded-xl">
|
|
|
|
|
|
<Blocks size={50} className="text-black" />
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</motion.div>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
</div>
|
2025-07-13 07:48:15 +00:00
|
|
|
|
|
2026-01-07 08:06:07 +00:00
|
|
|
|
{/* Content Section */}
|
|
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-[2fr_1fr] gap-8">
|
|
|
|
|
|
{/* Aktivitas Terakhir */}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
<motion.div
|
2026-01-07 08:06:07 +00:00
|
|
|
|
initial={{ opacity: 0, x: -20 }}
|
|
|
|
|
|
animate={{ opacity: 1, x: 0 }}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
transition={{ delay: 0.5 }}
|
|
|
|
|
|
>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div className="bg-white shadow-xl ">
|
|
|
|
|
|
<p className=" text-lg p-3 bg-cyan-900 text-white rounded-t-lg">
|
|
|
|
|
|
Aktivitas Terakhir
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<Table className="p-3">
|
|
|
|
|
|
<TableHeader className="bg-gradient-to-r from-[#BCD4DF] to-[#BCD4DF]">
|
|
|
|
|
|
<TableRow>
|
|
|
|
|
|
<TableHead className="text-[#008080] w-[40px]">No</TableHead>
|
|
|
|
|
|
<TableHead className="text-[#008080]">Tanggal</TableHead>
|
|
|
|
|
|
<TableHead className="text-[#008080]">Jenis Konten</TableHead>
|
|
|
|
|
|
<TableHead className="text-[#008080]">Judul / Nama</TableHead>
|
|
|
|
|
|
<TableHead className="text-[#008080]">Status</TableHead>
|
|
|
|
|
|
<TableHead className="text-[#008080] text-center">
|
|
|
|
|
|
Aksi
|
|
|
|
|
|
</TableHead>
|
|
|
|
|
|
</TableRow>
|
|
|
|
|
|
</TableHeader>
|
|
|
|
|
|
<TableBody>
|
|
|
|
|
|
{activities.map((item) => (
|
|
|
|
|
|
<TableRow key={item.no}>
|
|
|
|
|
|
<TableCell>{item.no}</TableCell>
|
|
|
|
|
|
<TableCell>{item.tanggal}</TableCell>
|
|
|
|
|
|
<TableCell>
|
|
|
|
|
|
<Badge
|
|
|
|
|
|
variant="secondary"
|
|
|
|
|
|
className="bg-slate-100 text-slate-700"
|
|
|
|
|
|
>
|
|
|
|
|
|
{item.jenis}
|
|
|
|
|
|
</Badge>
|
|
|
|
|
|
</TableCell>
|
|
|
|
|
|
<TableCell className="font-medium">{item.judul}</TableCell>
|
|
|
|
|
|
<TableCell>
|
|
|
|
|
|
{item.status === "Disetujui" && (
|
|
|
|
|
|
<Badge className="bg-green-100 text-green-700">
|
|
|
|
|
|
Disetujui
|
|
|
|
|
|
</Badge>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{item.status === "Menunggu" && (
|
|
|
|
|
|
<Badge className="bg-yellow-100 text-yellow-700">
|
|
|
|
|
|
Menunggu
|
|
|
|
|
|
</Badge>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{item.status === "Ditolak" && (
|
|
|
|
|
|
<Badge className="bg-red-100 text-red-700">
|
|
|
|
|
|
Ditolak
|
|
|
|
|
|
</Badge>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</TableCell>
|
|
|
|
|
|
<TableCell
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setSelectedItem(item);
|
|
|
|
|
|
setDialogOpen(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
className="text-blue-600 font-medium text-center cursor-pointer hover:underline"
|
|
|
|
|
|
>
|
|
|
|
|
|
Lihat
|
|
|
|
|
|
</TableCell>
|
|
|
|
|
|
</TableRow>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</TableBody>
|
|
|
|
|
|
</Table>
|
|
|
|
|
|
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
|
|
|
|
|
<DialogContent className="max-w-lg rounded-xl p-0 overflow-hidden">
|
|
|
|
|
|
{selectedItem && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
{/* Header */}
|
|
|
|
|
|
<div className="bg-cyan-900 text-white p-4 flex flex-col items-start justify-between">
|
|
|
|
|
|
<DialogTitle className="text-lg font-semibold">
|
|
|
|
|
|
{selectedItem.judul}
|
|
|
|
|
|
</DialogTitle>
|
|
|
|
|
|
{selectedItem.status === "Disetujui" && (
|
|
|
|
|
|
<Badge className="bg-green-100 text-green-700">
|
|
|
|
|
|
Disetujui
|
|
|
|
|
|
</Badge>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{selectedItem.status === "Menunggu" && (
|
|
|
|
|
|
<Badge className="bg-yellow-100 text-yellow-700">
|
|
|
|
|
|
Menunggu
|
|
|
|
|
|
</Badge>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{selectedItem.status === "Ditolak" && (
|
|
|
|
|
|
<Badge className="bg-red-100 text-red-700">
|
|
|
|
|
|
Ditolak
|
|
|
|
|
|
</Badge>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Body */}
|
|
|
|
|
|
<div className="p-5 space-y-4">
|
|
|
|
|
|
<div className="grid grid-cols-2 gap-3 text-sm">
|
|
|
|
|
|
<div className="bg-slate-50 p-3 rounded-lg border">
|
|
|
|
|
|
<p className="text-slate-500">Tanggal Upload</p>
|
|
|
|
|
|
<p className="font-medium">{selectedItem.tanggal}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 p-3 rounded-lg border">
|
|
|
|
|
|
<p className="text-slate-500">Ukuran File</p>
|
|
|
|
|
|
<p className="font-medium">2.4 MB</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 p-3 rounded-lg border">
|
|
|
|
|
|
<p className="text-slate-500">Diupload Oleh</p>
|
|
|
|
|
|
<p className="font-medium">Operator1</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 p-3 rounded-lg border">
|
|
|
|
|
|
<p className="text-slate-500">Waktu Upload</p>
|
|
|
|
|
|
<p className="font-medium">14:32 WIB</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p className="font-medium text-slate-700 mb-2">
|
|
|
|
|
|
Preview Konten
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div className="border rounded-lg p-6 flex flex-col items-center justify-center bg-slate-50">
|
|
|
|
|
|
<Eye className="w-10 h-10 text-cyan-800 mb-2" />
|
|
|
|
|
|
<p className="font-medium text-cyan-900">
|
|
|
|
|
|
Preview File
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p className="text-sm text-slate-500">
|
|
|
|
|
|
File: {selectedItem.judul}.jpg
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p className="font-medium text-slate-700 mb-1">
|
|
|
|
|
|
Deskripsi
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div className="border rounded-lg p-3 bg-slate-50">
|
|
|
|
|
|
<p className="text-slate-700">
|
|
|
|
|
|
Upload {selectedItem.judul}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p className="font-medium text-slate-700 mb-2">
|
|
|
|
|
|
Status Timeline
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<div className="flex items-start space-x-2">
|
|
|
|
|
|
<CheckCircle2 className="w-5 h-5 text-green-500 mt-0.5" />
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p className="text-sm font-medium text-slate-800">
|
|
|
|
|
|
Upload Berhasil
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p className="text-xs text-slate-500">
|
|
|
|
|
|
10/11/2024 • 14:32 WIB
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{selectedItem.status === "Disetujui" && (
|
|
|
|
|
|
<div className="flex items-start space-x-2">
|
|
|
|
|
|
<CheckCircle2 className="w-5 h-5 text-green-500 mt-0.5" />
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p className="text-sm font-medium text-slate-800">
|
|
|
|
|
|
Disetujui oleh Approver
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p className="text-xs text-slate-500">
|
|
|
|
|
|
10/11/2024 • 16:45 WIB
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Footer */}
|
|
|
|
|
|
<DialogFooter className="bg-slate-100 p-4">
|
|
|
|
|
|
<DialogClose asChild>
|
|
|
|
|
|
<Button className="bg-slate-300 text-slate-700 hover:bg-slate-400">
|
|
|
|
|
|
Tutup
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</DialogClose>
|
|
|
|
|
|
</DialogFooter>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
<div className="mt-4 text-right p-3">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="link"
|
|
|
|
|
|
className="text-blue-600 font-medium p-3 h-auto"
|
|
|
|
|
|
>
|
|
|
|
|
|
Lihat Semua Aktivitas →
|
|
|
|
|
|
</Button>
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</motion.div>
|
|
|
|
|
|
|
2026-01-07 08:06:07 +00:00
|
|
|
|
{/* Notifikasi */}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
<motion.div
|
2026-01-07 08:06:07 +00:00
|
|
|
|
initial={{ opacity: 0, x: 20 }}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
animate={{ opacity: 1, x: 0 }}
|
|
|
|
|
|
transition={{ delay: 0.6 }}
|
|
|
|
|
|
>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<div>
|
|
|
|
|
|
<p className=" text-lg bg-cyan-900 p-3 rounded-t-lg text-white">
|
|
|
|
|
|
Notifikasi
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<ScrollArea className="h-96 pr-2">
|
|
|
|
|
|
<div className="">
|
|
|
|
|
|
{notifications.map((notif, i) => (
|
|
|
|
|
|
<div
|
|
|
|
|
|
key={i}
|
|
|
|
|
|
className="flex items-start space-x-3 border p-2 hover:bg-slate-50 transition"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="text-xl">{notif.icon}</div>
|
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
|
<p className="text-sm text-slate-700 leading-snug">
|
|
|
|
|
|
{notif.text}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p className="text-xs text-slate-400 mt-1">
|
|
|
|
|
|
{notif.time}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span className="w-2 h-2 bg-blue-500 rounded-full mt-2"></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</ScrollArea>
|
|
|
|
|
|
<div className="mt-2 text-right">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="link"
|
|
|
|
|
|
className="text-blue-600 font-medium p-0 h-auto"
|
|
|
|
|
|
>
|
|
|
|
|
|
Tandai Semua Dibaca
|
|
|
|
|
|
</Button>
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</motion.div>
|
|
|
|
|
|
|
2026-01-07 08:06:07 +00:00
|
|
|
|
{/* Informasi Penting */}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
<motion.div
|
2026-01-07 08:06:07 +00:00
|
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
2025-07-13 07:48:15 +00:00
|
|
|
|
transition={{ delay: 0.7 }}
|
2026-01-07 08:06:07 +00:00
|
|
|
|
className="lg:col-span-2"
|
2025-07-13 07:48:15 +00:00
|
|
|
|
>
|
2026-01-07 08:06:07 +00:00
|
|
|
|
<Card className="bg-blue-50 border border-blue-200">
|
|
|
|
|
|
<CardContent className="flex items-start space-x-3 p-4">
|
|
|
|
|
|
<Info className="text-blue-600 w-5 h-5 mt-0.5" />
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h4 className="text-blue-800 font-semibold text-sm mb-1">
|
|
|
|
|
|
Informasi Penting
|
|
|
|
|
|
</h4>
|
|
|
|
|
|
<p className="text-blue-700 text-sm">
|
|
|
|
|
|
Upload yang berstatus <b>"Menunggu"</b> akan direview oleh
|
|
|
|
|
|
Approver. Pastikan semua konten sudah sesuai panduan sebelum
|
|
|
|
|
|
upload untuk mempercepat proses approval.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</CardContent>
|
|
|
|
|
|
</Card>
|
2025-07-13 07:48:15 +00:00
|
|
|
|
</motion.div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|