feat:update agenda setting,contest,filter SPIT, publish content

This commit is contained in:
Anang Yusman 2025-02-17 22:56:47 +08:00
parent 29bfb3c598
commit f9a54ceae5
8 changed files with 398 additions and 46 deletions

View File

@ -575,7 +575,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
<CardContent className="p-0">
<CardHeader className="border-none mb-2 pt-5">
{roleId == 11 || roleId == 2 || roleId == 12 ? (
{roleId == 3 || roleId == 11 || roleId == 2 || roleId == 12 ? (
<Button
onClick={handleDateClick}
className="dark:bg-background dark:text-foreground"

View File

@ -111,30 +111,57 @@ const columns: ColumnDef<any>[] = [
},
},
{
accessorKey: "statusName",
accessorKey: "statusId",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
};
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase();
const statusStyles =
statusColors[statusName] || "bg-gray-100 text-gray-600";
const userLevelId = 2; // Gantilah sesuai dengan konteks aplikasi
const statusId = Number(row.getValue("statusId"));
const reviewedAtLevel = row.original.reviewedAtLevel as string | null;
const needApprovalFromLevel = Number(row.original.needApprovalFromLevel);
let statusName = row.getValue("statusName") as string;
let icon = "fa:times-circle";
let statusClass = "bg-gray-100 text-gray-600";
if (
(statusId === 2 &&
reviewedAtLevel !== null &&
!reviewedAtLevel.includes(`:${userLevelId}:`)) ||
(statusId === 1 && needApprovalFromLevel === userLevelId)
) {
statusName = "Menunggu Review";
icon = "fa:hourglass-end";
statusClass = "bg-orange-100 text-orange-600";
} else if (
statusId === 2 &&
reviewedAtLevel?.includes(`:${userLevelId}:`)
) {
icon = "fa:check-circle";
statusClass = "bg-green-100 text-green-600";
} else if (statusId === 2) {
icon = "fa:check-circle";
statusClass = "bg-green-100 text-green-600";
} else if (statusId === 3) {
icon = "fa:comment";
statusClass = "bg-blue-100 text-blue-600";
} else if (statusId === 1) {
icon = "fa:hourglass-end";
statusClass = "bg-orange-100 text-orange-600";
}
return (
<Badge
className={cn(
"rounded-full px-5 w-full whitespace-nowrap",
statusStyles
statusClass
)}
>
{status}
{statusName}
</Badge>
);
},
},
{
id: "actions",
accessorKey: "action",

View File

@ -25,6 +25,7 @@ import {
import { Button } from "@/components/ui/button";
import {
ChevronDown,
ChevronLeft,
ChevronRight,
Eye,
@ -40,6 +41,12 @@ import { listSPIT } from "@/service/content/content";
import { useSearchParams } from "next/navigation";
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
import columns from "./columns";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Label } from "@/components/ui/label";
// export type CompanyData = {
// no: number;
@ -76,7 +83,7 @@ const TableSPIT = () => {
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [statusFilter, setStatusFilter] = React.useState([1, 2]);
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
const roleId = getCookiesDecrypt("urie");
@ -110,26 +117,23 @@ const TableSPIT = () => {
React.useEffect(() => {
fetchData();
}, [page, limit, search]);
}, [page, limit, search, statusFilter]);
async function fetchData() {
let isPublish;
if (statusFilter.length > 1) {
isPublish = "";
} else if (statusFilter.length === 1) {
if (statusFilter.includes(1)) {
isPublish = false;
} else {
isPublish = true;
}
if (statusFilter.length === 0) {
isPublish = ""; // Tidak ada filter, tampilkan semua data
} else if (statusFilter.length > 1) {
isPublish = ""; // Tidak spesifik, bisa ditafsirkan sebagai semua
} else {
isPublish = undefined;
isPublish = statusFilter.includes(1) ? false : true;
}
try {
const res = await listSPIT(page - 1, limit, search, isPublish);
const data = res?.data?.data;
const contentData = data?.content;
const contentData = data?.content || [];
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
});
@ -149,6 +153,14 @@ const TableSPIT = () => {
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
};
function handleStatusCheckboxChange(value: any) {
setStatusFilter((prev: any) =>
prev.includes(value)
? prev.filter((status: any) => status !== value)
: [...prev, value]
);
}
return (
<div className="w-full">
<div className="flex justify-between items-center px-5">
@ -166,17 +178,47 @@ const TableSPIT = () => {
/>
</InputGroup>
</div>
<div className="flex-none">
<Input
placeholder="Filter Status..."
value={
(table.getColumn("isPublish")?.getFilterValue() as string) ?? ""
}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
table.getColumn("isPublish")?.setFilterValue(event.target.value)
}
className="max-w-sm "
/>
<div className="flex items-center py-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto" size="md">
Filter <ChevronDown />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-64 h-[150px] overflow-y-auto"
>
<div className="flex flex-row justify-between my-1 mx-1">
<p>Filter</p>
</div>
<Label className="ml-2 mt-2">Status</Label>
<div className="flex items-center px-4 py-1">
<input
type="checkbox"
id="status-2"
className="mr-2"
checked={statusFilter.includes(1)}
onChange={() => handleStatusCheckboxChange(1)}
/>
<label htmlFor="status-2" className="text-sm">
Menunggu Review
</label>
</div>
<div className="flex items-center px-4 py-1">
<input
type="checkbox"
id="status-2"
className="mr-2"
checked={statusFilter.includes(2)}
onChange={() => handleStatusCheckboxChange(2)}
/>
<label htmlFor="status-2" className="text-sm">
Diterima
</label>
</div>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>

View File

@ -1,9 +1,239 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { LucideBoxSelect } from "lucide-react";
export default function ExecutiveDashboard() {
// const downloadReport = async () => {
// // const formattedDate = `${reportDate.year}-${String(
// // reportDate.month
// // ).padStart(2, "0")}-${String(reportDate.day).padStart(2, "0")}`;
// // const resLogin = await tableauSignin();
// // const token = resLogin?.data.data?.credentials?.token;
// // const resCover = await tableauViewImage(
// // token,
// // "1df3df4a-0457-4483-a8e1-160f70e7834f",
// // formattedDate
// // );
// // const resTotalLink = await tableauViewImage(
// // token,
// // "8f902032-a6eb-4083-817a-57350f509b75",
// // formattedDate
// // );
// // const resCount = await tableauViewImage(
// // token,
// // "11b2fe3c-f853-4156-800e-43342bf8e5ce",
// // formattedDate
// // );
// // const resCoverCommentTwitter = await tableauViewImage(
// // token,
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
// // formattedDate,
// // 2
// // );
// // const resCoverCommentTiktok = await tableauViewImage(
// // token,
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
// // formattedDate,
// // 3
// // );
// // const resCoverCommentFacebook = await tableauViewImage(
// // token,
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
// // formattedDate,
// // 4
// // );
// // const resCoverCommentInstagram = await tableauViewImage(
// // token,
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
// // formattedDate,
// // 5
// // );
// // const resCoverCommentYoutube = await tableauViewImage(
// // token,
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
// // formattedDate,
// // 6
// // );
// // const resCommentTwitter = await tableauViewImage(
// // token,
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
// // formattedDate,
// // 2
// // );
// // const resCommentTiktok = await tableauViewImage(
// // token,
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
// // formattedDate,
// // 3
// // );
// // const resCommentFacebook = await tableauViewImage(
// // token,
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
// // formattedDate,
// // 4
// // );
// // const resCommentInstagram = await tableauViewImage(
// // token,
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
// // formattedDate,
// // 5
// // );
// // const resCommentYoutube = await tableauViewImage(
// // token,
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
// // formattedDate,
// // 6
// // );
// // const blobCover = new Blob([resCover.data], { type: "image/png" });
// // const blobTotalLink = new Blob([resTotalLink.data], { type: "image/png" });
// // const blobCount = new Blob([resCount.data], { type: "image/png" });
// // const blobCoverCommentTwitter = new Blob([resCoverCommentTwitter.data], {
// // type: "image/png",
// // });
// // const blobCoverCommentTiktok = new Blob([resCoverCommentTiktok.data], {
// // type: "image/png",
// // });
// // const blobCoverCommentFacebook = new Blob([resCoverCommentFacebook.data], {
// // type: "image/png",
// // });
// // const blobCoverCommentInstagram = new Blob(
// // [resCoverCommentInstagram.data],
// // { type: "image/png" }
// // );
// // const blobCoverCommentYoutube = new Blob([resCoverCommentYoutube.data], {
// // type: "image/png",
// // });
// // const blobCommentTwitter = new Blob([resCommentTwitter.data], {
// // type: "image/png",
// // });
// // const blobCommentTiktok = new Blob([resCommentTiktok.data], {
// // type: "image/png",
// // });
// // const blobCommentFacebook = new Blob([resCommentFacebook.data], {
// // type: "image/png",
// // });
// // const blobCommentInstagram = new Blob([resCommentInstagram.data], {
// // type: "image/png",
// // });
// // const blobCommentYoutube = new Blob([resCommentYoutube.data], {
// // type: "image/png",
// // });
// // await pdfGenerator([
// // blobCover,
// // blobTotalLink,
// // blobCount,
// // blobCoverCommentTwitter,
// // blobCommentTwitter,
// // blobCoverCommentTiktok,
// // blobCommentTiktok,
// // blobCoverCommentFacebook,
// // blobCommentFacebook,
// // blobCoverCommentInstagram,
// // blobCommentInstagram,
// // blobCoverCommentYoutube,
// // blobCommentYoutube,
// // ]);
// };
return (
<div>
<SiteBreadcrumb />
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Download Report</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Download Report</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="w-full">
<Label>Date</Label>
<Input
type="date"
// value={dateFilter}
// onChange={(e) => setDateFilter(e.target.value)}
className="w-full"
/>
</div>
</div>
<DialogFooter>
<Button
type="submit"
// onClick={downloadReport}
>
Download
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<div className="mt-3 flex flex-row gap-3 justify-center">
<Card className="rounded-sm w-4/12 h-[500px] p-3">
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Upload konten hari ini Polda
</p>
<LucideBoxSelect />
</div>
</Card>
<Card className="rounded-sm w-4/12 p-3">
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Upload konten hari ini Satker
</p>
<LucideBoxSelect />
</div>
</Card>
<Card className="rounded-sm w-4/12 p-3">
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Upload konten hari ini Polres
</p>
<LucideBoxSelect />
</div>
</Card>
</div>
<div className="w-full mt-3">
<Card className="rounded-sm p-3 h-[300px]">
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">Konten Paling Populer</p>
<LucideBoxSelect />
</div>
</Card>
</div>
<div className="w-full mt-3">
<Card className="rounded-sm p-3 h-[300px]">
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Heatmap Konten Dengan Interaksi
</p>
<p className="text-base font-semibold">
Heatmap Kategori Dengan Interaksi
</p>
<LucideBoxSelect />
</div>
</Card>
</div>
<div className="w-full mt-3">
<Card className="rounded-sm p-3 h-[300px]">
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">Emergency Issue</p>
<LucideBoxSelect />
</div>
</Card>
</div>
</div>
);
}

View File

@ -46,7 +46,6 @@ import { listContest } from "@/service/contest/contest";
const TaskTable = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);

View File

@ -1,11 +1,20 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import ContestTable from "./components/contest-table";
import { Link } from "@/components/navigation";
import { Button } from "@/components/ui/button";
import { UploadIcon } from "lucide-react";
import { getCookiesDecrypt } from "@/lib/utils";
import { useEffect, useState } from "react";
const ContestPage = () => {
const [userLevelId, setUserLevelId] = useState<any>(null);
useEffect(() => {
setUserLevelId(Number(getCookiesDecrypt("ulie")));
}, []);
return (
<div>
<SiteBreadcrumb />
@ -17,14 +26,16 @@ const ContestPage = () => {
<div className="flex-1 text-xl font-medium text-default-900">
Tabel Lomba
</div>
<div className="flex-none">
<Link href={"/shared/contest/create"}>
<Button color="primary" className="text-white">
<UploadIcon />
Buat Lomba
</Button>
</Link>
</div>
{userLevelId !== 776 && userLevelId !== null && (
<div className="flex-none">
<Link href={"/shared/contest/create"}>
<Button color="primary" className="text-white">
<UploadIcon />
Buat Lomba
</Button>
</Link>
</div>
)}
</div>
</CardTitle>
</CardHeader>

View File

@ -27,6 +27,7 @@ import {
createMedia,
getTagsBySubCategoryId,
listEnableCategory,
publishMedia,
rejectFiles,
submitApproval,
} from "@/service/content/content";
@ -50,7 +51,7 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Textarea } from "@/components/ui/textarea";
import { close, loading } from "@/config/swal";
import { close, loading, successCallback } from "@/config/swal";
import { getCookiesDecrypt } from "@/lib/utils";
import { Icon } from "@iconify/react/dist/iconify.js";
import { error } from "@/lib/swal";
@ -406,6 +407,11 @@ export default function FormImageDetail() {
});
};
const publishToMabes = async () => {
const res = await publishMedia(id);
successCallback();
};
return (
<form>
{detail !== undefined ? (
@ -521,7 +527,7 @@ export default function FormImageDetail() {
</div>
</Card>
<div className="w-full lg:w-4/12">
<Card className=" h-[900px]">
<Card className=" h-[1050px]">
<div className="px-3 py-3">
<div className="space-y-2">
<Label>Kreator</Label>
@ -624,6 +630,38 @@ export default function FormImageDetail() {
) : (
""
)} */}
{detail?.isPublish == false && detail.isPublishOnPolda == true ? (
<div className="flex flex-col gap-2 p-3">
<Button
onClick={() => publishToMabes()}
type="button"
color="primary"
>
<Icon icon="fa:check" className="mr-3" /> Publish
</Button>
</div>
) : (
""
)}
{/* {detail?.isPublish == true ? (
<div className="row mt-4 mb-4">
<div className="col-12 col-lg-2 mt-2"></div>
<div className="col-12 col-lg-8 mt-2">
<a
className="btn btn-primary btn-block mb-2"
onClick={() => scheduledPublishToMabes()}
aria-hidden="true"
>
<Icon icon="fa:check" className="mr-3" /> Publish
Terjadwal
</a>
</div>
</div>
) : (
""
)} */}
{Number(detail?.needApprovalFromLevel) == Number(userLevelId) ? (
Number(detail?.uploadedById) == Number(userId) ? (
""

View File

@ -196,6 +196,11 @@ export async function rejectFiles(data: any) {
return httpPostInterceptor(url, data);
}
export async function publishMedia(id: any) {
const url = `media/public/publish-to-mabes?id=${id}`;
return httpPostInterceptor(url);
}
export async function saveContentRewrite(data: any) {
const url = "media/rewrite";
return httpPostInterceptor(url, data);