fix:comment article, dashboard recap data

This commit is contained in:
Rama Priyanto 2025-06-17 18:44:26 +07:00
parent 5f173def51
commit f0bd7e2513
4 changed files with 183 additions and 146 deletions

View File

@ -21,6 +21,7 @@ import {
import { getArticleById } from "@/services/article";
import Link from "next/link";
import { postArticleComment } from "@/services/master-user";
import Cookies from "js-cookie";
interface DetailComments {
id: number;
@ -98,6 +99,8 @@ export default function ReviewComment() {
const sendComment = async () => {
const data = {
commentFromEmail: "ppid@polri.go.id",
commentFromName: "Mabes Polri",
articleId: detailData?.articleId,
isPublic: true,
message: replyValue,

View File

@ -16,6 +16,7 @@ import {
Checkbox,
CheckboxGroup,
CheckboxIcon,
DateRangePicker,
Image,
Modal,
ModalBody,
@ -51,8 +52,14 @@ import {
convertDateFormatNoTimeV2,
textEllipsis,
} from "@/utils/global";
import { parseDate, getLocalTimeZone } from "@internationalized/date";
import {
parseDate,
getLocalTimeZone,
parseZonedDateTime,
parseAbsoluteToLocal,
} from "@internationalized/date";
import { Input } from "@heroui/input";
import { EyeIconMdi } from "@/components/icons";
type ArticleData = Article & {
no: number;
@ -94,12 +101,14 @@ export default function DashboardContainer() {
);
const [postContentDate, setPostContentDate] = useState({
startDate: parseDate(
convertDateFormatNoTimeV2(
start: parseZonedDateTime(
`${convertDateFormatNoTimeV2(
new Date(new Date().setDate(new Date().getDate() - 7))
)
)}T00:00[Asia/Jakarta]`
),
end: parseZonedDateTime(
`${convertDateFormatNoTimeV2(new Date())}T23:59[Asia/Jakarta]`
),
endDate: parseDate(convertDateFormatNoTimeV2(new Date())),
});
const [topContentDate, setTopContentDate] = useState({
@ -119,10 +128,12 @@ export default function DashboardContainer() {
const [polresData, setPolresData] = useState<any>({});
const [selectedAccordion, setSelectedAccordion] = useState<any>(new Set([]));
const [recapArticleList, setRecapArticleList] = useState<any>([]);
const [timeValue, setTimeValue] = useState("--:--");
const [selectedUnit, setSelectedUnit] = useState("");
const roleId = Cookies.get("urie");
const [recapArticlePage, setRecapArticlePage] = useState(1);
const [recapArticleTotalPage, setRecapArticleTotalPage] = useState(1);
useEffect(() => {
fetchSummary();
}, []);
@ -172,7 +183,7 @@ export default function DashboardContainer() {
endDate: getDate(topContentDate.endDate),
};
const res = await getTopArticles(req);
setTopPages(getTableNumber(10, res.data?.data));
setTopPages(getTableNumber(10, res.data?.data, "topPages"));
setTopPagesTotalPage(res?.data?.meta?.totalPage);
}
@ -187,15 +198,22 @@ export default function DashboardContainer() {
}`;
};
const res = await getUserLevelDataStat(
getDate(postContentDate.startDate),
getDate(postContentDate.endDate)
getDate(postContentDate.start),
getDate(postContentDate.end),
`${getTime(postContentDate.start.hour)}:${getTime(
postContentDate.start.minute
)}:00`,
`${getTime(postContentDate.end.hour)}:${getTime(
postContentDate.end.minute
)}:00`
);
setPostCount(getTableNumberStats(res?.data?.data));
}
const getTableNumber = (limit: number, data: any) => {
const getTableNumber = (limit: number, data: any, type: string) => {
if (data) {
const startIndex = limit * (topPagespage - 1);
const startIndex =
limit * (type == "topPages" ? topPagespage - 1 : recapArticlePage - 1);
let iterate = 0;
const newData = data.map((value: any) => {
iterate++;
@ -259,15 +277,26 @@ export default function DashboardContainer() {
}
}, [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.startDate),
getDate(postContentDate.endDate),
getDate(postContentDate.start),
getDate(postContentDate.end),
`${getTime(postContentDate.start.hour)}:${getTime(
postContentDate.start.minute
)}:00`,
`${getTime(postContentDate.end.hour)}-${getTime(
postContentDate.end.minute
)}:00`,
id
);
const polresNowData = getTableNumberStats(res?.data?.data);
@ -293,18 +322,27 @@ export default function DashboardContainer() {
}
};
const openModalArticle = async (limit: number) => {
useEffect(() => {
getRecapArticle();
}, [recapArticlePage]);
const getRecapArticle = async () => {
const req = {
limit: `${limit}`,
page: topPagespage,
limit: `10`,
page: recapArticlePage,
search: "",
sort: "desc",
startDate: getDate(topContentDate.startDate),
endDate: getDate(topContentDate.endDate),
};
const res = await getTopArticles(req);
setRecapArticleList(getTableNumber(10, res.data?.data));
// setTopPagesTotalPage(res?.data?.meta?.totalPage);
setRecapArticleList(getTableNumber(10, res.data?.data, "recapArticle"));
setRecapArticleTotalPage(res?.data?.meta?.totalPage);
};
const openModalArticle = async () => {
setRecapArticlePage(1);
getRecapArticle();
onOpen();
};
@ -365,75 +403,28 @@ export default function DashboardContainer() {
</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">
<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">
Rekapitulasi Post Berita Polda/Polres Pada Website
</p>
<div className="w-[300px] flex flex-row gap-2 justify-between font-semibold items-center">
<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>
<Input
type="time"
variant="bordered"
className="w-fit "
value={timeValue}
onValueChange={setTimeValue}
endContent={
<a>
<CheckboxIcon />
</a>
}
classNames={{
inputWrapper: [
"border-none rounded-lg !h-[30px] lg:h-[40px]",
"dark:group-data-[focused=false]:bg-transparent border-none",
],
}}
/>
</div>
<DateRangePicker
hideTimeZone
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"
/>
</div>
<div className="flex flex-row border-b-1 gap-1 py-1 items-center">
<div className="w-[5%]">NO</div>
@ -469,73 +460,93 @@ export default function DashboardContainer() {
</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>
selectedCategory === "polda" ? (
<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
className={`w-[10%] text-center ${
list?.totalArticle === 0 &&
"bg-red-600 text-white "
}`}
key={list.userLevelId}
className="flex flex-row gap-1 py-1"
>
{list?.totalArticle}
</div>
</div>
}
>
{polresData[list?.userLevelId] ? (
polresData[list?.userLevelId]?.map(
(child: any, index: number) => (
<div className="w-[5%]">{list?.no}</div>
<div className="w-[85%]">{list?.userLevelName}</div>
<div
key={child.userLevelId}
className={`flex flex-row gap-1 py-1 ${
index ===
polresData[list?.userLevelId].length - 1
? ""
: "border-b-1"
className={`w-[10%] text-center ${
list?.totalArticle === 0 &&
"bg-red-600 text-white "
}`}
>
<div className="w-[5%]"></div>
<div className="w-[85%]">
{child?.userLevelName}
</div>
<a
onClick={() => {
if (list?.totalArticle !== 0) {
setSelectedUnit(child?.userLevelName);
openModalArticle(list?.totalArticle);
}
}}
className={`w-[10%] text-start cursor-pointer ${
list?.totalArticle === 0 &&
"bg-red-600 text-white cursor-default"
{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"
}`}
>
{child?.totalArticle}
</a>
</div>
<div className="w-[5%]"></div>
<div className="w-[85%]">
{child?.userLevelName}
</div>
<a
onClick={() => {
if (list?.totalArticle !== 0) {
setSelectedUnit(child?.userLevelName);
openModalArticle();
}
}}
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 "
}`}
>
{child?.totalArticle}
<EyeIconMdi size={12} />
</a>
</div>
)
)
)
) : (
<Skeleton className="h-3 w-full rounded-lg" />
)}
</AccordionItem>
))}
</Accordion>
) : (
<Skeleton className="h-3 w-full rounded-lg" />
)}
</AccordionItem>
))}
</Accordion>
) : (
<div className="flex flex-row gap-1 py-1">
<div className="w-[5%]">-</div>
<div className="w-[85%]">Article Seluruh Polres</div>
<div className={`w-[10%] text-center `}>
<a
onClick={() => {
setSelectedUnit("Seluruh Polres");
openModalArticle();
}}
className="flex flex-row gap-1 items-center cursor-pointer"
>
122
<EyeIconMdi size={12} />
</a>
</div>
</div>
)
) : (
postCount?.map((list) => (
<div
@ -788,7 +799,7 @@ export default function DashboardContainer() {
<ModalHeader className="flex flex-col gap-1">
{selectedUnit} List Content
</ModalHeader>
<ModalBody>
<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>
@ -814,6 +825,25 @@ export default function DashboardContainer() {
))}
</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>
</>
)}

View File

@ -91,6 +91,8 @@ export default function Comment(props: { id: string | null }) {
}
const data = {
commentFromName: values.name,
commentFromEmail: values.email,
articleId: Number(id),
isPublic: false,
message: values.comment,

View File

@ -191,6 +191,8 @@ export async function deleteArticleFiles(id: number) {
export async function getUserLevelDataStat(
startDate: string,
endDate: string,
startTime: string,
endTime: string,
levelId?: number
) {
// const headers = {
@ -198,7 +200,7 @@ export async function getUserLevelDataStat(
// Authorization: `Bearer ${token}`,
// };
return await httpGetInterceptor(
`/articles/statistic/user-levels?startDate=${startDate}&endDate=${endDate}&userLevelId=${
`/articles/statistic/user-levels?startDate=${startDate}&endDate=${endDate}&startTime=${startTime}&endTime=${endTime}&userLevelId=${
levelId || ""
}`
);