fix:dashboard analytics
This commit is contained in:
parent
c018be888e
commit
1fc236bff3
|
|
@ -43,27 +43,88 @@ function processMonthlyData(count: number[]): {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRangeAcrossMonths(
|
||||||
|
data: any[],
|
||||||
|
startMonth: number,
|
||||||
|
startDay: number,
|
||||||
|
endMonth: number,
|
||||||
|
endDay: number
|
||||||
|
) {
|
||||||
|
const view: number[] = [];
|
||||||
|
const comment: number[] = [];
|
||||||
|
const share: number[] = [];
|
||||||
|
const labels: string[] = [];
|
||||||
|
|
||||||
|
const sortedData = data.sort((a, b) => a.month - b.month);
|
||||||
|
console.log("sorted data", sortedData);
|
||||||
|
for (const monthData of sortedData) {
|
||||||
|
const { month, view: v, comment: c, share: s } = monthData;
|
||||||
|
|
||||||
|
if (month < startMonth || month > endMonth) continue;
|
||||||
|
|
||||||
|
let startIndex = 0;
|
||||||
|
let endIndex = v.length - 1;
|
||||||
|
|
||||||
|
if (month === startMonth) startIndex = startDay - 1;
|
||||||
|
if (month === endMonth) endIndex = endDay - 1;
|
||||||
|
|
||||||
|
for (let i = startIndex; i <= endIndex; i++) {
|
||||||
|
view.push(v[i]);
|
||||||
|
comment.push(c[i]);
|
||||||
|
share.push(s[i]);
|
||||||
|
|
||||||
|
const label = `${(i + 1).toString().padStart(2, "0")} - ${month
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")} `;
|
||||||
|
labels.push(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { view, comment, share, labels };
|
||||||
|
}
|
||||||
|
|
||||||
const ApexChartColumn = (props: {
|
const ApexChartColumn = (props: {
|
||||||
type: string;
|
type: string;
|
||||||
date: string;
|
date: string;
|
||||||
view: string[];
|
view: string[];
|
||||||
|
range: { start: any; end: any };
|
||||||
}) => {
|
}) => {
|
||||||
const { date, type, view } = props;
|
const { date, type, view, range } = props;
|
||||||
const [categories, setCategories] = useState<string[]>([]);
|
const [categories, setCategories] = useState<string[]>([]);
|
||||||
const [series, setSeries] = useState<{ name: string; data: number[] }[]>([]);
|
const [series, setSeries] = useState<{ name: string; data: number[] }[]>([]);
|
||||||
const [seriesComment, setSeriesComment] = useState<number[]>([]);
|
const [seriesComment, setSeriesComment] = useState<number[]>([]);
|
||||||
const [seriesView, setSeriesView] = useState<number[]>([]);
|
const [seriesView, setSeriesView] = useState<number[]>([]);
|
||||||
const [seriesShare, setSeriesShare] = useState<number[]>([]);
|
const [seriesShare, setSeriesShare] = useState<number[]>([]);
|
||||||
|
const [years, setYear] = useState("");
|
||||||
|
const [datas, setDatas] = useState<any>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
}, [date, type, view]);
|
}, [date, type, view, range]);
|
||||||
|
|
||||||
const initFetch = async () => {
|
const initFetch = async () => {
|
||||||
const splitDate = date.split(" ");
|
const splitDate = date.split(" ");
|
||||||
|
const splitDateDaily = String(range.start.year);
|
||||||
const res = await getStatisticMonthly(splitDate[1]);
|
let data = [];
|
||||||
const data = res?.data?.data;
|
console.log(
|
||||||
|
"aaawwww",
|
||||||
|
type === "monthly" && splitDate[1] === years,
|
||||||
|
type === "daily" && splitDateDaily === years
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
(type === "monthly" && splitDate[1] === years) ||
|
||||||
|
(type === "daily" && splitDateDaily === years)
|
||||||
|
) {
|
||||||
|
console.log("if", datas);
|
||||||
|
data = datas;
|
||||||
|
} else {
|
||||||
|
const res = await getStatisticMonthly(
|
||||||
|
type === "monthly" ? splitDate[1] : splitDateDaily
|
||||||
|
);
|
||||||
|
data = res?.data?.data;
|
||||||
|
setDatas(data);
|
||||||
|
}
|
||||||
|
console.log("datas", data);
|
||||||
const getDatas = data?.find(
|
const getDatas = data?.find(
|
||||||
(a: any) =>
|
(a: any) =>
|
||||||
a.month == Number(splitDate[0]) && a.year === Number(splitDate[1])
|
a.month == Number(splitDate[0]) && a.year === Number(splitDate[1])
|
||||||
|
|
@ -94,16 +155,32 @@ const ApexChartColumn = (props: {
|
||||||
setSeriesView(getDatas.view);
|
setSeriesView(getDatas.view);
|
||||||
setSeriesShare(getDatas.share);
|
setSeriesShare(getDatas.share);
|
||||||
}
|
}
|
||||||
if (type === "weekly") {
|
if (type === "daily") {
|
||||||
const category = [];
|
const mappedData = getRangeAcrossMonths(
|
||||||
for (let i = 1; i <= temp1.weeks.length; i++) {
|
data,
|
||||||
category.push(`Week ${i}`);
|
range.start.month,
|
||||||
}
|
range.start.day,
|
||||||
setCategories(category);
|
range.end.month,
|
||||||
|
range.end.day
|
||||||
|
);
|
||||||
|
|
||||||
|
setSeriesComment(mappedData.comment);
|
||||||
|
setSeriesView(mappedData.view);
|
||||||
|
setSeriesShare(mappedData.share);
|
||||||
|
setCategories(mappedData.labels);
|
||||||
}
|
}
|
||||||
|
// if (type === "weekly") {
|
||||||
|
// const category = [];
|
||||||
|
// for (let i = 1; i <= temp1.weeks.length; i++) {
|
||||||
|
// category.push(`Week ${i}`);
|
||||||
|
// }
|
||||||
|
// setCategories(category);
|
||||||
|
// }
|
||||||
} else {
|
} else {
|
||||||
setSeriesComment([]);
|
setSeriesComment([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setYear(type === "monthly" ? splitDate[1] : splitDateDaily);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -122,8 +199,6 @@ const ApexChartColumn = (props: {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
console.log("temp", temp);
|
|
||||||
|
|
||||||
setSeries(temp);
|
setSeries(temp);
|
||||||
}, [view, seriesShare, seriesView, seriesComment]);
|
}, [view, seriesShare, seriesView, seriesComment]);
|
||||||
|
|
||||||
|
|
@ -143,7 +218,7 @@ const ApexChartColumn = (props: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
xaxis: {
|
xaxis: {
|
||||||
categories: type == "weekly" ? categories : [],
|
categories: type == "daily" ? categories : [],
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
series={series}
|
series={series}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,226 @@
|
||||||
|
"use client";
|
||||||
|
import React, { Component, useEffect, useState } from "react";
|
||||||
|
import ReactApexChart from "react-apexcharts";
|
||||||
|
import dummyData from "../../../../const/dummy.json";
|
||||||
|
import { getStatisticMonthly } from "@/services/article";
|
||||||
|
|
||||||
|
type WeekData = {
|
||||||
|
week: number;
|
||||||
|
days: number[];
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RemainingDays = {
|
||||||
|
days: number[];
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function processMonthlyData(count: number[]): {
|
||||||
|
weeks: WeekData[];
|
||||||
|
remaining_days: RemainingDays;
|
||||||
|
} {
|
||||||
|
const weeks: WeekData[] = [];
|
||||||
|
let weekIndex = 1;
|
||||||
|
|
||||||
|
for (let i = 0; i < count.length; i += 7) {
|
||||||
|
const weekData = count.slice(i, i + 7);
|
||||||
|
weeks.push({
|
||||||
|
week: weekIndex,
|
||||||
|
days: weekData,
|
||||||
|
total: weekData.reduce((sum, day) => sum + day, 0),
|
||||||
|
});
|
||||||
|
weekIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const remainingDays: RemainingDays = {
|
||||||
|
days: count.length % 7 === 0 ? [] : count.slice(-count.length % 7),
|
||||||
|
total: count.slice(-count.length % 7).reduce((sum, day) => sum + day, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
weeks,
|
||||||
|
remaining_days: remainingDays,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRangeAcrossMonths(
|
||||||
|
data: any[],
|
||||||
|
startMonth: number,
|
||||||
|
startDay: number,
|
||||||
|
endMonth: number,
|
||||||
|
endDay: number
|
||||||
|
) {
|
||||||
|
const view: number[] = [];
|
||||||
|
const comment: number[] = [];
|
||||||
|
const share: number[] = [];
|
||||||
|
const labels: string[] = [];
|
||||||
|
|
||||||
|
const sortedData = data.sort((a, b) => a.month - b.month);
|
||||||
|
console.log("sorted data", sortedData);
|
||||||
|
for (const monthData of sortedData) {
|
||||||
|
const { month, view: v, comment: c, share: s } = monthData;
|
||||||
|
|
||||||
|
if (month < startMonth || month > endMonth) continue;
|
||||||
|
|
||||||
|
let startIndex = 0;
|
||||||
|
let endIndex = v.length - 1;
|
||||||
|
|
||||||
|
if (month === startMonth) startIndex = startDay - 1;
|
||||||
|
if (month === endMonth) endIndex = endDay - 1;
|
||||||
|
|
||||||
|
for (let i = startIndex; i <= endIndex; i++) {
|
||||||
|
view.push(v[i]);
|
||||||
|
comment.push(c[i]);
|
||||||
|
share.push(s[i]);
|
||||||
|
|
||||||
|
const label = `${(i + 1).toString().padStart(2, "0")} - ${month
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")} `;
|
||||||
|
labels.push(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { view, comment, share, labels };
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApexChartColumnVisitors = (props: {
|
||||||
|
type: string;
|
||||||
|
date: string;
|
||||||
|
view: string[];
|
||||||
|
range: { start: any; end: any };
|
||||||
|
}) => {
|
||||||
|
const { date, type, view, range } = props;
|
||||||
|
const [categories, setCategories] = useState<string[]>([]);
|
||||||
|
const [series, setSeries] = useState<{ name: string; data: number[] }[]>([]);
|
||||||
|
const [seriesComment, setSeriesComment] = useState<number[]>([]);
|
||||||
|
const [seriesView, setSeriesView] = useState<number[]>([]);
|
||||||
|
const [seriesShare, setSeriesShare] = useState<number[]>([]);
|
||||||
|
const [years, setYear] = useState("");
|
||||||
|
const [datas, setDatas] = useState<any>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initFetch();
|
||||||
|
}, [date, type, view, range]);
|
||||||
|
|
||||||
|
const initFetch = async () => {
|
||||||
|
const splitDate = date.split(" ");
|
||||||
|
const splitDateDaily = String(range.start.year);
|
||||||
|
let data = [];
|
||||||
|
console.log(
|
||||||
|
"aaawwww",
|
||||||
|
type === "monthly" && splitDate[1] === years,
|
||||||
|
type === "daily" && splitDateDaily === years
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
(type === "monthly" && splitDate[1] === years) ||
|
||||||
|
(type === "daily" && splitDateDaily === years)
|
||||||
|
) {
|
||||||
|
console.log("if", datas);
|
||||||
|
data = datas;
|
||||||
|
} else {
|
||||||
|
const res = await getStatisticMonthly(
|
||||||
|
type === "monthly" ? splitDate[1] : splitDateDaily
|
||||||
|
);
|
||||||
|
data = res?.data?.data;
|
||||||
|
setDatas(data);
|
||||||
|
}
|
||||||
|
console.log("datas", data);
|
||||||
|
const getDatas = data?.find(
|
||||||
|
(a: any) =>
|
||||||
|
a.month == Number(splitDate[0]) && a.year === Number(splitDate[1])
|
||||||
|
);
|
||||||
|
if (getDatas) {
|
||||||
|
const temp1 = processMonthlyData(getDatas?.comment);
|
||||||
|
const temp2 = processMonthlyData(getDatas?.view);
|
||||||
|
const temp3 = processMonthlyData(getDatas?.share);
|
||||||
|
|
||||||
|
if (type == "weekly") {
|
||||||
|
setSeriesComment(
|
||||||
|
temp1.weeks.map((list) => {
|
||||||
|
return list.total;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setSeriesView(
|
||||||
|
temp2.weeks.map((list) => {
|
||||||
|
return list.total;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setSeriesShare(
|
||||||
|
temp3.weeks.map((list) => {
|
||||||
|
return list.total;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setSeriesComment(getDatas.comment);
|
||||||
|
setSeriesView(getDatas.view);
|
||||||
|
setSeriesShare(getDatas.share);
|
||||||
|
}
|
||||||
|
if (type === "daily") {
|
||||||
|
const mappedData = getRangeAcrossMonths(
|
||||||
|
data,
|
||||||
|
range.start.month,
|
||||||
|
range.start.day,
|
||||||
|
range.end.month,
|
||||||
|
range.end.day
|
||||||
|
);
|
||||||
|
|
||||||
|
setSeriesComment(mappedData.comment);
|
||||||
|
setSeriesView(mappedData.view);
|
||||||
|
setSeriesShare(mappedData.share);
|
||||||
|
setCategories(mappedData.labels);
|
||||||
|
}
|
||||||
|
// if (type === "weekly") {
|
||||||
|
// const category = [];
|
||||||
|
// for (let i = 1; i <= temp1.weeks.length; i++) {
|
||||||
|
// category.push(`Week ${i}`);
|
||||||
|
// }
|
||||||
|
// setCategories(category);
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
setSeriesComment([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setYear(type === "monthly" ? splitDate[1] : splitDateDaily);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const temp = [
|
||||||
|
{
|
||||||
|
name: "Visitors",
|
||||||
|
data: view.includes("visitor") ? seriesView : [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
setSeries(temp);
|
||||||
|
}, [view, seriesShare, seriesView, seriesComment]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full">
|
||||||
|
<div id="chart" className="h-full">
|
||||||
|
<ReactApexChart
|
||||||
|
options={{
|
||||||
|
chart: {
|
||||||
|
height: "100%",
|
||||||
|
type: "area",
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
curve: "smooth",
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
categories: type == "daily" ? categories : [],
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
series={series}
|
||||||
|
type="area"
|
||||||
|
height="100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div id="html-dist"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ApexChartColumnVisitors;
|
||||||
|
|
@ -60,7 +60,14 @@ import {
|
||||||
parseAbsoluteToLocal,
|
parseAbsoluteToLocal,
|
||||||
} from "@internationalized/date";
|
} from "@internationalized/date";
|
||||||
import { Input } from "@heroui/input";
|
import { Input } from "@heroui/input";
|
||||||
import { EyeIconMdi, SearchIcons } from "@/components/icons";
|
import {
|
||||||
|
ChevronLeftIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
EyeIconMdi,
|
||||||
|
SearchIcons,
|
||||||
|
} from "@/components/icons";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import ApexChartColumnVisitors from "./chart/visitor-chart";
|
||||||
|
|
||||||
type ArticleData = Article & {
|
type ArticleData = Article & {
|
||||||
no: number;
|
no: number;
|
||||||
|
|
@ -81,6 +88,21 @@ interface PostCount {
|
||||||
totalArticle: number;
|
totalArticle: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const months = [
|
||||||
|
"Jan",
|
||||||
|
"Feb",
|
||||||
|
"Mar",
|
||||||
|
"Apr",
|
||||||
|
"May",
|
||||||
|
"Jun",
|
||||||
|
"Jul",
|
||||||
|
"Aug",
|
||||||
|
"Sep",
|
||||||
|
"Oct",
|
||||||
|
"Nov",
|
||||||
|
"Dec",
|
||||||
|
];
|
||||||
|
|
||||||
export default function DashboardContainer() {
|
export default function DashboardContainer() {
|
||||||
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
||||||
|
|
||||||
|
|
@ -121,7 +143,6 @@ export default function DashboardContainer() {
|
||||||
endDate: parseDate(convertDateFormatNoTimeV2(new Date())),
|
endDate: parseDate(convertDateFormatNoTimeV2(new Date())),
|
||||||
});
|
});
|
||||||
|
|
||||||
const [typeDate, setTypeDate] = useState("monthly");
|
|
||||||
const [summary, setSummary] = useState<any>();
|
const [summary, setSummary] = useState<any>();
|
||||||
|
|
||||||
const [topPages, setTopPages] = useState<TopPages[]>([]);
|
const [topPages, setTopPages] = useState<TopPages[]>([]);
|
||||||
|
|
@ -134,10 +155,37 @@ export default function DashboardContainer() {
|
||||||
name: string;
|
name: string;
|
||||||
}>();
|
}>();
|
||||||
const roleId = Cookies.get("urie");
|
const roleId = Cookies.get("urie");
|
||||||
|
const today = new Date();
|
||||||
|
|
||||||
const [recapArticlePage, setRecapArticlePage] = useState(1);
|
const [recapArticlePage, setRecapArticlePage] = useState(1);
|
||||||
const [recapArticleTotalPage, setRecapArticleTotalPage] = useState(1);
|
const [recapArticleTotalPage, setRecapArticleTotalPage] = useState(1);
|
||||||
|
|
||||||
|
const [typeDate, setTypeDate] = useState("daily");
|
||||||
|
const [year, setYear] = useState(today.getFullYear());
|
||||||
|
const [selectedMonth, setSelectedMonth] = useState<Date | null>(today);
|
||||||
|
const [viewsDailyDate, setViewsDailyDate] = useState({
|
||||||
|
start: parseDate(convertDateFormatNoTimeV2(today)),
|
||||||
|
end: parseDate(convertDateFormatNoTimeV2(today)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const [typeDateVisitor, setTypeDateVisitor] = useState("daily");
|
||||||
|
const [visitorYear, setVisitorYear] = useState(today.getFullYear());
|
||||||
|
const [visitorSelectedMonth, setVisitorSelectedMonth] = useState<Date | null>(
|
||||||
|
today
|
||||||
|
);
|
||||||
|
const [visitorDailyDate, setVisitorDailyDate] = useState({
|
||||||
|
start: parseDate(convertDateFormatNoTimeV2(today)),
|
||||||
|
end: parseDate(convertDateFormatNoTimeV2(today)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleMonthClick = (monthIndex: number) => {
|
||||||
|
setSelectedMonth(new Date(year, monthIndex, 1));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMonthClickVisitor = (monthIndex: number) => {
|
||||||
|
setVisitorSelectedMonth(new Date(year, monthIndex, 1));
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSummary();
|
fetchSummary();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -246,32 +294,6 @@ export default function DashboardContainer() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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(() => {
|
useEffect(() => {
|
||||||
const temp = Array.from(selectedAccordion);
|
const temp = Array.from(selectedAccordion);
|
||||||
console.log("selecette", temp);
|
console.log("selecette", temp);
|
||||||
|
|
@ -587,141 +609,6 @@ export default function DashboardContainer() {
|
||||||
)}
|
)}
|
||||||
</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 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">
|
<div className="flex justify-between font-semibold">
|
||||||
<p>Top Pages</p>
|
<p>Top Pages</p>
|
||||||
|
|
@ -811,6 +698,248 @@ export default function DashboardContainer() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center min-h-[480px]">
|
||||||
|
<div className="border-1 shadow-sm w-screen rounded-lg lg:w-[50%] p-6 flex flex-col">
|
||||||
|
<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>
|
||||||
|
<div className="border-1 shadow-sm w-screen rounded-lg md:w-[50%] p-6 flex flex-col">
|
||||||
|
<div className="flex justify-between mb-3">
|
||||||
|
<div className="font-semibold flex flex-col">
|
||||||
|
Visitor Analytics
|
||||||
|
</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={[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 && 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">
|
||||||
|
<ApexChartColumnVisitors
|
||||||
|
type={typeDateVisitor}
|
||||||
|
date={`${
|
||||||
|
convertDateFormatNoTimeV2(
|
||||||
|
String(visitorSelectedMonth)
|
||||||
|
).split("-")[1]
|
||||||
|
} ${
|
||||||
|
convertDateFormatNoTimeV2(
|
||||||
|
String(visitorSelectedMonth)
|
||||||
|
).split("-")[0]
|
||||||
|
}`}
|
||||||
|
view={["visitor"]}
|
||||||
|
range={visitorDailyDate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange} size="3xl">
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange} size="3xl">
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue