feat:user analytic
This commit is contained in:
parent
cc45c2dabb
commit
686d29439c
|
|
@ -69,98 +69,98 @@ export default function Login() {
|
|||
error("Username & Password Wajib Diisi !");
|
||||
} else {
|
||||
// login dengan otp
|
||||
loading();
|
||||
const response: any = await emailValidation(data);
|
||||
if (response?.error) {
|
||||
console.log("error", response);
|
||||
if (response?.message?.messages[0]?.includes("failed to send mail")) {
|
||||
error("Gagal Mengirim OTP");
|
||||
return false;
|
||||
}
|
||||
// loading();
|
||||
// const response: any = await emailValidation(data);
|
||||
// if (response?.error) {
|
||||
// console.log("error", response);
|
||||
// if (response?.message?.messages[0]?.includes("failed to send mail")) {
|
||||
// error("Gagal Mengirim OTP");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if (response?.message?.messages[0]?.includes("username")) {
|
||||
error("Username / Password Tidak Sesuai");
|
||||
return false;
|
||||
}
|
||||
error("Unknown Error");
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
if (response?.data?.messages[0] === "Continue to setup email") {
|
||||
setFirstLogin(true);
|
||||
} else {
|
||||
setNeedOtp(true);
|
||||
}
|
||||
// if (response?.message?.messages[0]?.includes("username")) {
|
||||
// error("Username / Password Tidak Sesuai");
|
||||
// return false;
|
||||
// }
|
||||
// error("Unknown Error");
|
||||
// return false;
|
||||
// }
|
||||
// close();
|
||||
// if (response?.data?.messages[0] === "Continue to setup email") {
|
||||
// setFirstLogin(true);
|
||||
// } else {
|
||||
// setNeedOtp(true);
|
||||
// }
|
||||
|
||||
// login tanpa otp
|
||||
// loading();
|
||||
// const response = await postSignIn(data);
|
||||
// if (response?.error) {
|
||||
// error("Username / Password Tidak Sesuai");
|
||||
// } else {
|
||||
// const profile = await getProfile(response?.data?.data?.access_token);
|
||||
// const dateTime: any = new Date();
|
||||
loading();
|
||||
const response = await postSignIn(data);
|
||||
if (response?.error) {
|
||||
error("Username / Password Tidak Sesuai");
|
||||
} else {
|
||||
const profile = await getProfile(response?.data?.data?.access_token);
|
||||
const dateTime: any = new Date();
|
||||
|
||||
// const newTime: any = dateTime.getTime() + 10 * 60 * 1000;
|
||||
const newTime: any = dateTime.getTime() + 10 * 60 * 1000;
|
||||
|
||||
// Cookies.set("access_token", response?.data?.data?.access_token, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("refresh_token", response?.data?.data?.refresh_token, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("time_refresh", newTime, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("is_first_login", "true", {
|
||||
// secure: true,
|
||||
// sameSite: "strict",
|
||||
// });
|
||||
// const resActivity = await saveActivity(
|
||||
// {
|
||||
// activityTypeId: 1,
|
||||
// url: "https://kontenhumas.com/auth",
|
||||
// userId: profile?.data?.data?.id,
|
||||
// },
|
||||
// accessData?.id_token
|
||||
// );
|
||||
// Cookies.set("profile_picture", profile?.data?.data?.profilePictureUrl, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("uie", profile?.data?.data?.id, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("ufne", profile?.data?.data?.fullname, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("ulie", profile?.data?.data?.userLevelGroup, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("username", profile?.data?.data?.username, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("urie", profile?.data?.data?.userRoleId, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("masterPoldaId", profile?.data?.data?.masterPoldaId, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// Cookies.set("ulne", profile?.data?.data?.userLevelId, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// // Cookies.set("urce", profile?.data?.data?.roleCode, {
|
||||
// // expires: 1,
|
||||
// // });
|
||||
// Cookies.set("email", profile?.data?.data?.email, {
|
||||
// expires: 1,
|
||||
// });
|
||||
// router.push("/admin/dashboard");
|
||||
// Cookies.set("status", "login", {
|
||||
// expires: 1,
|
||||
// });
|
||||
Cookies.set("access_token", response?.data?.data?.access_token, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("refresh_token", response?.data?.data?.refresh_token, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("time_refresh", newTime, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("is_first_login", "true", {
|
||||
secure: true,
|
||||
sameSite: "strict",
|
||||
});
|
||||
const resActivity = await saveActivity(
|
||||
{
|
||||
activityTypeId: 1,
|
||||
url: "https://kontenhumas.com/auth",
|
||||
userId: profile?.data?.data?.id,
|
||||
},
|
||||
accessData?.id_token
|
||||
);
|
||||
Cookies.set("profile_picture", profile?.data?.data?.profilePictureUrl, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("uie", profile?.data?.data?.id, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("ufne", profile?.data?.data?.fullname, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("ulie", profile?.data?.data?.userLevelGroup, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("username", profile?.data?.data?.username, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("urie", profile?.data?.data?.userRoleId, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("masterPoldaId", profile?.data?.data?.masterPoldaId, {
|
||||
expires: 1,
|
||||
});
|
||||
Cookies.set("ulne", profile?.data?.data?.userLevelId, {
|
||||
expires: 1,
|
||||
});
|
||||
// Cookies.set("urce", profile?.data?.data?.roleCode, {
|
||||
// expires: 1,
|
||||
// });
|
||||
Cookies.set("email", profile?.data?.data?.email, {
|
||||
expires: 1,
|
||||
});
|
||||
router.push("/admin/dashboard");
|
||||
Cookies.set("status", "login", {
|
||||
expires: 1,
|
||||
});
|
||||
|
||||
// close();
|
||||
// }
|
||||
close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ function getRangeAcrossMonths(
|
|||
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;
|
||||
|
||||
|
|
@ -75,7 +74,7 @@ function getRangeAcrossMonths(
|
|||
|
||||
const label = `${(i + 1).toString().padStart(2, "0")} - ${month
|
||||
.toString()
|
||||
.padStart(2, "0")} `;
|
||||
.padStart(2, "0")}`;
|
||||
labels.push(label);
|
||||
}
|
||||
}
|
||||
|
|
@ -106,16 +105,11 @@ const ApexChartColumn = (props: {
|
|||
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(
|
||||
|
|
@ -124,7 +118,6 @@ const ApexChartColumn = (props: {
|
|||
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])
|
||||
|
|
|
|||
|
|
@ -0,0 +1,369 @@
|
|||
import React, { useState, useCallback, useEffect, useRef } from "react";
|
||||
import ReactApexChart from "react-apexcharts";
|
||||
import ApexCharts from "apexcharts";
|
||||
import { init } from "next/dist/compiled/webpack/webpack";
|
||||
|
||||
const wilayah = ["sumut", "bali", "jateng", "jabar", "metro", "papua", "riau"];
|
||||
|
||||
type TypeItem = {
|
||||
year: number;
|
||||
month: number;
|
||||
user: {
|
||||
list: { name: string; count: number }[];
|
||||
}[];
|
||||
};
|
||||
|
||||
interface MappedData {
|
||||
result: { x: string; y: number }[] | [];
|
||||
details: { name: string; count: number }[][];
|
||||
}
|
||||
|
||||
const generateDummyData = () => {
|
||||
const getDaysInMonth = (year: number, month: number) => {
|
||||
return new Date(year, month, 0).getDate();
|
||||
};
|
||||
|
||||
const dummy = [];
|
||||
|
||||
for (let month = 1; month <= 7; month++) {
|
||||
const userData = [];
|
||||
const daysInMonth = getDaysInMonth(2025, month);
|
||||
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
// Buat 2-4 wilayah acak per hari
|
||||
const regionCount = Math.floor(Math.random() * 3) + 2;
|
||||
const usedRegions = wilayah
|
||||
.sort(() => 0.5 - Math.random())
|
||||
.slice(0, regionCount);
|
||||
|
||||
const list = usedRegions.map((name) => ({
|
||||
name,
|
||||
count: Math.floor(Math.random() * 10) + 1,
|
||||
}));
|
||||
|
||||
userData.push({ list });
|
||||
}
|
||||
|
||||
dummy.push({
|
||||
year: 2025,
|
||||
month,
|
||||
user: userData,
|
||||
});
|
||||
}
|
||||
|
||||
return dummy;
|
||||
};
|
||||
|
||||
const dummy = generateDummyData();
|
||||
|
||||
const colors = ["#008FFB"];
|
||||
|
||||
export const makeDataByMonth = (
|
||||
data: TypeItem[],
|
||||
month: number
|
||||
): MappedData => {
|
||||
const result: { x: string; y: number }[] = [];
|
||||
const details: { name: string; count: number }[][] = [];
|
||||
|
||||
const filtered = data.find((entry) => entry.month === month);
|
||||
|
||||
if (!filtered) return { result: [], details: [] };
|
||||
|
||||
filtered.user.forEach((u, idx) => {
|
||||
const total = u.list.reduce((sum, item) => sum + item.count, 0);
|
||||
details.push(u.list);
|
||||
console.log("u.list", u.list);
|
||||
result.push({
|
||||
x: (idx + 1).toString(),
|
||||
y: total,
|
||||
});
|
||||
});
|
||||
|
||||
return { result, details };
|
||||
};
|
||||
|
||||
export const makeDataByRange = (
|
||||
data: TypeItem[],
|
||||
startMonth: number,
|
||||
startDay: number,
|
||||
endMonth: number,
|
||||
endDay: number
|
||||
) => {
|
||||
const user: number[] = [];
|
||||
const labels: string[] = [];
|
||||
const details = [];
|
||||
const result: { x: string; y: number }[] = [];
|
||||
const sortedData = data.sort((a, b) => a.month - b.month);
|
||||
for (const monthData of sortedData) {
|
||||
const { month, user: u } = monthData;
|
||||
|
||||
if (month < startMonth || month > endMonth) continue;
|
||||
|
||||
let startIndex = 0;
|
||||
let endIndex = u.length - 1;
|
||||
|
||||
if (month === startMonth) startIndex = startDay - 1;
|
||||
if (month === endMonth) endIndex = endDay - 1;
|
||||
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
const userEntry = u[i];
|
||||
|
||||
if (!userEntry) continue;
|
||||
|
||||
const total = userEntry.list.reduce((sum, item) => sum + item.count, 0);
|
||||
user.push(total);
|
||||
details.push(userEntry.list);
|
||||
const label = `${(i + 1).toString().padStart(2, "0")} - ${month
|
||||
.toString()
|
||||
.padStart(2, "0")}`;
|
||||
labels.push(label);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < user.length; i++) {
|
||||
result.push({ x: labels[i], y: user[i] });
|
||||
}
|
||||
|
||||
return { result, details };
|
||||
};
|
||||
|
||||
const ApexChartDynamic = (props: {
|
||||
type: string;
|
||||
date: string;
|
||||
range: { start: any; end: any };
|
||||
}) => {
|
||||
const { date, type, range } = props;
|
||||
const [state, setState] = useState<{
|
||||
series: ApexAxisChartSeries;
|
||||
options: ApexCharts.ApexOptions;
|
||||
seriesQuarter: ApexAxisChartSeries;
|
||||
}>({
|
||||
series: [{ data: [] }],
|
||||
options: {
|
||||
chart: {
|
||||
id: "barYear",
|
||||
height: 600,
|
||||
width: "100%",
|
||||
type: "bar",
|
||||
events: {
|
||||
dataPointSelection: function (e, chart, opts) {
|
||||
const quarterChartEl = document.querySelector("#chart-quarter");
|
||||
const yearChartEl = document.querySelector("#chart-year");
|
||||
|
||||
if (!quarterChartEl || !yearChartEl) return;
|
||||
|
||||
if (opts.selectedDataPoints[0].length === 1) {
|
||||
if (quarterChartEl.classList.contains("active")) {
|
||||
updateQuarterChart(chart, "barQuarter");
|
||||
} else {
|
||||
yearChartEl.classList.add("chart-quarter-activated");
|
||||
quarterChartEl.classList.add("active");
|
||||
updateQuarterChart(chart, "barQuarter");
|
||||
}
|
||||
} else {
|
||||
updateQuarterChart(chart, "barQuarter");
|
||||
}
|
||||
|
||||
if (opts.selectedDataPoints[0].length === 0) {
|
||||
yearChartEl.classList.remove("chart-quarter-activated");
|
||||
quarterChartEl.classList.remove("active");
|
||||
}
|
||||
},
|
||||
updated: function (chart) {
|
||||
updateQuarterChart(chart, "barQuarter");
|
||||
},
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
distributed: true,
|
||||
horizontal: true,
|
||||
barHeight: "100%",
|
||||
dataLabels: {
|
||||
position: "bottom",
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
textAnchor: "start",
|
||||
style: {
|
||||
colors: ["#fff"],
|
||||
},
|
||||
formatter: function (_val, opt) {
|
||||
return opt.w.globals.labels[opt.dataPointIndex];
|
||||
},
|
||||
offsetX: 0,
|
||||
dropShadow: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
colors: colors,
|
||||
states: {
|
||||
normal: {
|
||||
filter: {
|
||||
type: "desaturate",
|
||||
},
|
||||
},
|
||||
active: {
|
||||
allowMultipleDataPointsSelection: false,
|
||||
filter: {
|
||||
type: "darken",
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
x: { show: false },
|
||||
y: {
|
||||
title: {
|
||||
formatter: ((_seriesName: string, opts: any) =>
|
||||
opts.w.globals.labels[opts.dataPointIndex]) as (
|
||||
seriesName: string
|
||||
) => string,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
yaxis: {
|
||||
labels: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
seriesQuarter: [{ data: [] }],
|
||||
});
|
||||
|
||||
const [years, setYear] = useState("");
|
||||
const [datas, setDatas] = useState<any>([]);
|
||||
const [details, setDetails] = useState<{ name: string; count: number }[][]>(
|
||||
[]
|
||||
);
|
||||
const detailsRef = useRef(details);
|
||||
|
||||
useEffect(() => {
|
||||
detailsRef.current = details;
|
||||
}, [details]);
|
||||
|
||||
useEffect(() => {
|
||||
initFetch();
|
||||
}, [date, range.start, range.end, type]);
|
||||
|
||||
const initFetch = async () => {
|
||||
const splitDate = date.split(" ");
|
||||
const splitDateDaily = String(range.start.year);
|
||||
const currentYear = type === "monthly" ? splitDate[1] : splitDateDaily;
|
||||
|
||||
let data = [];
|
||||
|
||||
if (currentYear === years) {
|
||||
console.log("if", datas);
|
||||
data = datas;
|
||||
} else {
|
||||
// const res = await getStatisticMonthly(
|
||||
// type === "monthly" ? splitDate[1] : splitDateDaily
|
||||
// );
|
||||
// data = res?.data?.data;
|
||||
data = dummy;
|
||||
console.log("dataaa", data);
|
||||
setDatas(data);
|
||||
setYear(currentYear);
|
||||
}
|
||||
// console.log("datas", data);
|
||||
if (data) {
|
||||
if (type == "monthly") {
|
||||
const mappedData: MappedData = makeDataByMonth(
|
||||
data,
|
||||
Number(splitDate[0])
|
||||
);
|
||||
console.log("mapped month", mappedData);
|
||||
setDetails(mappedData.details);
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
series: [{ data: mappedData.result }],
|
||||
}));
|
||||
}
|
||||
|
||||
if (type == "daily") {
|
||||
const mappedData = makeDataByRange(
|
||||
data,
|
||||
range.start.month,
|
||||
range.start.day,
|
||||
range.end.month,
|
||||
range.end.day
|
||||
);
|
||||
console.log("mmapped,", mappedData.details);
|
||||
setDetails(mappedData.details);
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
series: [{ data: mappedData.result }],
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
series: [{ data: [] }],
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const updateQuarterChart = useCallback(
|
||||
(chart: any, id: string) => {
|
||||
const selectedIndex = chart?.w?.config?.series[0]?.data?.findIndex(
|
||||
(d: any, i: number) => {
|
||||
return chart.w.globals.selectedDataPoints[0]?.includes(i);
|
||||
}
|
||||
);
|
||||
|
||||
if (selectedIndex !== -1) {
|
||||
const counts = detailsRef.current[selectedIndex];
|
||||
console.log("countres", counts, detailsRef);
|
||||
|
||||
const quarterData = [
|
||||
{
|
||||
name: `${selectedIndex + 1}`,
|
||||
data: counts,
|
||||
},
|
||||
];
|
||||
ApexCharts.exec(id, "updateSeries", quarterData);
|
||||
|
||||
setState((prev: any) => ({
|
||||
...prev,
|
||||
seriesQuarter: quarterData,
|
||||
}));
|
||||
}
|
||||
},
|
||||
[detailsRef]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="lg:h-[600px]">
|
||||
<div id="wrap" className="flex flex-col lg:flex-row gap-2">
|
||||
<div id="chart-year" className="lg:w-[80%] h-[600px]">
|
||||
<ReactApexChart
|
||||
options={state.options}
|
||||
series={state.series}
|
||||
type="bar"
|
||||
height={600}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="chart-quarter" className="w-full lg:w-[20%]">
|
||||
<div className="flex flex-col gap-1">
|
||||
{state.seriesQuarter[0].data.map((list: any, index) => (
|
||||
<div key={index} className="flex flex-row gap-2">
|
||||
<p className="font-semibold capitalize">{list?.name} : </p>
|
||||
<p>{list?.count}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApexChartDynamic;
|
||||
|
|
@ -56,7 +56,6 @@ function getRangeAcrossMonths(
|
|||
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;
|
||||
|
||||
|
|
@ -106,16 +105,11 @@ const ApexChartColumnVisitors = (props: {
|
|||
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(
|
||||
|
|
@ -124,7 +118,6 @@ const ApexChartColumnVisitors = (props: {
|
|||
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])
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ import {
|
|||
import { format } from "date-fns";
|
||||
import ApexChartColumnVisitors from "./chart/visitor-chart";
|
||||
import { IndonesiaMap } from "@/components/ui/maps-charts";
|
||||
import ApexChartDynamic from "./chart/dynamic-bar-char";
|
||||
|
||||
type ArticleData = Article & {
|
||||
no: number;
|
||||
|
|
@ -198,7 +199,11 @@ export default function DashboardContainer() {
|
|||
today
|
||||
);
|
||||
const [visitorDailyDate, setVisitorDailyDate] = useState({
|
||||
start: parseDate(convertDateFormatNoTimeV2(today)),
|
||||
start: parseDate(
|
||||
convertDateFormatNoTimeV2(
|
||||
new Date(new Date().setDate(new Date().getDate() - 30))
|
||||
)
|
||||
),
|
||||
end: parseDate(convertDateFormatNoTimeV2(today)),
|
||||
});
|
||||
|
||||
|
|
@ -722,7 +727,7 @@ export default function DashboardContainer() {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center min-h-[480px]">
|
||||
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center lg:h-[700px]">
|
||||
<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">
|
||||
|
|
@ -861,11 +866,9 @@ export default function DashboardContainer() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-1 shadow-sm w-screen rounded-lg md:w-[50%] p-6 flex flex-col">
|
||||
<div className="border-1 shadow-sm w-screen rounded-lg md:w-[50%] p-6 flex flex-col h-[700px]">
|
||||
<div className="flex justify-between mb-3">
|
||||
<div className="font-semibold flex flex-col">
|
||||
Visitor Analytics
|
||||
</div>
|
||||
<div className="font-semibold flex flex-col">Users 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]"
|
||||
|
|
@ -937,7 +940,7 @@ export default function DashboardContainer() {
|
|||
<DateRangePicker
|
||||
className="h-[40px]"
|
||||
value={visitorDailyDate}
|
||||
onChange={(e) => e !== null && setViewsDailyDate(e)}
|
||||
onChange={(e) => e !== null && setVisitorDailyDate(e)}
|
||||
label=""
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -946,7 +949,16 @@ export default function DashboardContainer() {
|
|||
</div>
|
||||
<div className="flex flex-row w-full h-full">
|
||||
<div className="w-full h-[30vh] lg:h-full text-black">
|
||||
<ApexChartColumnVisitors
|
||||
<ApexChartDynamic
|
||||
key={`${
|
||||
convertDateFormatNoTimeV2(
|
||||
String(visitorSelectedMonth)
|
||||
).split("-")[1]
|
||||
} ${
|
||||
convertDateFormatNoTimeV2(
|
||||
String(visitorSelectedMonth)
|
||||
).split("-")[0]
|
||||
}-${typeDateVisitor}-${visitorDailyDate}`}
|
||||
type={typeDateVisitor}
|
||||
date={`${
|
||||
convertDateFormatNoTimeV2(
|
||||
|
|
@ -957,7 +969,6 @@ export default function DashboardContainer() {
|
|||
String(visitorSelectedMonth)
|
||||
).split("-")[0]
|
||||
}`}
|
||||
view={["visitor"]}
|
||||
range={visitorDailyDate}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -965,19 +976,124 @@ export default function DashboardContainer() {
|
|||
</div>
|
||||
</div>
|
||||
{roleId && Number(roleId) < 3 && (
|
||||
<div className="flex flex-row gap-6 w-full">
|
||||
<div className="w-screen md:w-[80%] h-[360px] lg:h-[600px]">
|
||||
<IndonesiaMap />
|
||||
<div>
|
||||
<div className="flex flex-col lg:flex-row gap-6 w-full pr-2 md:pr-0">
|
||||
<div className="w-screen md:w-[80%] h-[360px] lg:h-[600px]">
|
||||
<IndonesiaMap />
|
||||
</div>
|
||||
<div className="w-screen md:w-[300px] h-[360px] lg:h-[600px] overflow-auto gap-2 flex flex-col">
|
||||
{dummyData.map((list) => (
|
||||
<div key={list.id} className="w-full flex flex-row gap-2">
|
||||
<p className="w-1/2">{list.name}</p>{" "}
|
||||
<p className="w-1/2 bg-[#0e7490] text-white border-white ">
|
||||
{list.value}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-screen md:w-[300px] h-[360px] lg:h-[600px] overflow-auto gap-2 flex flex-col">
|
||||
{dummyData.map((list) => (
|
||||
<div key={list.id} className="w-full flex flex-row gap-2">
|
||||
<p className="w-1/2">{list.name}</p>{" "}
|
||||
<p className="w-1/2 bg-[#0e7490] text-white border-white ">
|
||||
{list.value}
|
||||
</p>
|
||||
<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
|
||||
</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 && 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]
|
||||
}`}
|
||||
view={["visitor"]}
|
||||
range={visitorDailyDate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue