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

1165 lines
43 KiB
TypeScript
Raw Normal View History

2024-12-23 02:25:59 +00:00
"use client";
import {
DashboardCommentIcon,
2024-12-23 02:25:59 +00:00
DashboardConnectIcon,
DashboardShareIcon,
2024-12-23 02:25:59 +00:00
DashboardSpeecIcon,
DashboardTopLeftPointIcon,
DashboardUserIcon,
} from "@/components/icons/dashboard-icon";
2024-04-19 13:26:27 +00:00
import { Submenu1Icon } from "@/components/icons/sidebar-icon";
import {
Accordion,
AccordionItem,
Button,
2025-02-17 07:21:28 +00:00
Calendar,
Checkbox,
CheckboxGroup,
CheckboxIcon,
DateRangePicker,
2025-03-06 10:40:54 +00:00
Image,
Modal,
ModalBody,
ModalContent,
ModalHeader,
Pagination,
2025-02-17 07:21:28 +00:00
Popover,
PopoverContent,
PopoverTrigger,
Select,
SelectItem,
SelectSection,
Skeleton,
useDisclosure,
2025-02-13 08:25:39 +00:00
} from "@heroui/react";
2024-04-19 13:26:27 +00:00
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 { Fragment, useEffect, useState } from "react";
2025-02-17 07:21:28 +00:00
import {
getListArticle,
2025-05-25 09:20:56 +00:00
getListArticleAdminPage,
2025-06-18 02:32:56 +00:00
getRecapArticleData,
2025-02-17 07:21:28 +00:00
getStatisticSummary,
getTopArticles,
getUserLevelDataStat,
2025-05-04 07:14:12 +00:00
} from "@/services/article";
import { Article } from "@/types/globals";
2025-02-17 07:21:28 +00:00
import {
convertDateFormat,
convertDateFormatNoTime,
convertDateFormatNoTimeV2,
2025-08-29 11:39:32 +00:00
getCookiesDecrypt,
2025-08-29 17:32:19 +00:00
getUnixTimestamp,
2025-02-17 07:21:28 +00:00
textEllipsis,
} from "@/utils/global";
import {
parseDate,
getLocalTimeZone,
parseZonedDateTime,
parseAbsoluteToLocal,
} from "@internationalized/date";
import { Input } from "@heroui/input";
2025-06-23 10:57:30 +00:00
import {
ChevronLeftIcon,
ChevronRightIcon,
EyeIconMdi,
SearchIcons,
} from "@/components/icons";
import { format } from "date-fns";
import ApexChartColumnVisitors from "./chart/visitor-chart";
2025-06-26 06:01:49 +00:00
import IndonesiaMap from "@/components/ui/maps-charts";
2025-06-25 10:39:34 +00:00
import ApexChartDynamic from "./chart/dynamic-bar-char";
2025-06-27 14:17:17 +00:00
import ApexMultiLineChart from "./chart/multiline-chart";
2025-07-02 10:20:43 +00:00
import PieChartLoginBrowser from "./chart/pie-chart-browser";
import DashboardVisitorsTable from "@/components/table/dashboard-visitors-table";
type ArticleData = Article & {
no: number;
createdAt: string;
};
2024-04-19 13:26:27 +00:00
2025-02-06 09:49:20 +00:00
interface TopPages {
id: number;
no: number;
title: string;
2025-02-17 07:21:28 +00:00
viewCount: number;
2025-02-06 09:49:20 +00:00
}
2025-06-23 15:51:20 +00:00
const colors = [
"#38bdf8",
"#155e75",
"#0e7490",
"#0284c7",
"#0ea5e9",
"#38bdf8",
"#7dd3fc",
];
2025-02-06 09:49:20 +00:00
interface PostCount {
userLevelId: number;
2025-02-06 09:49:20 +00:00
no: number;
2025-02-17 07:21:28 +00:00
userLevelName: string;
totalArticle: number;
2025-02-06 09:49:20 +00:00
}
2025-06-23 10:57:30 +00:00
const months = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
2024-04-19 13:26:27 +00:00
export default function DashboardContainer() {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
2025-08-29 11:39:32 +00:00
const username = getCookiesDecrypt("username");
const fullname = getCookiesDecrypt("ufne");
const [page, setPage] = useState(1);
const [totalPage, setTotalPage] = useState(1);
2025-02-06 09:49:20 +00:00
const [topPagespage, setTopPagesPage] = useState(1);
const [topPagesTotalPage, setTopPagesTotalPage] = useState(1);
const [article, setArticle] = useState<ArticleData[]>([]);
const [selectedCategory, setSelectedCategory] = useState("polda");
const [analyticsView, setAnalyticView] = useState<string[]>([
2025-02-17 07:21:28 +00:00
"comment",
"view",
"share",
]);
2025-02-17 07:21:28 +00:00
const [startDateValue, setStartDateValue] = useState(
parseDate(convertDateFormatNoTimeV2(new Date()))
);
2025-08-29 11:39:32 +00:00
const [postContentDate, setPostContentDate] = useState<any>({
start: parseZonedDateTime(
`${convertDateFormatNoTimeV2(
2025-02-17 07:21:28 +00:00
new Date(new Date().setDate(new Date().getDate() - 7))
)}T00:00[Asia/Jakarta]`
),
end: parseZonedDateTime(
`${convertDateFormatNoTimeV2(new Date())}T23:59[Asia/Jakarta]`
2025-02-17 07:21:28 +00:00
),
2025-02-06 09:49:20 +00:00
});
2025-08-29 11:39:32 +00:00
const [topContentDate, setTopContentDate] = useState<any>({
startDate: parseDate(
convertDateFormatNoTimeV2(
new Date(new Date().setDate(new Date().getDate() - 7))
)
),
endDate: parseDate(convertDateFormatNoTimeV2(new Date())),
});
2025-02-17 07:21:28 +00:00
const [summary, setSummary] = useState<any>();
2025-02-06 09:49:20 +00:00
const [topPages, setTopPages] = useState<TopPages[]>([]);
const [postCount, setPostCount] = useState<PostCount[]>([]);
const [polresData, setPolresData] = useState<any>({});
const [selectedAccordion, setSelectedAccordion] = useState<any>(new Set([]));
const [recapArticleList, setRecapArticleList] = useState<any>([]);
2025-06-18 02:32:56 +00:00
const [selectedUnit, setSelectedUnit] = useState<{
id: number;
name: string;
}>();
2025-08-29 11:39:32 +00:00
const roleId = getCookiesDecrypt("urie");
2025-06-23 10:57:30 +00:00
const today = new Date();
2025-06-29 10:53:03 +00:00
const [chartVisitorTotal, setChartVisitorTotal] = useState(0);
const [recapArticlePage, setRecapArticlePage] = useState(1);
const [recapArticleTotalPage, setRecapArticleTotalPage] = useState(1);
2025-06-23 10:57:30 +00:00
const [typeDate, setTypeDate] = useState("daily");
const [year, setYear] = useState(today.getFullYear());
const [selectedMonth, setSelectedMonth] = useState<Date | null>(today);
2025-08-29 11:39:32 +00:00
const [viewsDailyDate, setViewsDailyDate] = useState<any>({
2025-06-28 05:14:54 +00:00
start: parseDate(
convertDateFormatNoTimeV2(
new Date(new Date().setDate(new Date().getDate() - 7))
)
),
2025-06-23 10:57:30 +00:00
end: parseDate(convertDateFormatNoTimeV2(today)),
});
const [typeDateVisitor, setTypeDateVisitor] = useState("daily");
const [visitorYear, setVisitorYear] = useState(today.getFullYear());
const [visitorSelectedMonth, setVisitorSelectedMonth] = useState<Date | null>(
today
);
2025-08-29 11:39:32 +00:00
const [visitorDailyDate, setVisitorDailyDate] = useState<any>({
2025-06-25 10:39:34 +00:00
start: parseDate(
convertDateFormatNoTimeV2(
2025-06-28 05:14:54 +00:00
new Date(new Date().setDate(new Date().getDate() - 7))
2025-06-25 10:39:34 +00:00
)
),
2025-06-23 10:57:30 +00:00
end: parseDate(convertDateFormatNoTimeV2(today)),
});
2025-06-27 14:17:17 +00:00
const [typeDateUsers, setTypeDateUsers] = useState("daily");
const [usersYear, setUsersYear] = useState(today.getFullYear());
2025-08-29 11:39:32 +00:00
const [usersSelectedMonth, setUsersSelectedMonth] = useState<any>(today);
const [usersDailyDate, setUsersDailyDate] = useState<any>({
2025-06-27 14:17:17 +00:00
start: parseDate(
convertDateFormatNoTimeV2(
2025-06-28 05:14:54 +00:00
new Date(new Date().setDate(new Date().getDate() - 7))
2025-06-27 14:17:17 +00:00
)
),
end: parseDate(convertDateFormatNoTimeV2(today)),
});
2025-06-23 10:57:30 +00:00
const handleMonthClick = (monthIndex: number) => {
setSelectedMonth(new Date(year, monthIndex, 1));
};
const handleMonthClickVisitor = (monthIndex: number) => {
2025-06-27 14:17:17 +00:00
setVisitorSelectedMonth(new Date(visitorYear, monthIndex, 1));
};
const handleMonthClickUsers = (monthIndex: number) => {
setUsersSelectedMonth(new Date(usersYear, monthIndex, 1));
2025-06-23 10:57:30 +00:00
};
2025-02-17 07:21:28 +00:00
useEffect(() => {
fetchSummary();
}, []);
useEffect(() => {
initState();
}, [page]);
async function initState() {
const req = {
limit: "4",
page: page,
search: "",
2025-05-26 01:04:28 +00:00
sort: "desc",
2025-05-28 06:56:41 +00:00
timeStamp: getUnixTimestamp(),
};
2025-05-25 09:20:56 +00:00
const res = await getListArticleAdminPage(req);
setArticle(res.data?.data);
setTotalPage(res?.data?.meta?.totalPage);
}
2025-02-17 07:21:28 +00:00
async function fetchSummary() {
2025-05-28 06:56:41 +00:00
const res = await getStatisticSummary(getUnixTimestamp());
2025-02-17 07:21:28 +00:00
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
}`;
}
};
2025-02-17 07:21:28 +00:00
2025-02-06 09:49:20 +00:00
async function fetchTopPages() {
2025-02-17 07:21:28 +00:00
const req = {
limit: "10",
page: topPagespage,
search: "",
2025-05-26 01:04:28 +00:00
sort: "desc",
2025-05-26 02:01:35 +00:00
isPublish: true,
2025-05-28 06:56:41 +00:00
timeStamp: getUnixTimestamp(),
startDate: getDate(topContentDate.startDate),
endDate: getDate(topContentDate.endDate),
2025-02-17 07:21:28 +00:00
};
const res = await getTopArticles(req);
setTopPages(getTableNumber(10, res.data?.data, "topPages"));
2025-02-17 07:21:28 +00:00
setTopPagesTotalPage(res?.data?.meta?.totalPage);
2025-02-06 09:49:20 +00:00
}
2025-02-17 07:21:28 +00:00
useEffect(() => {
fetchPostCount();
2025-06-18 04:51:17 +00:00
}, [selectedCategory]);
2025-02-06 09:49:20 +00:00
async function fetchPostCount() {
2025-02-17 07:21:28 +00:00
const getDate = (data: any) => {
2025-03-06 10:40:54 +00:00
return `${data.year}-${data.month < 10 ? `0${data.month}` : data.month}-${
data.day < 10 ? `0${data.day}` : data.day
}`;
2025-02-17 07:21:28 +00:00
};
const res = await getUserLevelDataStat(
getDate(postContentDate.start),
getDate(postContentDate.end),
getUnixTimestamp(),
`${getTime(postContentDate.start.hour)}:${getTime(
postContentDate.start.minute
)}:00`,
`${getTime(postContentDate.end.hour)}:${getTime(
postContentDate.end.minute
2025-06-17 16:55:17 +00:00
)}:00`,
selectedCategory === "polda" ? "" : selectedCategory
2025-02-17 07:21:28 +00:00
);
setPostCount(getTableNumberStats(res?.data?.data));
2025-02-06 09:49:20 +00:00
}
const getTableNumber = (limit: number, data: any, type: string) => {
2025-02-06 09:49:20 +00:00
if (data) {
const startIndex =
limit * (type == "topPages" ? topPagespage - 1 : recapArticlePage - 1);
2025-02-06 09:49:20 +00:00
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;
2025-06-13 10:03:42 +00:00
const newData = data
.filter(
(value: any) =>
value.userLevelName !== "SATWIL" && value.userLevelName !== "SATKER"
)
.map((value: any) => {
iterate++;
value.no = iterate;
return value;
});
return newData;
}
};
useEffect(() => {
const temp = Array.from(selectedAccordion);
console.log("selecette", temp);
if (temp.length > 0) {
for (const element of temp) {
getPolresData(Number(element));
}
}
}, [postContentDate]);
const getTime = (time: number) => {
return time < 10 ? `0${time}` : String(time);
};
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.start),
getDate(postContentDate.end),
2025-06-13 10:35:07 +00:00
getUnixTimestamp(),
`${getTime(postContentDate.start.hour)}:${getTime(
postContentDate.start.minute
)}:00`,
2025-06-17 16:55:17 +00:00
`${getTime(postContentDate.end.hour)}:${getTime(
postContentDate.end.minute
)}:00`,
2025-06-17 16:55:17 +00:00
"",
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);
}
};
useEffect(() => {
getRecapArticle();
}, [recapArticlePage]);
2025-06-18 02:32:56 +00:00
const getRecapArticle = async (userLevelId?: number) => {
const req = {
2025-06-18 02:32:56 +00:00
id: userLevelId || selectedUnit?.id,
page: recapArticlePage,
2025-06-18 02:32:56 +00:00
startDate: getDate(postContentDate.start),
endDate: getDate(postContentDate.end),
startTime: `${getTime(postContentDate.start.hour)}:${getTime(
postContentDate.start.minute
)}:00`,
endTime: `${getTime(postContentDate.end.hour)}:${getTime(
postContentDate.end.minute
)}:00`,
};
2025-06-18 02:32:56 +00:00
const res = await getRecapArticleData(req);
setRecapArticleList(getTableNumber(10, res.data?.data, "recapArticle"));
setRecapArticleTotalPage(res?.data?.meta?.totalPage);
};
2025-06-18 02:32:56 +00:00
const openModalArticle = async (userLevelId: number) => {
setRecapArticlePage(1);
2025-06-18 02:32:56 +00:00
getRecapArticle(userLevelId);
onOpen();
};
2024-12-23 02:25:59 +00:00
return (
2025-01-30 11:34:29 +00:00
<div className="px-2 lg:p-8 flex justify-center">
2024-12-23 02:25:59 +00:00
<div className="w-full flex flex-col gap-6">
2025-01-30 11:34:29 +00:00
<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} />
2024-12-23 02:25:59 +00:00
</div>
<div className="flex flex-row gap-5">
<p className="text-lg font-semibold">
2025-02-17 07:21:28 +00:00
{summary?.totalToday} Post{" "}
2025-02-12 05:07:28 +00:00
<span className="text-sm font-normal">Hari ini</span>
</p>
<p className="text-lg font-semibold">
2025-02-17 07:21:28 +00:00
{summary?.totalThisWeek} Post{" "}
2025-02-12 05:07:28 +00:00
<span className="text-sm font-normal">Minggu ini</span>
</p>
2024-12-23 02:25:59 +00:00
</div>
</div>
2025-01-30 11:34:29 +00:00
<div className="lg:w-[20%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
2024-12-23 02:25:59 +00:00
<div className="h-1/2 flex items-center justify-center">
<DashboardSpeecIcon />
2024-12-23 02:25:59 +00:00
</div>
<div className="">Total post</div>
2025-02-17 07:21:28 +00:00
<div className="font-semibold text-lg">{summary?.totalAll}</div>
2024-12-23 02:25:59 +00:00
</div>
2025-01-30 11:34:29 +00:00
<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">
2024-12-23 02:25:59 +00:00
<div className="h-1/2 flex items-center justify-center">
<DashboardConnectIcon />
2024-12-23 02:25:59 +00:00
</div>
<div className="">Total views</div>
2025-02-17 07:21:28 +00:00
<div className="font-semibold text-lg">{summary?.totalViews}</div>
</div>
2025-01-30 11:34:29 +00:00
<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>
2025-02-17 07:21:28 +00:00
<div className="font-semibold text-lg">{summary?.totalShares}</div>
</div>
2025-01-30 11:34:29 +00:00
<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>
2025-02-17 07:21:28 +00:00
<div className="font-semibold text-lg">
{summary?.totalComments}
</div>
2024-12-23 02:25:59 +00:00
</div>
</div>
2025-02-06 09:49:20 +00:00
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center ">
2025-06-30 01:24:18 +00:00
<div className="border-1 shadow-sm w-screen rounded-lg lg:w-[55%] p-6 flex flex-col text-xs lg:text-sm h-fit">
<div className="flex flex-col md:flex-row md:justify-between mb-4 items-center gap-2 md:gap-0">
<p className="font-semibold self-start">
2025-02-17 07:21:28 +00:00
Rekapitulasi Post Berita Polda/Polres Pada Website
2025-02-06 09:49:20 +00:00
</p>
<DateRangePicker
hideTimeZone
2025-06-18 04:51:17 +00:00
selectorButtonPlacement="start"
value={postContentDate}
onChange={(e) => {
e && setPostContentDate(e);
}}
label="Tanggal dan Waktu"
visibleMonths={2}
hourCycle={24}
className="w-fit text-sm self-end"
classNames={{
timeInputLabel: "text-xs",
innerWrapper: "text-xs",
inputWrapper: "border-1",
}}
size="sm"
variant="bordered"
2025-06-18 04:51:17 +00:00
endContent={
<a
className="cursor-pointer hover:text-black"
onClick={() => fetchPostCount()}
>
<SearchIcons />
</a>
}
/>
2025-02-06 09:49:20 +00:00
</div>
<div className="flex flex-row border-b-1 gap-1 py-1 items-center">
2025-02-06 09:49:20 +00:00
<div className="w-[5%]">NO</div>
<div className="w-[50%] lg:w-[70%]">
{Number(roleId) < 3 ? (
<Select
variant="underlined"
className="max-w-xs border-none"
label=""
selectedKeys={[selectedCategory]}
classNames={{
innerWrapper: "!border-none",
mainWrapper: "!border-none",
trigger: "border-none",
}}
onChange={(e) =>
e.target.value !== ""
? setSelectedCategory(e.target.value)
: ""
}
>
<SelectItem key="polda">POLDA</SelectItem>
<SelectItem key="polres">POLRES</SelectItem>
<SelectItem key="satker">SATKER MABES</SelectItem>
</Select>
) : (
"POLDA/POLRES"
)}
</div>
2025-02-06 09:49:20 +00:00
<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">
2025-06-17 16:55:17 +00:00
{roleId && Number(roleId) < 3 && selectedCategory == "polda" ? (
<Accordion
selectedKeys={selectedAccordion}
onSelectionChange={(e) => handleSelectionChange(e)}
selectionMode="multiple"
className="w-full"
2025-02-07 03:59:22 +00:00
>
{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>
<a
onClick={() => {
if (list?.totalArticle !== 0) {
2025-06-18 02:32:56 +00:00
setSelectedUnit({
name: child?.userLevelName,
id: child.userLevelId,
});
openModalArticle(child.userLevelId);
}
}}
2025-06-17 16:55:17 +00:00
className={`w-[10%] text-start cursor-pointer flex flex-row gap-1 items-center justify-end ${
list?.totalArticle === 0 &&
2025-06-17 16:55:17 +00:00
"bg-red-600 text-white cursor-default "
}`}
>
{child?.totalArticle}
2025-06-17 16:55:17 +00:00
<EyeIconMdi size={12} />
</a>
</div>
)
)
) : (
<Skeleton className="h-3 w-full rounded-lg" />
)}
</AccordionItem>
))}
</Accordion>
) : (
postCount?.map((list) => (
2025-02-06 09:49:20 +00:00
<div
key={list.userLevelId}
className="flex flex-row border-b-1 gap-1 py-1"
2025-02-06 09:49:20 +00:00
>
<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"
}`}
>
2025-06-17 16:55:17 +00:00
<a
onClick={() => {
if (list?.totalArticle !== 0) {
2025-06-18 02:32:56 +00:00
setSelectedUnit({
name: list?.userLevelName,
id: list.userLevelId,
});
openModalArticle(list.userLevelId);
2025-06-17 16:55:17 +00:00
}
}}
className={`w-[10%] text-start cursor-pointer flex flex-row gap-1 items-center justify-end ${
list?.totalArticle === 0 &&
"bg-red-600 text-white cursor-default "
}`}
>
{list?.totalArticle}
<EyeIconMdi />
</a>
</div>
2025-02-06 09:49:20 +00:00
</div>
))
)}
2025-02-06 09:49:20 +00:00
</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>
2025-02-06 09:49:20 +00:00
<div className="flex flex-row border-b-1">
<div className="w-[5%]">No</div>
<div className="w-[85%]">Title</div>
2025-02-17 07:21:28 +00:00
<div className="w-[10%] text-center">Visits</div>
2025-02-06 09:49:20 +00:00
</div>
{(!topPages || topPages?.length < 1) && (
<div className="flex justify-center items-center">
Tidak ada Data
</div>
)}
2025-02-06 09:49:20 +00:00
{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>
2025-02-17 07:21:28 +00:00
<div className="w-[10%] text-center">{list?.viewCount}</div>
2024-12-23 02:25:59 +00:00
</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>
)}
2024-12-23 02:25:59 +00:00
</div>
2024-04-19 13:26:27 +00:00
</div>
2025-06-27 14:17:17 +00:00
<div className="w-full flex flex-col gap-6 justify-center">
2025-06-30 01:24:18 +00:00
<div className="border-1 shadow-sm w-full rounded-lg p-6 flex flex-col h-[600px]">
2025-06-23 10:57:30 +00:00
<div className="flex justify-between mb-3">
<div className="font-semibold flex flex-col">
Engagement 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">
<Select
className="w-full md:w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px]"
label=""
labelPlacement="outside"
selectedKeys={[typeDate]}
onChange={(e) =>
e.target.value !== "" && setTypeDate(e.target.value)
}
>
<SelectItem key="monthly">Bulanan</SelectItem>
<SelectItem key="daily">Harian</SelectItem>
</Select>
{typeDate === "monthly" ? (
<Popover
placement="bottom"
showArrow={true}
className="w-full"
>
<PopoverTrigger>
<Button className="w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px] rounded-sm lg:rounded-lg">
{" "}
{selectedMonth
? format(selectedMonth, "MMMM yyyy")
: "Pilih Bulan"}
</Button>
</PopoverTrigger>
<PopoverContent className="p-4 w-[200px]">
<div className="flex items-center justify-between mb-2 px-1 w-full">
<button
className="text-gray-500 hover:text-black"
onClick={() => setYear((prev) => prev - 1)}
>
<ChevronLeftIcon />
</button>
<span className="font-semibold text-center">
{year}
</span>
<button
className="text-gray-500 hover:text-black"
onClick={() => setYear((prev) => prev + 1)}
>
<ChevronRightIcon />
</button>
</div>
<div className="grid grid-cols-3 gap-2 w-full">
{months.map((month, idx) => (
<button
key={idx}
onClick={() => handleMonthClick(idx)}
className={`py-1 rounded-md text-sm transition-colors ${
selectedMonth &&
selectedMonth.getMonth() === idx &&
selectedMonth.getFullYear() === year
? "bg-blue-500 text-white"
: "hover:bg-gray-200 text-gray-700"
}`}
>
{month}
</button>
))}
</div>
</PopoverContent>
</Popover>
) : (
<div className="w-[220px]">
<DateRangePicker
className="h-[40px]"
value={viewsDailyDate}
onChange={(e) => e !== null && setViewsDailyDate(e)}
label=""
/>
</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={`${
convertDateFormatNoTimeV2(String(selectedMonth)).split(
"-"
)[1]
} ${
convertDateFormatNoTimeV2(String(selectedMonth)).split(
"-"
)[0]
}`}
view={analyticsView}
range={viewsDailyDate}
/>
</div>
</div>
</div>
2025-06-27 14:17:17 +00:00
<div className="border-1 shadow-sm w-full rounded-lg p-6 flex flex-col h-[700px]">
2025-06-23 10:57:30 +00:00
<div className="flex justify-between mb-3">
2025-06-29 13:01:49 +00:00
<div className="font-semibold flex flex-col">
Article Contribution Analytics
</div>
2025-06-23 10:57:30 +00:00
<div className="flex flex-col lg:flex-row gap-2">
<Select
className="w-full md:w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px]"
label=""
labelPlacement="outside"
2025-06-27 14:17:17 +00:00
selectedKeys={[typeDateUsers]}
2025-06-23 10:57:30 +00:00
onChange={(e) =>
2025-06-27 14:17:17 +00:00
e.target.value !== "" && setTypeDateUsers(e.target.value)
2025-06-23 10:57:30 +00:00
}
>
<SelectItem key="monthly">Bulanan</SelectItem>
<SelectItem key="daily">Harian</SelectItem>
</Select>
2025-06-27 14:17:17 +00:00
{typeDateUsers === "monthly" ? (
2025-06-23 10:57:30 +00:00
<Popover
placement="bottom"
showArrow={true}
className="w-full"
>
<PopoverTrigger>
<Button className="w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px] rounded-sm lg:rounded-lg">
{" "}
2025-06-27 14:17:17 +00:00
{usersSelectedMonth
? format(usersSelectedMonth, "MMMM yyyy")
2025-06-23 10:57:30 +00:00
: "Pilih Bulan"}
</Button>
</PopoverTrigger>
<PopoverContent className="p-4 w-[200px]">
<div className="flex items-center justify-between mb-2 px-1 w-full">
<button
className="text-gray-500 hover:text-black"
2025-06-27 14:17:17 +00:00
onClick={() => setUsersYear((prev) => prev - 1)}
2025-06-23 10:57:30 +00:00
>
<ChevronLeftIcon />
</button>
<span className="font-semibold text-center">
{year}
</span>
<button
className="text-gray-500 hover:text-black"
2025-06-27 14:17:17 +00:00
onClick={() => setUsersYear((prev) => prev + 1)}
2025-06-23 10:57:30 +00:00
>
<ChevronRightIcon />
</button>
</div>
<div className="grid grid-cols-3 gap-2 w-full">
{months.map((month, idx) => (
<button
key={idx}
2025-06-27 14:17:17 +00:00
onClick={() => handleMonthClickUsers(idx)}
2025-06-23 10:57:30 +00:00
className={`py-1 rounded-md text-sm transition-colors ${
2025-06-27 14:17:17 +00:00
usersSelectedMonth &&
usersSelectedMonth.getMonth() === idx &&
usersSelectedMonth.getFullYear() === year
2025-06-23 10:57:30 +00:00
? "bg-blue-500 text-white"
: "hover:bg-gray-200 text-gray-700"
}`}
>
{month}
</button>
))}
</div>
</PopoverContent>
</Popover>
) : (
<div className="w-[220px]">
<DateRangePicker
className="h-[40px]"
2025-06-27 14:17:17 +00:00
value={usersDailyDate}
onChange={(e) => e !== null && setUsersDailyDate(e)}
2025-06-23 10:57:30 +00:00
label=""
/>
</div>
)}
</div>
</div>
<div className="flex flex-row w-full h-full">
<div className="w-full h-[30vh] lg:h-full text-black">
2025-06-27 14:17:17 +00:00
<ApexMultiLineChart
type={typeDateUsers}
2025-06-23 10:57:30 +00:00
date={`${
2025-06-27 14:17:17 +00:00
convertDateFormatNoTimeV2(String(usersSelectedMonth)).split(
"-"
)[1]
2025-06-23 10:57:30 +00:00
} ${
2025-06-27 14:17:17 +00:00
convertDateFormatNoTimeV2(String(usersSelectedMonth)).split(
"-"
)[0]
2025-06-23 10:57:30 +00:00
}`}
2025-06-27 14:17:17 +00:00
range={usersDailyDate}
2025-06-23 10:57:30 +00:00
/>
</div>
</div>
</div>
</div>
2025-06-24 07:50:35 +00:00
{roleId && Number(roleId) < 3 && (
2025-06-25 10:39:34 +00:00
<div>
2025-06-28 16:39:22 +00:00
<IndonesiaMap />
2025-06-26 06:01:49 +00:00
2025-06-25 10:39:34 +00:00
<div className="border-1 shadow-sm w-full rounded-lg p-6 flex flex-col mt-4 h-[600px]">
<div className="flex justify-between mb-3">
<div className="font-semibold flex flex-col">
Visitor Analytics
2025-06-24 07:50:35 +00:00
</div>
2025-06-25 10:39:34 +00:00
<div className="flex flex-col lg:flex-row gap-2">
<Select
className="w-full md:w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px]"
label=""
labelPlacement="outside"
selectedKeys={[typeDateVisitor]}
onChange={(e) =>
e.target.value !== "" &&
setTypeDateVisitor(e.target.value)
}
>
<SelectItem key="monthly">Bulanan</SelectItem>
<SelectItem key="daily">Harian</SelectItem>
</Select>
{typeDateVisitor === "monthly" ? (
<Popover
placement="bottom"
showArrow={true}
className="w-full"
>
<PopoverTrigger>
<Button className="w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px] rounded-sm lg:rounded-lg">
{" "}
{visitorSelectedMonth
? format(visitorSelectedMonth, "MMMM yyyy")
: "Pilih Bulan"}
</Button>
</PopoverTrigger>
<PopoverContent className="p-4 w-[200px]">
<div className="flex items-center justify-between mb-2 px-1 w-full">
<button
className="text-gray-500 hover:text-black"
onClick={() => setVisitorYear((prev) => prev - 1)}
>
<ChevronLeftIcon />
</button>
<span className="font-semibold text-center">
{year}
</span>
<button
className="text-gray-500 hover:text-black"
onClick={() => setVisitorYear((prev) => prev + 1)}
>
<ChevronRightIcon />
</button>
</div>
<div className="grid grid-cols-3 gap-2 w-full">
{months.map((month, idx) => (
<button
key={idx}
onClick={() => handleMonthClickVisitor(idx)}
className={`py-1 rounded-md text-sm transition-colors ${
visitorSelectedMonth &&
visitorSelectedMonth.getMonth() === idx &&
visitorSelectedMonth.getFullYear() === year
? "bg-blue-500 text-white"
: "hover:bg-gray-200 text-gray-700"
}`}
>
{month}
</button>
))}
</div>
</PopoverContent>
</Popover>
) : (
<div className="w-[220px]">
<DateRangePicker
className="h-[40px]"
value={visitorDailyDate}
onChange={(e) => e !== null && setVisitorDailyDate(e)}
label=""
/>
</div>
)}
</div>
</div>
<div className="flex flex-row w-full h-full">
<div className="w-full h-[30vh] lg:h-full text-black">
<ApexChartColumnVisitors
type={typeDateVisitor}
date={`${
convertDateFormatNoTimeV2(
String(visitorSelectedMonth)
).split("-")[1]
} ${
convertDateFormatNoTimeV2(
String(visitorSelectedMonth)
).split("-")[0]
}`}
range={visitorDailyDate}
2025-06-29 10:53:03 +00:00
total={(data) => setChartVisitorTotal(data)}
2025-06-25 10:39:34 +00:00
/>
</div>
</div>
2025-06-29 10:53:03 +00:00
<div className="flex flex-row gap-2 mt-5 border-1 p-5 rounded-lg">
<div className="w-1/5 text-center">Chart Total</div>
<div className="w-4/5 text-center">
{chartVisitorTotal} Visitors
</div>
2025-06-29 10:53:03 +00:00
</div>
2025-06-24 07:50:35 +00:00
</div>
2025-07-02 10:20:43 +00:00
<PieChartLoginBrowser />
<DashboardVisitorsTable />
2025-06-23 15:51:20 +00:00
</div>
2025-06-24 07:50:35 +00:00
)}
2024-12-23 02:25:59 +00:00
</div>
<Modal isOpen={isOpen} onOpenChange={onOpenChange} size="3xl">
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">
2025-06-18 02:32:56 +00:00
{selectedUnit?.name} List Content
</ModalHeader>
<ModalBody className="flex flex-col gap-2">
<div className="grid grid-cols-12 text-xs ">
<div className="col-span-1 font-bold">No</div>
<div className="col-span-9 text-center font-bold">Judul</div>
<div className="col-span-2 font-bold ">Dibuat</div>
<div className="col-span-12 grid grid-cols-12 max-h-[480px] overflow-auto">
{recapArticleList?.length > 0 &&
recapArticleList?.map((list: any) => (
<Fragment key={list.id}>
<div className="col-span-1 my-2">{list.no}</div>
<div className="col-span-9 my-2">
<Link
href={`/news/detail/${list.id}-${list.slug}`}
target="_blank"
className="hover:underline hover:text-primary"
>
{textEllipsis(list.title, 80)}
</Link>
</div>
<div className="my-2 col-span-2">
{convertDateFormat(list?.createdAt)}
</div>
</Fragment>
))}
</div>
</div>
{recapArticleList?.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={recapArticlePage}
total={recapArticleTotalPage}
onChange={(page) => setRecapArticlePage(page)}
/>
</div>
)}
</ModalBody>
</>
)}
</ModalContent>
</Modal>
2024-12-23 02:25:59 +00:00
</div>
);
}