web-humas-fe/components/main/dashboard/dashboard-container.tsx

717 lines
25 KiB
TypeScript

"use client";
import {
DashboardCommentIcon,
DashboardConnectIcon,
DashboardShareIcon,
DashboardSpeecIcon,
DashboardTopLeftPointIcon,
DashboardUserIcon,
} from "@/components/icons/dashboard-icon";
import { Submenu1Icon } from "@/components/icons/sidebar-icon";
import {
Accordion,
AccordionItem,
Button,
Calendar,
Checkbox,
CheckboxGroup,
Image,
Pagination,
Popover,
PopoverContent,
PopoverTrigger,
Select,
SelectItem,
SelectSection,
Skeleton,
} from "@heroui/react";
import ApexChartColumn from "./chart/column-chart";
import ApexChartDonut from "./chart/donut-chart";
import ApexChartLineArea from "./chart/line-area-chart";
import Cookies from "js-cookie";
import Link from "next/link";
import { useEffect, useState } from "react";
import {
getListArticle,
getListArticleAdminPage,
getStatisticSummary,
getTopArticles,
getUserLevelDataStat,
} from "@/services/article";
import { Article } from "@/types/globals";
import {
convertDateFormat,
convertDateFormatNoTime,
convertDateFormatNoTimeV2,
getUnixTimestamp,
textEllipsis,
} from "@/utils/global";
import { parseDate, getLocalTimeZone } from "@internationalized/date";
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 [topPagespage, setTopPagesPage] = 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 [topContentDate, setTopContentDate] = useState({
startDate: parseDate(
convertDateFormatNoTimeV2(
new Date(new Date().setDate(new Date().getDate() - 7))
)
),
endDate: parseDate(convertDateFormatNoTimeV2(new Date())),
});
const [typeDate, setTypeDate] = useState("monthly");
const [summary, setSummary] = useState<any>();
const [topPages, setTopPages] = useState<TopPages[]>([]);
const [postCount, setPostCount] = useState<PostCount[]>([]);
const [polresData, setPolresData] = useState<any>({});
const [selectedAccordion, setSelectedAccordion] = useState<any>(new Set([]));
const roleId = Cookies.get("urie");
useEffect(() => {
fetchSummary();
}, []);
useEffect(() => {
initState();
}, [page]);
async function initState() {
const req = {
limit: "4",
page: page,
search: "",
sort: "desc",
timeStamp: getUnixTimestamp(),
};
const res = await getListArticleAdminPage(req);
setArticle(res.data?.data);
setTotalPage(res?.data?.meta?.totalPage);
}
async function fetchSummary() {
const res = await getStatisticSummary(getUnixTimestamp());
setSummary(res?.data?.data);
}
useEffect(() => {
fetchTopPages();
}, [topPagespage, topContentDate]);
const getDate = (data: any) => {
if (data === null) {
return "";
} else {
return `${data.year}-${data.month < 10 ? `0${data.month}` : data.month}-${
data.day < 10 ? `0${data.day}` : data.day
}`;
}
};
async function fetchTopPages() {
const req = {
limit: "10",
page: topPagespage,
search: "",
sort: "desc",
isPublish: true,
timeStamp: getUnixTimestamp(),
startDate: getDate(topContentDate.startDate),
endDate: getDate(topContentDate.endDate),
};
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),
getUnixTimestamp()
);
setPostCount(getTableNumberStats(res?.data?.data));
}
const getTableNumber = (limit: number, data: any) => {
if (data) {
const startIndex = limit * (topPagespage - 1);
let iterate = 0;
const newData = data.map((value: any) => {
iterate++;
value.no = startIndex + iterate;
return value;
});
return newData;
}
};
const getTableNumberStats = (data: any) => {
if (data) {
let iterate = 0;
const newData = data
.filter(
(value: any) =>
value.userLevelName !== "SATWIL" && value.userLevelName !== "SATKER"
)
.map((value: any) => {
iterate++;
value.no = 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;
};
useEffect(() => {
const temp = Array.from(selectedAccordion);
console.log("selecette", temp);
if (temp.length > 0) {
for (const element of temp) {
getPolresData(Number(element));
}
}
}, [postContentDate]);
const getPolresData = async (id: number) => {
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),
getUnixTimestamp(),
id
);
const polresNowData = getTableNumberStats(res?.data?.data);
setPolresData((prev: any) => ({
...prev,
[id]: polresNowData,
}));
};
const handleSelectionChange = (e: any) => {
const prev = selectedAccordion;
const current = e;
const currentArray = Array.from(current);
const added = Number(currentArray.filter((item) => !prev.has(item))[0]);
setSelectedAccordion(current);
if (added) {
getPolresData(added);
}
};
return (
<div className="px-2 lg:p-8 flex justify-center">
<div className="w-full flex flex-col gap-6">
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center">
<div className="px-4 lg:px-8 py-4 justify-between w-full lg:w-[37%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col rounded-lg">
<div className="flex justify-between w-full items-center">
<div className="flex flex-col gap-2">
<p className="font-bold text-xl ">{fullname}</p>
<p>{username}</p>
</div>
<DashboardUserIcon size={78} />
</div>
<div className="flex flex-row gap-5">
<p className="text-lg font-semibold">
{summary?.totalToday} Post{" "}
<span className="text-sm font-normal">Hari ini</span>
</p>
<p className="text-lg font-semibold">
{summary?.totalThisWeek} Post{" "}
<span className="text-sm font-normal">Minggu ini</span>
</p>
</div>
</div>
<div className="lg:w-[20%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
<div className="h-1/2 flex items-center justify-center">
<DashboardSpeecIcon />
</div>
<div className="">Total post</div>
<div className="font-semibold text-lg">{summary?.totalAll}</div>
</div>
<div className="w-full lg:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
<div className="h-1/2 flex items-center justify-center">
<DashboardConnectIcon />
</div>
<div className="">Total views</div>
<div className="font-semibold text-lg">{summary?.totalViews}</div>
</div>
<div className="w-full lg:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
<div className="h-1/2 flex items-center justify-center">
<DashboardShareIcon />
</div>
<div className="">Total share</div>
<div className="font-semibold text-lg">{summary?.totalShares}</div>
</div>
<div className="w-full lg:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
<div className="h-1/2 flex items-center justify-center">
<DashboardCommentIcon size={50} />
</div>
<div className="">Total comment</div>
<div className="font-semibold text-lg">
{summary?.totalComments}
</div>
</div>
</div>
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center ">
<div className="border-1 shadow-sm w-screen rounded-lg lg:w-[55%] p-6 flex flex-col text-xs lg:text-sm">
<div className="flex justify-between mb-4 items-center">
<p className="font-semibold">
Rekapitulasi Post Berita Polda/Polres Pada Website
</p>
<div className="w-[220px] flex flex-row gap-2 justify-between font-semibold">
<Popover
placement="bottom"
classNames={{ content: ["!bg-transparent", "p-0"] }}
>
<PopoverTrigger>
<a className="cursor-pointer">
{convertDateFormatNoTime(postContentDate.startDate)}
</a>
</PopoverTrigger>
<PopoverContent className="bg-transparent">
<Calendar
value={postContentDate.startDate}
onChange={(e) =>
setPostContentDate({
startDate: e,
endDate: postContentDate.endDate,
})
}
maxValue={postContentDate.endDate}
/>
</PopoverContent>
</Popover>
-
<Popover
placement="bottom"
classNames={{ content: ["!bg-transparent", "p-0"] }}
>
<PopoverTrigger>
<a className="cursor-pointer ">
{convertDateFormatNoTime(postContentDate.endDate)}
</a>
</PopoverTrigger>
<PopoverContent className="bg-transparent">
<Calendar
value={postContentDate.endDate}
onChange={(e) =>
setPostContentDate({
startDate: postContentDate.startDate,
endDate: e,
})
}
minValue={postContentDate.startDate}
/>
</PopoverContent>
</Popover>
</div>
</div>
<div className="flex flex-row border-b-1 gap-1 py-1">
<div className="w-[5%]">NO</div>
<div className="w-[50%] lg:w-[70%]">POLDA/POLRES</div>
<div className="w-[45%] lg:w-[25%] text-right">
JUMLAH POST BERITA
</div>
</div>
<div className="flex flex-col gap-1 lg:h-[500px] overflow-y-auto">
{roleId && Number(roleId) < 3 ? (
<Accordion
selectedKeys={selectedAccordion}
onSelectionChange={(e) => handleSelectionChange(e)}
selectionMode="multiple"
className="w-full"
>
{postCount?.map((list) => (
<AccordionItem
key={list.userLevelId}
aria-label={list.userLevelName}
title={
<div
key={list.userLevelId}
className="flex flex-row gap-1 py-1"
>
<div className="w-[5%]">{list?.no}</div>
<div className="w-[85%]">{list?.userLevelName}</div>
<div
className={`w-[10%] text-center ${
list?.totalArticle === 0 &&
"bg-red-600 text-white"
}`}
>
{list?.totalArticle}
</div>
</div>
}
>
{polresData[list?.userLevelId] ? (
polresData[list?.userLevelId]?.map(
(child: any, index: number) => (
<div
key={child.userLevelId}
className={`flex flex-row gap-1 py-1 ${
index ===
polresData[list?.userLevelId].length - 1
? ""
: "border-b-1"
}`}
>
<div className="w-[5%]"></div>
<div className="w-[85%]">
{child?.userLevelName}
</div>
<div
className={`w-[10%] text-start ${
list?.totalArticle === 0 &&
"bg-red-600 text-white"
}`}
>
{child?.totalArticle}
</div>
</div>
)
)
) : (
<Skeleton className="h-3 w-full rounded-lg" />
)}
</AccordionItem>
))}
</Accordion>
) : (
postCount?.map((list) => (
<div
key={list.userLevelId}
className="flex flex-row border-b-1 gap-1 py-1"
>
<div className="w-[5%]">{list?.no}</div>
<div className="w-[85%]">{list?.userLevelName}</div>
<div
className={`w-[10%] text-center ${
list?.totalArticle === 0 && "bg-red-600 text-white"
}`}
>
{list?.totalArticle}
</div>
</div>
))
)}
</div>
</div>
<div className="flex flex-col w-full lg:w-[45%] gap-6 shadow-md bg-white dark:bg-[#18181b] rounded-lg p-8 text-sm">
<div className="flex justify-between font-semibold">
<p>Recent Article</p>
<Link href="/admin/article/create">
<Button color="primary" variant="bordered">
Buat Article
</Button>
</Link>
</div>
{article?.map((list: any) => (
<div
key={list?.id}
className="flex flex-row gap-2 items-center border-b-2 py-2"
>
<Image
alt="thumbnail"
src={list?.thumbnailUrl || `/no-image.jpg`}
className="h-[70px] w-[70px] object-cover rounded-lg"
/>
<div className="flex flex-col gap-2">
<p>{textEllipsis(list?.title, 78)}</p>
<p className="text-xs">
{convertDateFormat(list?.createdAt)}
</p>
</div>
</div>
))}
<div className="my-2 w-full flex justify-center">
<Pagination
isCompact
showControls
showShadow
color="primary"
classNames={{
base: "bg-transparent",
wrapper: "bg-transparent",
item: "w-fit px-3",
cursor: "w-fit px-3",
}}
page={page}
total={totalPage}
onChange={(page) => setPage(page)}
/>
</div>
</div>
</div>
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center ">
<div className="border-1 shadow-sm w-screen rounded-lg lg:w-[55%] p-6 flex flex-col">
<div className="flex justify-between mb-3">
<div className="font-semibold flex flex-col">
Analytics
<div className="font-normal text-xs text-gray-600 flex flex-row gap-2">
<CheckboxGroup
label=""
value={analyticsView}
orientation="vertical"
onValueChange={setAnalyticView}
className="lg:hidden"
>
<Checkbox size="sm" value="comment">
Comment
</Checkbox>
<Checkbox size="sm" value="view" color="success">
View
</Checkbox>
<Checkbox size="sm" value="share" color="warning">
Share
</Checkbox>
</CheckboxGroup>
<CheckboxGroup
label=""
value={analyticsView}
orientation="horizontal"
onValueChange={setAnalyticView}
className="hidden lg:block"
>
<Checkbox size="sm" value="comment">
Comment
</Checkbox>
<Checkbox size="sm" value="view" color="success">
View
</Checkbox>
<Checkbox size="sm" value="share" color="warning">
Share
</Checkbox>
</CheckboxGroup>
</div>
</div>
<div className="flex flex-col lg:flex-row gap-2">
<Button
color="primary"
variant={typeDate === "monthly" ? "solid" : "bordered"}
onPress={() => setTypeDate("monthly")}
className="w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px] rounded-sm lg:rounded-lg"
>
Bulanan
</Button>
<Button
color="primary"
onPress={() => setTypeDate("weekly")}
variant={typeDate === "weekly" ? "solid" : "bordered"}
className="w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px] rounded-sm lg:rounded-lg"
>
Mingguan
</Button>
<div className="w-[140px]">
<Popover
placement="bottom"
classNames={{ content: ["!bg-transparent", "p-0"] }}
>
<PopoverTrigger>
<Button className="w-full">
{getMonthYearName(startDateValue)}
</Button>
</PopoverTrigger>
<PopoverContent className="bg-transparent">
<Calendar
value={startDateValue}
onChange={setStartDateValue}
/>
</PopoverContent>
</Popover>
</div>
</div>
</div>
<div className="flex flex-row w-full h-full">
<div className="w-full h-[30vh] lg:h-full text-black">
<ApexChartColumn
type={typeDate}
date={`${startDateValue.month} ${startDateValue.year}`}
view={analyticsView}
/>
</div>
</div>
</div>
<div className="flex flex-col w-full lg:w-[45%] gap-6 shadow-md bg-white dark:bg-[#18181b] rounded-lg p-8 text-xs lg:text-sm">
<div className="flex justify-between font-semibold">
<p>Top Pages</p>
<div className="w-[220px] flex flex-row gap-2 justify-between font-semibold">
<Popover
placement="bottom"
classNames={{ content: ["!bg-transparent", "p-0"] }}
>
<PopoverTrigger>
<a className="cursor-pointer">
{convertDateFormatNoTime(topContentDate.startDate)}
</a>
</PopoverTrigger>
<PopoverContent className="bg-transparent">
<Calendar
value={topContentDate.startDate}
onChange={(e) =>
setTopContentDate({
startDate: e,
endDate: topContentDate.endDate,
})
}
maxValue={topContentDate.endDate}
/>
</PopoverContent>
</Popover>
-
<Popover
placement="bottom"
classNames={{ content: ["!bg-transparent", "p-0"] }}
>
<PopoverTrigger>
<a className="cursor-pointer ">
{convertDateFormatNoTime(topContentDate.endDate)}
</a>
</PopoverTrigger>
<PopoverContent className="bg-transparent">
<Calendar
value={topContentDate.endDate}
onChange={(e) =>
setTopContentDate({
startDate: topContentDate.startDate,
endDate: e,
})
}
minValue={topContentDate.startDate}
/>
</PopoverContent>
</Popover>
</div>
</div>
<div className="flex flex-row border-b-1">
<div className="w-[5%]">No</div>
<div className="w-[85%]">Title</div>
<div className="w-[10%] text-center">Visits</div>
</div>
{(!topPages || topPages?.length < 1) && (
<div className="flex justify-center items-center">
Tidak ada Data
</div>
)}
{topPages?.map((list) => (
<div key={list.id} className="flex flex-row border-b-1">
<div className="w-[5%]">{list?.no}</div>
<div className="w-[85%]">{list?.title}</div>
<div className="w-[10%] text-center">{list?.viewCount}</div>
</div>
))}
{topPages?.length > 0 && (
<div className="my-2 w-full flex justify-center">
<Pagination
isCompact
showControls
showShadow
color="primary"
classNames={{
base: "bg-transparent",
wrapper: "bg-transparent",
item: "w-fit px-3",
cursor: "w-fit px-3",
}}
page={topPagespage}
total={topPagesTotalPage}
onChange={(page) => setTopPagesPage(page)}
/>
</div>
)}
</div>
</div>
</div>
</div>
);
}