Compare commits
46 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
9223122e66 | |
|
|
9e286ffd06 | |
|
|
18d6f32536 | |
|
|
9c2b3612c6 | |
|
|
6bd83237c4 | |
|
|
61df236e13 | |
|
|
7f9166b866 | |
|
|
2876e4eb30 | |
|
|
292cde6ed0 | |
|
|
3c5e644668 | |
|
|
d80adaa133 | |
|
|
eefa43a5b0 | |
|
|
277f1cc805 | |
|
|
f357a4e8ce | |
|
|
bb91379058 | |
|
|
740d73d689 | |
|
|
76c4ed9238 | |
|
|
44c6cc6d9d | |
|
|
2b8f7b724e | |
|
|
10694547d1 | |
|
|
e5e32496ab | |
|
|
78d1fc0d93 | |
|
|
c99e1a5c7d | |
|
|
274fd022e1 | |
|
|
933a672280 | |
|
|
ce7c343808 | |
|
|
cba94cf0a9 | |
|
|
7c721dcc96 | |
|
|
78ddd461df | |
|
|
ec2d7eb203 | |
|
|
ad5d048e78 | |
|
|
5614b5ada5 | |
|
|
f1b59ee537 | |
|
|
0ad1acee09 | |
|
|
2d09af2e8b | |
|
|
edafc223db | |
|
|
933cdb1100 | |
|
|
280ea508e9 | |
|
|
1a0d53bb11 | |
|
|
be1c799629 | |
|
|
3d23d75e05 | |
|
|
1ee379df71 | |
|
|
9a1b41e90b | |
|
|
fcfa24b06c | |
|
|
cf91ea33ec | |
|
|
1cc7786dee |
|
|
@ -0,0 +1,45 @@
|
|||
kind: pipeline
|
||||
type: ssh
|
||||
name: mediahub-fe-build-deploy
|
||||
|
||||
server:
|
||||
host:
|
||||
from_secret: ssh_host
|
||||
user:
|
||||
from_secret: ssh_user
|
||||
ssh_key:
|
||||
from_secret: ssh_key
|
||||
|
||||
steps:
|
||||
- name: prepare repo
|
||||
when:
|
||||
branch:
|
||||
- dev-sabda-v2
|
||||
- main
|
||||
- prod
|
||||
commands:
|
||||
- rm -rf /opt/build/mediahub-fe
|
||||
- mkdir -p /opt/build
|
||||
- cd /opt/build
|
||||
- git clone http://38.47.180.165:3000/mediahub/mediahub-fe.git
|
||||
|
||||
- name: build image
|
||||
when:
|
||||
branch:
|
||||
- dev-sabda-v2
|
||||
- prod
|
||||
commands:
|
||||
- docker login 38.47.180.165:3000 -u administrator -p HarborDockerImageRep0
|
||||
- cd /opt/build/mediahub-fe
|
||||
- docker build -t 38.47.180.165:3000/mediahub/mediahub-fe:$DRONE_BRANCH .
|
||||
- docker push 38.47.180.165:3000/mediahub/mediahub-fe:$DRONE_BRANCH
|
||||
|
||||
- name: deploy
|
||||
when:
|
||||
branch:
|
||||
- prod
|
||||
commands:
|
||||
- docker pull 38.47.180.165:3000/mediahub/mediahub-fe:$DRONE_BRANCH
|
||||
- docker stop new-mediahub-fe || true
|
||||
- docker rm new-mediahub-fe || true
|
||||
- docker run -dt -p 4200:3000 --restart always --name new-mediahub-fe 38.47.180.165:3000/mediahub/mediahub-fe:$DRONE_BRANCH
|
||||
4
.env
4
.env
|
|
@ -1,3 +1,3 @@
|
|||
NEXT_PUBLIC_API=https://netidhub.com/api
|
||||
NEXT_PUBLIC=https://netidhub.com
|
||||
NEXT_PUBLIC_API=https://new.netidhub.com/api
|
||||
NEXT_PUBLIC=https://new.netidhub.com
|
||||
NEXT_PUBLIC_TINYMCE_API_KEY=bhteuja26yz5p0aubxry9b95hs33amgn65kjv5km0fd5iuev
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
stages:
|
||||
- build
|
||||
- deploy
|
||||
|
||||
|
||||
build-dev:
|
||||
stage: build
|
||||
when: on_success
|
||||
only:
|
||||
- main
|
||||
- dev-landing-v2
|
||||
image:
|
||||
image:
|
||||
name: docker:25.0.3-cli
|
||||
services:
|
||||
- name: docker:25.0.3-dind
|
||||
command: ["--insecure-registry=103.82.242.92:8900"]
|
||||
command: ["--insecure-registry=38.47.185.86:8900"]
|
||||
script:
|
||||
- docker logout
|
||||
- docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 103.82.242.92:8900
|
||||
- docker build -t 103.82.242.92:8900/mediahub/new-mediahub-fe:dev .
|
||||
- docker push 103.82.242.92:8900/mediahub/new-mediahub-fe:dev
|
||||
- docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 38.47.185.86:8900
|
||||
- docker build -t 38.47.185.86:8900/mediahub/new-mediahub-fe:dev .
|
||||
- docker push 38.47.185.86:8900/mediahub/new-mediahub-fe:dev
|
||||
|
||||
auto-deploy:
|
||||
stage: deploy
|
||||
|
|
@ -26,7 +26,5 @@ auto-deploy:
|
|||
- main
|
||||
- dev-landing-v2
|
||||
image: curlimages/curl:latest
|
||||
services:
|
||||
- docker:dind
|
||||
script:
|
||||
- curl --user admin:$JENKINS_PWD http://103.31.38.120:8080/job/auto-deploy-new-mediahub-fe/build?token=autodeploynewmediahub
|
||||
- curl --user admin:$JENKINS_PWD http://38.47.185.86:8080/job/auto-deploy-new-mediahub-fe/build?token=autodeploynewmediahub
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ import {
|
|||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
|
||||
import { validateMediaLink } from "@/service/media-tracking/media-tracking";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
|
|
@ -52,12 +54,132 @@ const columns: ColumnDef<any>[] = [
|
|||
<span className="normal-case">{row.getValue("title")}</span>
|
||||
),
|
||||
},
|
||||
// {
|
||||
// accessorKey: "link",
|
||||
// header: "Link Berita",
|
||||
// cell: ({ row }) => (
|
||||
// <span className="normal-case">{row.getValue("link")}</span>
|
||||
// ),
|
||||
// },
|
||||
{
|
||||
accessorKey: "link",
|
||||
header: "Link Berita",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("link")}</span>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const link = row.getValue<string>("link");
|
||||
|
||||
if (!link) {
|
||||
return <span className="text-muted-foreground">-</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 underline hover:text-blue-800 break-all"
|
||||
>
|
||||
{link}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "validation",
|
||||
header: "Validasi",
|
||||
cell: ({ row, table }) => {
|
||||
const original = row.original;
|
||||
|
||||
// const isValid = original.isValid;
|
||||
const isRelevant = original.isRelevant;
|
||||
const link = original.link;
|
||||
|
||||
const updateRow = (data: Partial<any>) => {
|
||||
table.options.meta?.updateData(row.index, data);
|
||||
};
|
||||
|
||||
const handleValid = async () => {
|
||||
try {
|
||||
await validateMediaLink(original.id, true);
|
||||
updateRow({
|
||||
isRelevant: true,
|
||||
});
|
||||
table.options.meta?.refetchData?.();
|
||||
} catch (err: any) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInvalid = async () => {
|
||||
try {
|
||||
await validateMediaLink(original.id, false);
|
||||
|
||||
updateRow({
|
||||
isRelevant: false,
|
||||
});
|
||||
table.options.meta?.refetchData?.();
|
||||
} catch (err: any) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
if (!link) {
|
||||
return <span className="text-muted-foreground">-</span>;
|
||||
}
|
||||
|
||||
if (isRelevant === true) {
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
className="bg-green-600 hover:bg-green-700"
|
||||
disabled
|
||||
>
|
||||
Relevan
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={handleValid}
|
||||
className="flex items-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M18.7 7.2c-.4-.4-1-.4-1.4 0l-7.5 7.5l-3.1-3.1c-.4-.4-1-.4-1.4 0s-.4 1 0 1.4l3.8 3.8c.2.2.4.3.7.3s.5-.1.7-.3l8.2-8.2c.4-.4.4-1 0-1.4"
|
||||
/>
|
||||
</svg>
|
||||
Relevan
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={handleInvalid} className="flex text-center items-center justify-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M6.758 17.243L12.001 12m5.243-5.243L12 12m0 0L6.758 6.757M12.001 12l5.243 5.243"
|
||||
/>
|
||||
</svg>
|
||||
Tidak Relevan
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,18 @@ const NewsDetailTable = () => {
|
|||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onPaginationChange: setPagination,
|
||||
meta: {
|
||||
updateData: (rowIndex: number, value: Partial<any>) => {
|
||||
setDataTable((old) =>
|
||||
old.map((row, index) =>
|
||||
index === rowIndex ? { ...row, ...value } : row
|
||||
)
|
||||
);
|
||||
},
|
||||
refetchData: () => {
|
||||
fetchData();
|
||||
},
|
||||
},
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
|
|
@ -154,7 +166,7 @@ const NewsDetailTable = () => {
|
|||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
}, [page, showData]);
|
||||
}, [page, showData, id]);
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
import * as React from "react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { exportMediaTrackingToExcel } from "@/utils/export-media-tracking";
|
||||
import { loading, close } from "@/config/swal";
|
||||
import { error } from "@/lib/swal";
|
||||
import {
|
||||
DownloadIcon,
|
||||
Eye,
|
||||
MoreVertical,
|
||||
SquarePen,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
|
@ -46,7 +54,7 @@ const columns: ColumnDef<any>[] = [
|
|||
},
|
||||
{
|
||||
accessorKey: "resultTotal",
|
||||
header: () => <div className="text-center w-full">Jumlah Amplifikasi</div>,
|
||||
header: () => <div className="text-center w-full">Total Artikel</div>,
|
||||
cell: ({ row }) => {
|
||||
const value = row.getValue("resultTotal") as number | string | null;
|
||||
|
||||
|
|
@ -58,6 +66,29 @@ const columns: ColumnDef<any>[] = [
|
|||
return <div className="text-center w-full">{finalValue}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "amplification",
|
||||
header: () => <div className="text-center w-full">Jumlah Amplifikasi</div>,
|
||||
cell: ({ row }) => {
|
||||
const raw = row.getValue("amplification") as string | null;
|
||||
|
||||
let total = 0;
|
||||
let invalidTotal = 0;
|
||||
|
||||
if (raw && typeof raw === "string") {
|
||||
const parts = raw.split("/").map((v) => v.trim());
|
||||
total = Number(parts[0]) || 0;
|
||||
invalidTotal = Number(parts[1]) || 0;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="text-center w-full font-medium">
|
||||
{total}
|
||||
<span className="text-muted-foreground">/{invalidTotal}</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
// {
|
||||
// accessorKey: "status",
|
||||
|
|
@ -81,11 +112,7 @@ const columns: ColumnDef<any>[] = [
|
|||
header: () => <div className="text-center">Status</div>,
|
||||
cell: ({ row }) => {
|
||||
const raw = Boolean(row.getValue("isProcessing"));
|
||||
|
||||
// KONDISI STATUS
|
||||
const statusText = raw ? "Sedang Diproses" : "Sudah Selesai";
|
||||
|
||||
// WARNA STATUS
|
||||
const colorClass = raw
|
||||
? "bg-yellow-100 text-yellow-700 border border-yellow-300"
|
||||
: "bg-green-100 text-green-700 border border-green-300";
|
||||
|
|
@ -141,13 +168,31 @@ const columns: ColumnDef<any>[] = [
|
|||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<Link href={`/admin/media-tracking/detail/${row.original.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground items-center rounded-none">
|
||||
<DropdownMenuItem className="p-2 border-b cursor-pointer text-default-700 group focus:bg-default focus:text-primary-foreground items-center rounded-none">
|
||||
<Eye className="w-4 h-4 me-1.5" />
|
||||
View
|
||||
{row.original.mediaUpload.fileType.secondaryName &&
|
||||
row.original.mediaUpload.fileType.secondaryName.toLowerCase()}
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
<DropdownMenuItem
|
||||
className="p-2 border-b cursor-pointer text-default-700 group rounded-none focus:bg-default focus:text-primary-foreground "
|
||||
onClick={async () => {
|
||||
try {
|
||||
loading();
|
||||
await exportMediaTrackingToExcel({
|
||||
mediaTrackingId: row.original.id,
|
||||
});
|
||||
close();
|
||||
} catch (e: any) {
|
||||
close();
|
||||
error(e.message || "Gagal export data");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DownloadIcon className="w-4 h-4 me-1.5" />
|
||||
<p>Download</p>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -894,7 +894,6 @@ const EventModal = ({
|
|||
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
console.log("CSRF TOKEN : ", csrfToken);
|
||||
const headers = {
|
||||
"X-XSRF-TOKEN": csrfToken,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ const useTableColumns = ({
|
|||
// try {
|
||||
// loading();
|
||||
// const response = await axios.get(
|
||||
// `https://netidhub.com/api/media/report/download?id=${id}`,
|
||||
// `https://new.netidhub.com/api/media/report/download?id=${id}`,
|
||||
// {
|
||||
// responseType: "blob",
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ import withReactContent from "sweetalert2-react-content";
|
|||
import Swal from "sweetalert2";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
const useTableColumns = (activeTab: "ta" | "daily" | "special") => {
|
||||
const useTableColumns = (
|
||||
activeTab: "ta" | "daily" | "special" | "mabes-koor",
|
||||
) => {
|
||||
const t = useTranslations("Table");
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
|
|
@ -190,7 +192,11 @@ const useTableColumns = (activeTab: "ta" | "daily" | "special") => {
|
|||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
{(roleId == 11 || roleId == 12 || roleId == 19) && (
|
||||
{/* {(roleId == 11 || roleId == 12 || roleId == 19) && ( */}
|
||||
{(roleId == 11 ||
|
||||
roleId == 12 ||
|
||||
roleId == 19 ||
|
||||
roleId == 3) && (
|
||||
<Link href={`/contributor/task-ta/detail/${row.original.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Eye className="w-4 h-4 me-1.5" />
|
||||
|
|
@ -198,14 +204,15 @@ const useTableColumns = (activeTab: "ta" | "daily" | "special") => {
|
|||
</DropdownMenuItem>
|
||||
</Link>
|
||||
)}
|
||||
{roleId == 11 && (
|
||||
<Link href={`/contributor/task-ta/update/${row.original.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<SquarePen className="w-4 h-4 me-1.5" />
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
)}
|
||||
{roleId == 11 ||
|
||||
(roleId == 3 && (
|
||||
<Link href={`/contributor/task-ta/update/${row.original.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<SquarePen className="w-4 h-4 me-1.5" />
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
))}
|
||||
{(roleId == 12 || roleId == 19) && (
|
||||
<Link
|
||||
href={`/contributor/task-ta/upload-task/${row.original.id}`}
|
||||
|
|
@ -216,15 +223,16 @@ const useTableColumns = (activeTab: "ta" | "daily" | "special") => {
|
|||
</DropdownMenuItem>
|
||||
</Link>
|
||||
)}
|
||||
{roleId == 11 && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => TaskDelete(row.original.id)}
|
||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 me-1.5" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{roleId == 11 ||
|
||||
(roleId == 3 && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => TaskDelete(row.original.id)}
|
||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 me-1.5" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,7 +5,11 @@ import { Button } from "@/components/ui/button";
|
|||
import { UploadIcon } from "lucide-react";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { Link } from "@/components/navigation";
|
||||
import { checkAuthorization, checkLoginSession } from "@/lib/utils";
|
||||
import {
|
||||
checkAuthorization,
|
||||
checkLoginSession,
|
||||
getCookiesDecrypt,
|
||||
} from "@/lib/utils";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
|
|
@ -13,12 +17,13 @@ const TaskPage = () => {
|
|||
const t = useTranslations("AnalyticsDashboard");
|
||||
useEffect(() => {
|
||||
function initState() {
|
||||
checkAuthorization("admin");
|
||||
checkAuthorization("admin");
|
||||
checkLoginSession();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
const levelNumber = getCookiesDecrypt("ulne");
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -29,16 +34,28 @@ const TaskPage = () => {
|
|||
<CardTitle>
|
||||
<div className="flex flex-col sm:flex-row lg:flex-row lg:items-center">
|
||||
<div className="flex-1 text-xl font-medium text-default-900">
|
||||
{t("tabel", { defaultValue: "Tabel" })} {t("task", { defaultValue: "Task" })}
|
||||
{t("tabel", { defaultValue: "Tabel" })}{" "}
|
||||
{t("task", { defaultValue: "Task" })}
|
||||
</div>
|
||||
<div className="flex-none">
|
||||
{Number(levelNumber) !== 3 && (
|
||||
<div className="flex-none">
|
||||
<Link href="/contributor/task/create">
|
||||
<Button color="primary" className="text-white">
|
||||
<UploadIcon size={18} className="mr-2" />
|
||||
{t("create-task", { defaultValue: "Create Task" })}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* <div className="flex-none">
|
||||
<Link href={"/contributor/task/create"}>
|
||||
<Button color="primary" className="text-white">
|
||||
<UploadIcon size={18} className="mr-2" />
|
||||
{t("create-task", { defaultValue: "Create Task" })}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import ReportTable from "../contributor/report/components/report-table";
|
|||
const DashboardPage = () => {
|
||||
const t = useTranslations("AnalyticsDashboard");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
||||
|
||||
return Number(roleId) == 2 || Number(roleId) == 11 || Number(roleId) == 12 ? (
|
||||
<div>
|
||||
|
|
@ -54,18 +55,23 @@ const DashboardPage = () => {
|
|||
>
|
||||
{t("schedule", { defaultValue: "Schedule" })}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="indeks"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
||||
>
|
||||
{t("indeks", { defaultValue: "Indeks" })}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="report"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
||||
>
|
||||
{t("report", { defaultValue: "Report" })}
|
||||
</TabsTrigger>
|
||||
{levelNumber !== 3 && (
|
||||
<>
|
||||
<TabsTrigger
|
||||
value="indeks"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
||||
>
|
||||
{t("indeks", { defaultValue: "Indeks" })}
|
||||
</TabsTrigger>
|
||||
|
||||
<TabsTrigger
|
||||
value="report"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
||||
>
|
||||
{t("report", { defaultValue: "Report" })}
|
||||
</TabsTrigger>
|
||||
</>
|
||||
)}
|
||||
</TabsList>
|
||||
</Card>
|
||||
<TabsContent value="routine-task">
|
||||
|
|
@ -75,18 +81,24 @@ const DashboardPage = () => {
|
|||
<CardContent className="p-4">
|
||||
<div className="grid md:grid-cols-3 gap-4">
|
||||
<StatisticsBlock
|
||||
title={t("Hasil_unggah_disetujui_hari_ini", { defaultValue: "Hasil Unggah Disetujui Hari Ini" })}
|
||||
title={t("Hasil_unggah_disetujui_hari_ini", {
|
||||
defaultValue: "Hasil Unggah Disetujui Hari Ini",
|
||||
})}
|
||||
total="3,564"
|
||||
className="bg-info/10 border-none shadow-none"
|
||||
/>
|
||||
<StatisticsBlock
|
||||
title={t("Hasil_unggah_direvisi_hari_ini", { defaultValue: "Hasil Unggah Direvisi Hari Ini" })}
|
||||
title={t("Hasil_unggah_direvisi_hari_ini", {
|
||||
defaultValue: "Hasil Unggah Direvisi Hari Ini",
|
||||
})}
|
||||
total="564"
|
||||
className="bg-warning/10 border-none shadow-none"
|
||||
chartColor="#FB8F65"
|
||||
/>
|
||||
<StatisticsBlock
|
||||
title={t("Hasil_unggah_ditolak_hari_ini", { defaultValue: "Hasil Unggah Ditolak Hari Ini" })}
|
||||
title={t("Hasil_unggah_ditolak_hari_ini", {
|
||||
defaultValue: "Hasil Unggah Ditolak Hari Ini",
|
||||
})}
|
||||
total="+5.0%"
|
||||
className="bg-primary/10 border-none shadow-none"
|
||||
chartColor="#2563eb"
|
||||
|
|
@ -101,7 +113,9 @@ const DashboardPage = () => {
|
|||
<Card>
|
||||
<CardHeader className="flex flex-row items-center">
|
||||
<CardTitle className="flex-1 text-lg">
|
||||
{t("Total-Content-Production", { defaultValue: "Total Content Production" })}
|
||||
{t("Total-Content-Production", {
|
||||
defaultValue: "Total Content Production",
|
||||
})}
|
||||
</CardTitle>
|
||||
<DashboardDropdown />
|
||||
</CardHeader>
|
||||
|
|
@ -113,7 +127,9 @@ const DashboardPage = () => {
|
|||
<div className="lg:col-span-8 col-span-12">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center">
|
||||
<CardTitle className="flex-1">{t("tabel", { defaultValue: "Tabel" })}</CardTitle>
|
||||
<CardTitle className="flex-1">
|
||||
{t("tabel", { defaultValue: "Tabel" })}
|
||||
</CardTitle>
|
||||
{/* <DashboardDropdown /> */}
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
|
|
@ -132,14 +148,26 @@ const DashboardPage = () => {
|
|||
<div className="flex-1 text-xl font-medium text-default-900">
|
||||
Tabel Penugasan
|
||||
</div>
|
||||
<div>
|
||||
{Number(levelNumber) !== 3 && (
|
||||
<div className="flex-none">
|
||||
<Link href="/contributor/task/create">
|
||||
<Button color="primary" className="text-white">
|
||||
<UploadIcon size={18} className="mr-2" />
|
||||
{t("create-task", {
|
||||
defaultValue: "Create Task",
|
||||
})}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{/* <div>
|
||||
<Link href={"/contributor/task/create"}>
|
||||
<Button color="primary" className="text-white">
|
||||
<UploadIcon />
|
||||
Buat Penugasan
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</Card>
|
||||
<CardContent className="p-0 mt-3">
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import { useTranslations } from "next-intl";
|
|||
const ContestPage = () => {
|
||||
const [userLevelId, setUserLevelId] = useState<any>(null);
|
||||
const t = useTranslations("Contest");
|
||||
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
||||
|
||||
useEffect(() => {
|
||||
setUserLevelId(Number(getCookiesDecrypt("ulie")));
|
||||
}, []);
|
||||
|
|
@ -25,18 +27,36 @@ const ContestPage = () => {
|
|||
<CardTitle>
|
||||
<div className="flex items-center">
|
||||
<div className="flex-1 text-xl font-medium text-default-900">
|
||||
{t("table", { defaultValue: "Table" })} {t("contest", { defaultValue: "Contest" })}
|
||||
{t("table", { defaultValue: "Table" })}{" "}
|
||||
{t("contest", { defaultValue: "Contest" })}
|
||||
</div>
|
||||
{userLevelId !== 776 && userLevelId !== null && (
|
||||
{userLevelId !== 776 &&
|
||||
userLevelId !== null &&
|
||||
levelNumber !== 3 && (
|
||||
<div className="flex-none">
|
||||
<Link href={"/shared/contest/create"}>
|
||||
<Button color="primary" className="text-white">
|
||||
<UploadIcon size={18} className="mr-2" />
|
||||
{t("create-contest", {
|
||||
defaultValue: "Create Contest",
|
||||
})}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* {userLevelId !== 776 && userLevelId !== null && (
|
||||
<div className="flex-none">
|
||||
<Link href={"/shared/contest/create"}>
|
||||
<Button color="primary" className="text-white">
|
||||
<UploadIcon size={18} className="mr-2" />
|
||||
{t("create-contest", { defaultValue: "Create Contest" })}
|
||||
{t("create-contest", {
|
||||
defaultValue: "Create Contest",
|
||||
})}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ const LatestNews = (props: { type: string }) => {
|
|||
|
||||
// useEffect(() => {
|
||||
// async function fetchCategories() {
|
||||
// const url = "https://netidhub.com/api/csrf";
|
||||
// const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
// try {
|
||||
// const response = await fetch(url);
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ const NationalNews = () => {
|
|||
|
||||
// useEffect(() => {
|
||||
// async function fetchCategories() {
|
||||
// const url = "https://netidhub.com/api/csrf";
|
||||
// const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
// try {
|
||||
// const response = await fetch(url);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const PopularNews = () => {
|
|||
|
||||
useEffect(() => {
|
||||
async function fetchCategories() {
|
||||
const url = "https://netidhub.com/api/csrf";
|
||||
const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ const RegionalNews = () => {
|
|||
|
||||
// useEffect(() => {
|
||||
// async function fetchCategories() {
|
||||
// const url = "https://netidhub.com/api/csrf";
|
||||
// const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
// try {
|
||||
// const response = await fetch(url);
|
||||
|
|
|
|||
|
|
@ -311,14 +311,15 @@ export default function FormQuestionsForward() {
|
|||
{` `}
|
||||
mengirimkan pesan untuk{` `}
|
||||
<Link
|
||||
href={
|
||||
detail?.feed
|
||||
? detail?.feed?.permalink_url == undefined
|
||||
? detail?.feedUrl
|
||||
: detail?.feed?.permalink_url
|
||||
: ""
|
||||
}
|
||||
target="_blank"
|
||||
// href={
|
||||
// detail?.feed
|
||||
// ? detail?.feed?.permalink_url == undefined
|
||||
// ? detail?.feedUrl
|
||||
// : detail?.feed?.permalink_url
|
||||
// : ""
|
||||
// }
|
||||
// target="_blank"
|
||||
href={detail?.feedUrl}
|
||||
className="font-bold"
|
||||
>
|
||||
{detail?.message}
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ const ViewEditor = dynamic(
|
|||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
interface Destination {
|
||||
|
|
@ -198,6 +198,12 @@ export default function FormAudioDetail() {
|
|||
satker: boolean;
|
||||
}>
|
||||
>([]);
|
||||
const [creatorLevelNumber, setCreatorLevelNumber] = useState<number | null>(
|
||||
null,
|
||||
);
|
||||
const isContentFromSatker = creatorLevelNumber === 3;
|
||||
const isContentFromMabesOrPolda =
|
||||
creatorLevelNumber === 1 || creatorLevelNumber === 2;
|
||||
|
||||
useEffect(() => {
|
||||
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
|
||||
|
|
@ -209,7 +215,7 @@ export default function FormAudioDetail() {
|
|||
const handleFileUnitChange = (
|
||||
fileIndex: number,
|
||||
key: keyof typeof unitSelection,
|
||||
value: boolean
|
||||
value: boolean,
|
||||
) => {
|
||||
setFileUnitSelections((prev) => {
|
||||
const newSelections = [...prev];
|
||||
|
|
@ -229,7 +235,7 @@ export default function FormAudioDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[fileIndex] || new Set()
|
||||
newArray[fileIndex] || new Set(),
|
||||
);
|
||||
|
||||
if (value) {
|
||||
|
|
@ -260,12 +266,12 @@ export default function FormAudioDetail() {
|
|||
(item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
currentFileCheckedLevels.has(Number(item.id))
|
||||
currentFileCheckedLevels.has(Number(item.id)),
|
||||
);
|
||||
|
||||
if (!hasSelectedPolda) {
|
||||
alert(
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
|
||||
);
|
||||
return prev; // Batalkan perubahan
|
||||
}
|
||||
|
|
@ -302,7 +308,7 @@ export default function FormAudioDetail() {
|
|||
useEffect(() => {
|
||||
if (detail?.assignedToTopLevel) {
|
||||
const outputSet = new Set(
|
||||
detail.assignedToTopLevel.split(",").map(Number)
|
||||
detail.assignedToTopLevel.split(",").map(Number),
|
||||
);
|
||||
setUnitSelection({
|
||||
semua: outputSet.has(0),
|
||||
|
|
@ -327,7 +333,7 @@ export default function FormAudioDetail() {
|
|||
acc[polda.id] = false;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
{},
|
||||
);
|
||||
setExpandedPolda(initialExpandedState);
|
||||
console.log("polres", initialExpandedState);
|
||||
|
|
@ -362,7 +368,7 @@ export default function FormAudioDetail() {
|
|||
|
||||
const handleUnitChange = (
|
||||
key: keyof typeof unitSelection,
|
||||
value: boolean
|
||||
value: boolean,
|
||||
) => {
|
||||
if (key === "semua") {
|
||||
const newState = {
|
||||
|
|
@ -472,7 +478,7 @@ export default function FormAudioDetail() {
|
|||
|
||||
const handleCheckboxChange = (id: number) => {
|
||||
setSelectedPublishers((prev) =>
|
||||
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
||||
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id],
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -494,7 +500,7 @@ export default function FormAudioDetail() {
|
|||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
o.name.toLowerCase().includes("pers rilis"),
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
|
|
@ -542,6 +548,9 @@ export default function FormAudioDetail() {
|
|||
const details = response?.data?.data;
|
||||
setFiles(details?.files);
|
||||
setDetail(details);
|
||||
if (details?.uploadedBy?.userLevel?.levelNumber) {
|
||||
setCreatorLevelNumber(details.uploadedBy.userLevel.levelNumber);
|
||||
}
|
||||
setMain({
|
||||
type: details?.fileType.name,
|
||||
url: details?.files[0]?.url,
|
||||
|
|
@ -552,14 +561,14 @@ export default function FormAudioDetail() {
|
|||
|
||||
if (details?.assignedToLevel) {
|
||||
const levels = new Set(
|
||||
details.assignedToLevel.split(",").map(Number)
|
||||
details.assignedToLevel.split(",").map(Number),
|
||||
);
|
||||
setCheckedLevels(levels);
|
||||
}
|
||||
|
||||
if (details?.publishedForObject) {
|
||||
const publisherIds = details?.publishedForObject.map(
|
||||
(obj: any) => obj.id
|
||||
(obj: any) => obj.id,
|
||||
);
|
||||
setSelectedPublishers(publisherIds);
|
||||
}
|
||||
|
|
@ -709,7 +718,7 @@ export default function FormAudioDetail() {
|
|||
const setupPlacement = (
|
||||
index: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
checked: boolean,
|
||||
) => {
|
||||
let temp = [...filePlacements];
|
||||
if (checked) {
|
||||
|
|
@ -720,7 +729,7 @@ export default function FormAudioDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[index] || new Set()
|
||||
newArray[index] || new Set(),
|
||||
);
|
||||
|
||||
// Checklist semua item di modal
|
||||
|
|
@ -770,7 +779,7 @@ export default function FormAudioDetail() {
|
|||
// Hanya auto-checklist "all" jika polda, polres, dan mabes ter-checklist
|
||||
// JANGAN include satker dalam perhitungan auto "all"
|
||||
const nonSatkerItems = now.filter(
|
||||
(item) => item !== "satker" && item !== "all"
|
||||
(item) => item !== "satker" && item !== "all",
|
||||
);
|
||||
if (nonSatkerItems.length === 3 && !now.includes("all")) {
|
||||
now.push("all");
|
||||
|
|
@ -785,7 +794,7 @@ export default function FormAudioDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[index] || new Set()
|
||||
newArray[index] || new Set(),
|
||||
);
|
||||
|
||||
// Unchecklist semua item di modal
|
||||
|
|
@ -819,7 +828,7 @@ export default function FormAudioDetail() {
|
|||
// Hapus "all" jika tidak semua item ter-checklist
|
||||
if (now.includes("all")) {
|
||||
const nonSatkerItems = now.filter(
|
||||
(item) => item !== "satker" && item !== "all"
|
||||
(item) => item !== "satker" && item !== "all",
|
||||
);
|
||||
if (nonSatkerItems.length < 3) {
|
||||
const newData = now.filter((b) => b !== "all");
|
||||
|
|
@ -838,7 +847,7 @@ export default function FormAudioDetail() {
|
|||
type: string,
|
||||
url: string,
|
||||
names: string,
|
||||
format: string
|
||||
format: string,
|
||||
) => {
|
||||
console.log("Test 3 :", type, url, names, format);
|
||||
setMain({
|
||||
|
|
@ -873,7 +882,7 @@ export default function FormAudioDetail() {
|
|||
const updateModalChecklistLevels = (
|
||||
fileIndex: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
checked: boolean,
|
||||
) => {
|
||||
if (!listDest || listDest.length === 0) return;
|
||||
|
||||
|
|
@ -906,7 +915,7 @@ export default function FormAudioDetail() {
|
|||
} else if (placement === "satker") {
|
||||
// Checklist SATKER POLRI dan semua sub-item di bawahnya
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
currentFileLevels.add(Number(satkerItem.id));
|
||||
|
|
@ -942,7 +951,7 @@ export default function FormAudioDetail() {
|
|||
} else if (placement === "satker") {
|
||||
// Unchecklist SATKER POLRI dan semua sub-item di bawahnya
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
currentFileLevels.delete(Number(satkerItem.id));
|
||||
|
|
@ -975,7 +984,7 @@ export default function FormAudioDetail() {
|
|||
// Fungsi untuk mengupdate checklist levels untuk file tertentu
|
||||
const handleFileCheckboxChangePlacement = (
|
||||
fileIndex: number,
|
||||
levelId: number
|
||||
levelId: number,
|
||||
) => {
|
||||
setFileCheckedLevels((prev) => {
|
||||
const newArray = [...prev];
|
||||
|
|
@ -987,7 +996,7 @@ export default function FormAudioDetail() {
|
|||
|
||||
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
|
||||
const poldaItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (
|
||||
poldaItem &&
|
||||
|
|
@ -1010,7 +1019,7 @@ export default function FormAudioDetail() {
|
|||
|
||||
// Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya
|
||||
const satkerItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (satkerItem && satkerItem.name === "SATKER POLRI") {
|
||||
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
|
||||
|
|
@ -1041,7 +1050,7 @@ export default function FormAudioDetail() {
|
|||
|
||||
// Hitung total POLDA yang ada (bukan SATKER POLRI)
|
||||
const totalPoldaCount = listDest.filter(
|
||||
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
|
||||
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI",
|
||||
).length;
|
||||
|
||||
// Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI)
|
||||
|
|
@ -1068,7 +1077,7 @@ export default function FormAudioDetail() {
|
|||
}
|
||||
return total;
|
||||
},
|
||||
0
|
||||
0,
|
||||
);
|
||||
|
||||
// Hitung berapa banyak POLRES yang ter-checklist
|
||||
|
|
@ -1078,7 +1087,7 @@ export default function FormAudioDetail() {
|
|||
return (
|
||||
total +
|
||||
item.subDestination.filter((sub: any) =>
|
||||
currentFileLevels.has(Number(sub.id))
|
||||
currentFileLevels.has(Number(sub.id)),
|
||||
).length
|
||||
);
|
||||
}
|
||||
|
|
@ -1087,7 +1096,7 @@ export default function FormAudioDetail() {
|
|||
|
||||
// Cek apakah SATKER POLRI ter-checklist
|
||||
const satkerItem = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
const isSatkerChecked =
|
||||
satkerItem && currentFileLevels.has(Number(satkerItem.id));
|
||||
|
|
@ -1121,7 +1130,7 @@ export default function FormAudioDetail() {
|
|||
|
||||
// Cek apakah semua sub-items sudah ter-checklist
|
||||
const allSubItemsChecked = polda.subDestination?.every((sub: any) =>
|
||||
currentFileLevels.has(Number(sub.id))
|
||||
currentFileLevels.has(Number(sub.id)),
|
||||
);
|
||||
|
||||
if (allSubItemsChecked) {
|
||||
|
|
@ -1204,7 +1213,7 @@ export default function FormAudioDetail() {
|
|||
{detail &&
|
||||
!categories.find(
|
||||
(cat) =>
|
||||
String(cat.id) === String(detail.category.id)
|
||||
String(cat.id) === String(detail.category.id),
|
||||
) && (
|
||||
<SelectItem
|
||||
key={String(detail.category.id)}
|
||||
|
|
@ -1464,7 +1473,7 @@ export default function FormAudioDetail() {
|
|||
Tingkat Distribusi:
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{/* {[
|
||||
{ key: "semua", label: "Semua" },
|
||||
{ key: "nasional", label: "Nasional" },
|
||||
{ key: "wilayah", label: "Wilayah" },
|
||||
|
|
@ -1472,6 +1481,39 @@ export default function FormAudioDetail() {
|
|||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
] */}
|
||||
{[
|
||||
{ key: "semua", label: "Semua" },
|
||||
|
||||
...(isContentFromMabesOrPolda
|
||||
? [
|
||||
{
|
||||
key: "nasional",
|
||||
label: "Nasional",
|
||||
},
|
||||
{
|
||||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
{
|
||||
key: "wilayah",
|
||||
label: "Wilayah",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
...(isContentFromSatker
|
||||
? [
|
||||
{
|
||||
key: "nasional",
|
||||
label: "Nasional",
|
||||
},
|
||||
{
|
||||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
|
|
@ -1488,12 +1530,12 @@ export default function FormAudioDetail() {
|
|||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean
|
||||
value as boolean,
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value)
|
||||
Boolean(value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -1509,251 +1551,262 @@ export default function FormAudioDetail() {
|
|||
</div>
|
||||
|
||||
{/* Detail Wilayah */}
|
||||
{fileUnitSelections[index]?.wilayah && (
|
||||
<div className="border-t border-gray-200 pt-2">
|
||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||
Detail Wilayah:
|
||||
</p>
|
||||
{/* {fileUnitSelections[index]?.wilayah && ( */}
|
||||
{!isContentFromSatker &&
|
||||
fileUnitSelections[index]?.wilayah && (
|
||||
<div className="border-t border-gray-200 pt-2">
|
||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||
Detail Wilayah:
|
||||
</p>
|
||||
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
<Checkbox
|
||||
id={`${item.key}-${index}`}
|
||||
checked={
|
||||
fileUnitSelections[index]?.[
|
||||
item.key as keyof typeof unitSelection
|
||||
] || false
|
||||
}
|
||||
onCheckedChange={(value) => {
|
||||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`${item.key}-${index}`}
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
{item.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
<Checkbox
|
||||
id={`${item.key}-${index}`}
|
||||
checked={
|
||||
fileUnitSelections[index]?.[
|
||||
item.key as keyof typeof unitSelection
|
||||
] || false
|
||||
}
|
||||
onCheckedChange={(value) => {
|
||||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean,
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`${item.key}-${index}`}
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
{t("custom", {
|
||||
defaultValue: "Kustom",
|
||||
})}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
Daftar Wilayah POLDA dan POLRES
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||
{listDest.map((polda: any) => (
|
||||
<div
|
||||
key={polda.id}
|
||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||
>
|
||||
{/* Header POLDA */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(polda.id)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(polda.id)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="font-semibold text-gray-900 text-sm">
|
||||
{polda.name}
|
||||
</span>
|
||||
</Label>
|
||||
{polda.subDestination && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleExpand(
|
||||
polda.id
|
||||
);
|
||||
}}
|
||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
expandedPolda[
|
||||
polda.id
|
||||
]
|
||||
? "mdi:chevron-up"
|
||||
: "mdi:chevron-down"
|
||||
}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{item.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Sub-items */}
|
||||
{polda.subDestination &&
|
||||
expandedPolda[polda.id] && (
|
||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||
{/* Tombol Pilih Semua untuk sub-items */}
|
||||
<div className="mb-2 flex justify-start">
|
||||
{(() => {
|
||||
const allSubItemsChecked =
|
||||
polda.subDestination?.every(
|
||||
(sub: any) =>
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
polda
|
||||
)
|
||||
}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-indeterminate-small"
|
||||
width={12}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Batal Semua
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-all"
|
||||
width={12}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Pilih Semua
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
{t("custom", {
|
||||
defaultValue: "Kustom",
|
||||
})}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
Daftar Wilayah POLDA dan
|
||||
POLRES
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||
{listDest.map((polda: any) => (
|
||||
<div
|
||||
key={polda.id}
|
||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||
>
|
||||
{/* Header POLDA */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(polda.id),
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(polda.id),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="font-semibold text-gray-900 text-sm">
|
||||
{polda.name}
|
||||
</span>
|
||||
</Label>
|
||||
{polda.subDestination && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleExpand(
|
||||
polda.id,
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{polda.subDestination.map(
|
||||
(sub: any) => (
|
||||
<Label
|
||||
key={sub.id}
|
||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||
>
|
||||
<Checkbox
|
||||
checked={
|
||||
}}
|
||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
expandedPolda[
|
||||
polda.id
|
||||
]
|
||||
? "mdi:chevron-up"
|
||||
: "mdi:chevron-down"
|
||||
}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Sub-items */}
|
||||
{polda.subDestination &&
|
||||
expandedPolda[
|
||||
polda.id
|
||||
] && (
|
||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||
{/* Tombol Pilih Semua untuk sub-items */}
|
||||
<div className="mb-2 flex justify-start">
|
||||
{(() => {
|
||||
const allSubItemsChecked =
|
||||
polda.subDestination?.every(
|
||||
(sub: any) =>
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
sub.id,
|
||||
),
|
||||
),
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
polda,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
)
|
||||
)}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-indeterminate-small"
|
||||
width={
|
||||
12
|
||||
}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Batal
|
||||
Semua
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-all"
|
||||
width={
|
||||
12
|
||||
}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Pilih
|
||||
Semua
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{polda.subDestination.map(
|
||||
(sub: any) => (
|
||||
<Label
|
||||
key={sub.id}
|
||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||
>
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id,
|
||||
),
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(
|
||||
sub.id,
|
||||
),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{/* {t("save", {
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{/* {t("save", {
|
||||
defaultValue: "Simpan",
|
||||
})} */}
|
||||
Simpan
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
Simpan
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -1832,7 +1885,7 @@ export default function FormAudioDetail() {
|
|||
>
|
||||
{template}
|
||||
</Button>
|
||||
)
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1806,7 +1806,7 @@ export default function FormAudio() {
|
|||
</Card>
|
||||
<div className="flex flex-row justify-end gap-3">
|
||||
<div className="mt-4">
|
||||
{levelNumber !== "2" && levelNumber !== "3" && (
|
||||
{levelNumber !== "2" && (
|
||||
<Button type="submit" color="primary">
|
||||
{t("submit", { defaultValue: "Submit" })}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ const ViewEditor = dynamic(
|
|||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
interface Destination {
|
||||
|
|
@ -172,6 +172,12 @@ export default function FormImageDetail() {
|
|||
satker: boolean;
|
||||
}>
|
||||
>([]);
|
||||
const [creatorLevelNumber, setCreatorLevelNumber] = useState<number | null>(
|
||||
null,
|
||||
);
|
||||
const isContentFromSatker = creatorLevelNumber === 3;
|
||||
const isContentFromMabesOrPolda =
|
||||
creatorLevelNumber === 1 || creatorLevelNumber === 2;
|
||||
|
||||
useEffect(() => {
|
||||
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
|
||||
|
|
@ -200,7 +206,7 @@ export default function FormImageDetail() {
|
|||
const [files, setFiles] = useState<FileType[]>([]);
|
||||
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
||||
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>(
|
||||
{}
|
||||
{},
|
||||
);
|
||||
|
||||
// State untuk melacak apakah perubahan berasal dari checkbox utama
|
||||
|
|
@ -231,7 +237,7 @@ export default function FormImageDetail() {
|
|||
useEffect(() => {
|
||||
if (detail?.assignedToTopLevel) {
|
||||
const outputSet = new Set(
|
||||
detail.assignedToTopLevel.split(",").map(Number)
|
||||
detail.assignedToTopLevel.split(",").map(Number),
|
||||
);
|
||||
setUnitSelection({
|
||||
semua: outputSet.has(0),
|
||||
|
|
@ -260,7 +266,7 @@ export default function FormImageDetail() {
|
|||
acc[polda.id] = false;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
{},
|
||||
);
|
||||
setExpandedPolda(initialExpandedState);
|
||||
console.log("polres", initialExpandedState);
|
||||
|
|
@ -299,7 +305,7 @@ export default function FormImageDetail() {
|
|||
(item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
checkedLevels.has(Number(item.id))
|
||||
checkedLevels.has(Number(item.id)),
|
||||
).length;
|
||||
|
||||
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
|
||||
|
|
@ -308,7 +314,7 @@ export default function FormImageDetail() {
|
|||
return (
|
||||
total +
|
||||
item.subDestination.filter((sub: any) =>
|
||||
checkedLevels.has(Number(sub.id))
|
||||
checkedLevels.has(Number(sub.id)),
|
||||
).length
|
||||
);
|
||||
}
|
||||
|
|
@ -316,12 +322,12 @@ export default function FormImageDetail() {
|
|||
}, 0);
|
||||
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
const checkedSatkerCount = satkerItem
|
||||
? (checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) +
|
||||
(satkerItem.subDestination?.filter((sub: any) =>
|
||||
checkedLevels.has(Number(sub.id))
|
||||
checkedLevels.has(Number(sub.id)),
|
||||
).length || 0)
|
||||
: 0;
|
||||
|
||||
|
|
@ -391,7 +397,7 @@ export default function FormImageDetail() {
|
|||
} else if (mainCheckboxChangeType === "satker_checked") {
|
||||
// Checklist satker
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
newCheckedLevels.add(Number(satkerItem.id));
|
||||
|
|
@ -439,7 +445,7 @@ export default function FormImageDetail() {
|
|||
} else if (mainCheckboxChangeType === "satker_unchecked") {
|
||||
// Clear satker dan semua sub-item di bawahnya dari checkedLevels
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
newCheckedLevels.delete(Number(satkerItem.id));
|
||||
|
|
@ -466,7 +472,7 @@ export default function FormImageDetail() {
|
|||
const handleFileUnitChange = (
|
||||
fileIndex: number,
|
||||
key: keyof typeof unitSelection,
|
||||
value: boolean
|
||||
value: boolean,
|
||||
) => {
|
||||
setFileUnitSelections((prev) => {
|
||||
const newSelections = [...prev];
|
||||
|
|
@ -486,7 +492,7 @@ export default function FormImageDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[fileIndex] || new Set()
|
||||
newArray[fileIndex] || new Set(),
|
||||
);
|
||||
|
||||
if (value) {
|
||||
|
|
@ -517,12 +523,12 @@ export default function FormImageDetail() {
|
|||
(item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
currentFileCheckedLevels.has(Number(item.id))
|
||||
currentFileCheckedLevels.has(Number(item.id)),
|
||||
);
|
||||
|
||||
if (!hasSelectedPolda) {
|
||||
alert(
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
|
||||
);
|
||||
return prev; // Batalkan perubahan
|
||||
}
|
||||
|
|
@ -552,7 +558,7 @@ export default function FormImageDetail() {
|
|||
// Fungsi lama untuk kompatibilitas (akan dihapus nanti)
|
||||
const handleUnitChange = (
|
||||
key: keyof typeof unitSelection,
|
||||
value: boolean
|
||||
value: boolean,
|
||||
) => {
|
||||
// Set flag bahwa perubahan berasal dari checkbox utama
|
||||
setIsUpdatingFromMainCheckbox(true);
|
||||
|
|
@ -578,13 +584,13 @@ export default function FormImageDetail() {
|
|||
(item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
checkedLevels.has(Number(item.id))
|
||||
checkedLevels.has(Number(item.id)),
|
||||
);
|
||||
|
||||
if (!hasSelectedPolda) {
|
||||
// Jika tidak ada POLDA yang dipilih, tampilkan peringatan dan batalkan
|
||||
alert(
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
|
||||
);
|
||||
// Reset flag karena perubahan dibatalkan
|
||||
setIsUpdatingFromMainCheckbox(false);
|
||||
|
|
@ -640,14 +646,14 @@ export default function FormImageDetail() {
|
|||
|
||||
const handleCheckboxChange = (id: number) => {
|
||||
setSelectedPublishers((prev) =>
|
||||
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
||||
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id],
|
||||
);
|
||||
};
|
||||
|
||||
// Fungsi untuk mengupdate checklist levels untuk file tertentu
|
||||
const handleFileCheckboxChangePlacement = (
|
||||
fileIndex: number,
|
||||
levelId: number
|
||||
levelId: number,
|
||||
) => {
|
||||
setFileCheckedLevels((prev) => {
|
||||
const newArray = [...prev];
|
||||
|
|
@ -659,7 +665,7 @@ export default function FormImageDetail() {
|
|||
|
||||
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
|
||||
const poldaItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (
|
||||
poldaItem &&
|
||||
|
|
@ -682,7 +688,7 @@ export default function FormImageDetail() {
|
|||
|
||||
// Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya
|
||||
const satkerItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (satkerItem && satkerItem.name === "SATKER POLRI") {
|
||||
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
|
||||
|
|
@ -710,7 +716,7 @@ export default function FormImageDetail() {
|
|||
|
||||
// Cek apakah semua sub-items sudah ter-checklist
|
||||
const allSubItemsChecked = polda.subDestination?.every((sub: any) =>
|
||||
currentFileLevels.has(Number(sub.id))
|
||||
currentFileLevels.has(Number(sub.id)),
|
||||
);
|
||||
|
||||
if (allSubItemsChecked) {
|
||||
|
|
@ -750,7 +756,7 @@ export default function FormImageDetail() {
|
|||
|
||||
// Hitung total POLDA yang ada (bukan SATKER POLRI)
|
||||
const totalPoldaCount = listDest.filter(
|
||||
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
|
||||
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI",
|
||||
).length;
|
||||
|
||||
// Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI)
|
||||
|
|
@ -777,7 +783,7 @@ export default function FormImageDetail() {
|
|||
}
|
||||
return total;
|
||||
},
|
||||
0
|
||||
0,
|
||||
);
|
||||
|
||||
// Hitung berapa banyak POLRES yang ter-checklist
|
||||
|
|
@ -787,7 +793,7 @@ export default function FormImageDetail() {
|
|||
return (
|
||||
total +
|
||||
item.subDestination.filter((sub: any) =>
|
||||
currentFileLevels.has(Number(sub.id))
|
||||
currentFileLevels.has(Number(sub.id)),
|
||||
).length
|
||||
);
|
||||
}
|
||||
|
|
@ -796,7 +802,7 @@ export default function FormImageDetail() {
|
|||
|
||||
// Cek apakah SATKER POLRI ter-checklist
|
||||
const satkerItem = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
const isSatkerChecked =
|
||||
satkerItem && currentFileLevels.has(Number(satkerItem.id));
|
||||
|
|
@ -833,7 +839,7 @@ export default function FormImageDetail() {
|
|||
|
||||
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
|
||||
const poldaItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (
|
||||
poldaItem &&
|
||||
|
|
@ -856,7 +862,7 @@ export default function FormImageDetail() {
|
|||
|
||||
// Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya
|
||||
const satkerItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (satkerItem && satkerItem.name === "SATKER POLRI") {
|
||||
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
|
||||
|
|
@ -887,7 +893,7 @@ export default function FormImageDetail() {
|
|||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
o.name.toLowerCase().includes("pers rilis"),
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
|
|
@ -936,6 +942,11 @@ export default function FormImageDetail() {
|
|||
console.log("detail", details);
|
||||
setFiles(details?.files);
|
||||
setDetail(details);
|
||||
|
||||
if (details?.uploadedBy?.userLevel?.levelNumber) {
|
||||
setCreatorLevelNumber(details.uploadedBy.userLevel.levelNumber);
|
||||
}
|
||||
|
||||
setMain({
|
||||
type: details?.fileType.name,
|
||||
url: details?.files[0]?.url,
|
||||
|
|
@ -946,14 +957,14 @@ export default function FormImageDetail() {
|
|||
|
||||
if (details?.assignedToLevel) {
|
||||
const levels = new Set<number>(
|
||||
details.assignedToLevel.split(",").map(Number)
|
||||
details.assignedToLevel.split(",").map(Number),
|
||||
);
|
||||
setCheckedLevels(levels);
|
||||
}
|
||||
|
||||
if (details?.publishedForObject) {
|
||||
const publisherIds = details?.publishedForObject?.map(
|
||||
(obj: any) => obj.id
|
||||
(obj: any) => obj.id,
|
||||
);
|
||||
setSelectedPublishers(publisherIds);
|
||||
}
|
||||
|
|
@ -963,7 +974,7 @@ export default function FormImageDetail() {
|
|||
|
||||
const filesData = details.files || [];
|
||||
const fileUrls = filesData.map((file: { thumbnailFileUrl: string }) =>
|
||||
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg"
|
||||
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg",
|
||||
);
|
||||
setDetailThumb(fileUrls);
|
||||
|
||||
|
|
@ -1083,7 +1094,7 @@ export default function FormImageDetail() {
|
|||
const setupPlacement = (
|
||||
index: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
checked: boolean,
|
||||
) => {
|
||||
let temp = [...filePlacements];
|
||||
if (checked) {
|
||||
|
|
@ -1094,7 +1105,7 @@ export default function FormImageDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[index] || new Set()
|
||||
newArray[index] || new Set(),
|
||||
);
|
||||
|
||||
// Checklist semua item di modal
|
||||
|
|
@ -1144,7 +1155,7 @@ export default function FormImageDetail() {
|
|||
// Hanya auto-checklist "all" jika polda, polres, dan mabes ter-checklist
|
||||
// JANGAN include satker dalam perhitungan auto "all"
|
||||
const nonSatkerItems = now.filter(
|
||||
(item) => item !== "satker" && item !== "all"
|
||||
(item) => item !== "satker" && item !== "all",
|
||||
);
|
||||
if (nonSatkerItems.length === 3 && !now.includes("all")) {
|
||||
now.push("all");
|
||||
|
|
@ -1159,7 +1170,7 @@ export default function FormImageDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[index] || new Set()
|
||||
newArray[index] || new Set(),
|
||||
);
|
||||
|
||||
// Unchecklist semua item di modal
|
||||
|
|
@ -1193,7 +1204,7 @@ export default function FormImageDetail() {
|
|||
// Hapus "all" jika tidak semua item ter-checklist
|
||||
if (now.includes("all")) {
|
||||
const nonSatkerItems = now.filter(
|
||||
(item) => item !== "satker" && item !== "all"
|
||||
(item) => item !== "satker" && item !== "all",
|
||||
);
|
||||
if (nonSatkerItems.length < 3) {
|
||||
const newData = now.filter((b) => b !== "all");
|
||||
|
|
@ -1212,7 +1223,7 @@ export default function FormImageDetail() {
|
|||
const updateModalChecklistLevels = (
|
||||
fileIndex: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
checked: boolean,
|
||||
) => {
|
||||
if (!listDest || listDest.length === 0) return;
|
||||
|
||||
|
|
@ -1245,7 +1256,7 @@ export default function FormImageDetail() {
|
|||
} else if (placement === "satker") {
|
||||
// Checklist SATKER POLRI dan semua sub-item di bawahnya
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
currentFileLevels.add(Number(satkerItem.id));
|
||||
|
|
@ -1281,7 +1292,7 @@ export default function FormImageDetail() {
|
|||
} else if (placement === "satker") {
|
||||
// Unchecklist SATKER POLRI dan semua sub-item di bawahnya
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
currentFileLevels.delete(Number(satkerItem.id));
|
||||
|
|
@ -1311,7 +1322,7 @@ export default function FormImageDetail() {
|
|||
type: string,
|
||||
url: string,
|
||||
names: string,
|
||||
format: string
|
||||
format: string,
|
||||
) => {
|
||||
console.log("Test 3 :", type, url, names, format);
|
||||
setMain({
|
||||
|
|
@ -1411,7 +1422,7 @@ export default function FormImageDetail() {
|
|||
{detail &&
|
||||
!categories.find(
|
||||
(cat) =>
|
||||
String(cat.id) === String(detail.category.id)
|
||||
String(cat.id) === String(detail.category.id),
|
||||
) && (
|
||||
<SelectItem
|
||||
key={String(detail.category.id)}
|
||||
|
|
@ -1732,7 +1743,7 @@ export default function FormImageDetail() {
|
|||
Tingkat Distribusi:
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{/* {[
|
||||
{ key: "semua", label: "Semua" },
|
||||
{ key: "nasional", label: "Nasional" },
|
||||
{ key: "wilayah", label: "Wilayah" },
|
||||
|
|
@ -1740,6 +1751,39 @@ export default function FormImageDetail() {
|
|||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
] */}
|
||||
{[
|
||||
{ key: "semua", label: "Semua" },
|
||||
|
||||
...(isContentFromMabesOrPolda
|
||||
? [
|
||||
{
|
||||
key: "nasional",
|
||||
label: "Nasional",
|
||||
},
|
||||
{
|
||||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
{
|
||||
key: "wilayah",
|
||||
label: "Wilayah",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
...(isContentFromSatker
|
||||
? [
|
||||
{
|
||||
key: "nasional",
|
||||
label: "Nasional",
|
||||
},
|
||||
{
|
||||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
|
|
@ -1756,12 +1800,12 @@ export default function FormImageDetail() {
|
|||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean
|
||||
value as boolean,
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value)
|
||||
Boolean(value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -1777,246 +1821,257 @@ export default function FormImageDetail() {
|
|||
</div>
|
||||
|
||||
{/* Detail Wilayah */}
|
||||
{fileUnitSelections[index]?.wilayah && (
|
||||
<div className="border-t border-gray-200 pt-2">
|
||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||
Detail Wilayah:
|
||||
</p>
|
||||
{/* {fileUnitSelections[index]?.wilayah && ( */}
|
||||
{!isContentFromSatker &&
|
||||
fileUnitSelections[index]?.wilayah && (
|
||||
<div className="border-t border-gray-200 pt-2">
|
||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||
Detail Wilayah:
|
||||
</p>
|
||||
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
<Checkbox
|
||||
id={`${item.key}-${index}`}
|
||||
checked={
|
||||
fileUnitSelections[index]?.[
|
||||
item.key as keyof typeof unitSelection
|
||||
] || false
|
||||
}
|
||||
onCheckedChange={(value) => {
|
||||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`${item.key}-${index}`}
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
{item.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
<Checkbox
|
||||
id={`${item.key}-${index}`}
|
||||
checked={
|
||||
fileUnitSelections[index]?.[
|
||||
item.key as keyof typeof unitSelection
|
||||
] || false
|
||||
}
|
||||
onCheckedChange={(value) => {
|
||||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean,
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`${item.key}-${index}`}
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
{t("custom", {
|
||||
defaultValue: "Kustom",
|
||||
})}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
Daftar Wilayah POLDA dan POLRES
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||
{listDest.map((polda: any) => (
|
||||
<div
|
||||
key={polda.id}
|
||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||
>
|
||||
{/* Header POLDA */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(polda.id)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(polda.id)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="font-semibold text-gray-900 text-sm">
|
||||
{polda.name}
|
||||
</span>
|
||||
</Label>
|
||||
{polda.subDestination && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleExpand(
|
||||
polda.id
|
||||
);
|
||||
}}
|
||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
expandedPolda[
|
||||
polda.id
|
||||
]
|
||||
? "mdi:chevron-up"
|
||||
: "mdi:chevron-down"
|
||||
}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{item.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Sub-items */}
|
||||
{polda.subDestination &&
|
||||
expandedPolda[polda.id] && (
|
||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||
{/* Tombol Pilih Semua untuk sub-items */}
|
||||
<div className="mb-2 flex justify-start">
|
||||
{(() => {
|
||||
const allSubItemsChecked =
|
||||
polda.subDestination?.every(
|
||||
(sub: any) =>
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
polda
|
||||
)
|
||||
}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-indeterminate-small"
|
||||
width={12}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Batal Semua
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-all"
|
||||
width={12}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Pilih Semua
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
{t("custom", {
|
||||
defaultValue: "Kustom",
|
||||
})}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
Daftar Wilayah POLDA dan
|
||||
POLRES
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||
{listDest.map((polda: any) => (
|
||||
<div
|
||||
key={polda.id}
|
||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||
>
|
||||
{/* Header POLDA */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(polda.id),
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(polda.id),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="font-semibold text-gray-900 text-sm">
|
||||
{polda.name}
|
||||
</span>
|
||||
</Label>
|
||||
{polda.subDestination && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleExpand(
|
||||
polda.id,
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{polda.subDestination.map(
|
||||
(sub: any) => (
|
||||
<Label
|
||||
key={sub.id}
|
||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||
>
|
||||
<Checkbox
|
||||
checked={
|
||||
}}
|
||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
expandedPolda[
|
||||
polda.id
|
||||
]
|
||||
? "mdi:chevron-up"
|
||||
: "mdi:chevron-down"
|
||||
}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Sub-items */}
|
||||
{polda.subDestination &&
|
||||
expandedPolda[
|
||||
polda.id
|
||||
] && (
|
||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||
{/* Tombol Pilih Semua untuk sub-items */}
|
||||
<div className="mb-2 flex justify-start">
|
||||
{(() => {
|
||||
const allSubItemsChecked =
|
||||
polda.subDestination?.every(
|
||||
(sub: any) =>
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
sub.id,
|
||||
),
|
||||
),
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
polda,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
)
|
||||
)}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-indeterminate-small"
|
||||
width={
|
||||
12
|
||||
}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Batal
|
||||
Semua
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-all"
|
||||
width={
|
||||
12
|
||||
}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Pilih
|
||||
Semua
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{polda.subDestination.map(
|
||||
(sub: any) => (
|
||||
<Label
|
||||
key={sub.id}
|
||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||
>
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id,
|
||||
),
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(
|
||||
sub.id,
|
||||
),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>Simpan</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>Simpan</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -2095,7 +2150,7 @@ export default function FormImageDetail() {
|
|||
>
|
||||
{template}
|
||||
</Button>
|
||||
)
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ const CustomEditor = dynamic(
|
|||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
export default function FormImage() {
|
||||
|
|
@ -117,7 +117,7 @@ export default function FormImage() {
|
|||
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
||||
null
|
||||
null,
|
||||
);
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] =
|
||||
|
|
@ -183,17 +183,17 @@ export default function FormImage() {
|
|||
.filter(
|
||||
(file) =>
|
||||
["image/jpeg", "image/png", "image/jpg"].includes(file.type) &&
|
||||
file.size <= MAX_FILE_SIZE
|
||||
file.size <= MAX_FILE_SIZE,
|
||||
)
|
||||
.map((file) =>
|
||||
Object.assign(file, {
|
||||
preview: URL.createObjectURL(file),
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
if (validFiles.length === 0) {
|
||||
toast.error(
|
||||
"File tidak valid. Hanya .jpg, .jpeg, .png maksimal 100MB yang diperbolehkan."
|
||||
"File tidak valid. Hanya .jpg, .jpeg, .png maksimal 100MB yang diperbolehkan.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -227,12 +227,12 @@ export default function FormImage() {
|
|||
files.every(
|
||||
(file: File) =>
|
||||
["image/jpeg", "image/png", "image/jpg"].includes(file.type) &&
|
||||
file.size <= 100 * 1024 * 1024
|
||||
file.size <= 100 * 1024 * 1024,
|
||||
),
|
||||
{
|
||||
message:
|
||||
"Hanya file .jpg, .jpeg, .png, maksimal 100MB yang diperbolehkan.",
|
||||
}
|
||||
},
|
||||
),
|
||||
categoryId: z.string().min(1, { message: "Kategori wajib dipilih." }),
|
||||
tags: z
|
||||
|
|
@ -454,7 +454,7 @@ export default function FormImage() {
|
|||
const articleData = await waitForStatusUpdate();
|
||||
const cleanArticleBody = articleData?.articleBody?.replace(
|
||||
/<img[^>]*>/g,
|
||||
""
|
||||
"",
|
||||
);
|
||||
const articleImagesData = articleData?.imagesUrl?.split(",");
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
|
|
@ -507,7 +507,7 @@ export default function FormImage() {
|
|||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
o.name.toLowerCase().includes("pers rilis"),
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
|
|
@ -530,7 +530,7 @@ export default function FormImage() {
|
|||
setPublishedFor(
|
||||
options
|
||||
.filter((opt: any) => opt.id !== "all")
|
||||
.map((opt: any) => opt.id)
|
||||
.map((opt: any) => opt.id),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -594,11 +594,16 @@ export default function FormImage() {
|
|||
} else if (data.rewriteDescription && selectedFileType === "rewrite") {
|
||||
finalDescription = data.rewriteDescription;
|
||||
console.log("📤 Upload versi rewrite");
|
||||
} else {
|
||||
// fallback: gunakan versi Indonesia original
|
||||
finalDescription = data.descriptionOri ?? "";
|
||||
console.log("📤 Upload versi Indonesia (original)");
|
||||
} else if (data.description?.trim()) {
|
||||
finalDescription = data.description;
|
||||
} else if (data.descriptionOri?.trim()) {
|
||||
finalDescription = data.descriptionOri;
|
||||
}
|
||||
// else {
|
||||
// // fallback: gunakan versi Indonesia original
|
||||
// finalDescription = data.descriptionOri ?? "";
|
||||
// console.log("📤 Upload versi Indonesia (original)");
|
||||
// }
|
||||
|
||||
if (!finalDescription?.trim()) {
|
||||
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
|
||||
|
|
@ -690,7 +695,7 @@ export default function FormImage() {
|
|||
index,
|
||||
String(id),
|
||||
item,
|
||||
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
|
||||
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0",
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -717,7 +722,7 @@ export default function FormImage() {
|
|||
idx: number,
|
||||
id: string,
|
||||
file: any,
|
||||
duration: string
|
||||
duration: string,
|
||||
) {
|
||||
console.log(idx, id, file, duration);
|
||||
|
||||
|
|
@ -753,7 +758,7 @@ export default function FormImage() {
|
|||
onChunkComplete: (
|
||||
chunkSize: any,
|
||||
bytesAccepted: any,
|
||||
bytesTotal: any
|
||||
bytesTotal: any,
|
||||
) => {
|
||||
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
||||
progressInfo[idx].percentage = uploadPersen;
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ const ViewEditor = dynamic(
|
|||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
interface Destination {
|
||||
|
|
@ -191,6 +191,12 @@ export default function FormTeksDetail() {
|
|||
satker: boolean;
|
||||
}>
|
||||
>([]);
|
||||
const [creatorLevelNumber, setCreatorLevelNumber] = useState<number | null>(
|
||||
null,
|
||||
);
|
||||
const isContentFromSatker = creatorLevelNumber === 3;
|
||||
const isContentFromMabesOrPolda =
|
||||
creatorLevelNumber === 1 || creatorLevelNumber === 2;
|
||||
|
||||
useEffect(() => {
|
||||
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
|
||||
|
|
@ -202,7 +208,7 @@ export default function FormTeksDetail() {
|
|||
const handleFileUnitChange = (
|
||||
fileIndex: number,
|
||||
key: keyof typeof unitSelection,
|
||||
value: boolean
|
||||
value: boolean,
|
||||
) => {
|
||||
setFileUnitSelections((prev) => {
|
||||
const newSelections = [...prev];
|
||||
|
|
@ -222,7 +228,7 @@ export default function FormTeksDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[fileIndex] || new Set()
|
||||
newArray[fileIndex] || new Set(),
|
||||
);
|
||||
|
||||
if (value) {
|
||||
|
|
@ -253,12 +259,12 @@ export default function FormTeksDetail() {
|
|||
(item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
currentFileCheckedLevels.has(Number(item.id))
|
||||
currentFileCheckedLevels.has(Number(item.id)),
|
||||
);
|
||||
|
||||
if (!hasSelectedPolda) {
|
||||
alert(
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
|
||||
);
|
||||
return prev; // Batalkan perubahan
|
||||
}
|
||||
|
|
@ -297,7 +303,7 @@ export default function FormTeksDetail() {
|
|||
useEffect(() => {
|
||||
if (detail?.assignedToTopLevel) {
|
||||
const outputSet = new Set(
|
||||
detail.assignedToTopLevel.split(",").map(Number)
|
||||
detail.assignedToTopLevel.split(",").map(Number),
|
||||
);
|
||||
setUnitSelection({
|
||||
semua: outputSet.has(0),
|
||||
|
|
@ -322,7 +328,7 @@ export default function FormTeksDetail() {
|
|||
acc[polda.id] = false;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
{},
|
||||
);
|
||||
setExpandedPolda(initialExpandedState);
|
||||
console.log("polres", initialExpandedState);
|
||||
|
|
@ -357,7 +363,7 @@ export default function FormTeksDetail() {
|
|||
|
||||
const handleUnitChange = (
|
||||
key: keyof typeof unitSelection,
|
||||
value: boolean
|
||||
value: boolean,
|
||||
) => {
|
||||
if (key === "semua") {
|
||||
const newState = {
|
||||
|
|
@ -436,7 +442,7 @@ export default function FormTeksDetail() {
|
|||
|
||||
const handleCheckboxChange = (id: number) => {
|
||||
setSelectedPublishers((prev) =>
|
||||
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
||||
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id],
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -458,7 +464,7 @@ export default function FormTeksDetail() {
|
|||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
o.name.toLowerCase().includes("pers rilis"),
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
|
|
@ -507,6 +513,9 @@ export default function FormTeksDetail() {
|
|||
console.log("detail", details);
|
||||
setFiles(details?.files);
|
||||
setDetail(details);
|
||||
if (details?.uploadedBy?.userLevel?.levelNumber) {
|
||||
setCreatorLevelNumber(details.uploadedBy.userLevel.levelNumber);
|
||||
}
|
||||
setMain({
|
||||
type: details?.fileType.name,
|
||||
url: details?.files[0]?.url,
|
||||
|
|
@ -517,14 +526,14 @@ export default function FormTeksDetail() {
|
|||
|
||||
if (details?.assignedToLevel) {
|
||||
const levels = new Set(
|
||||
details.assignedToLevel.split(",").map(Number)
|
||||
details.assignedToLevel.split(",").map(Number),
|
||||
);
|
||||
setCheckedLevels(levels);
|
||||
}
|
||||
|
||||
if (details?.publishedForObject) {
|
||||
const publisherIds = details?.publishedForObject?.map(
|
||||
(obj: any) => obj.id
|
||||
(obj: any) => obj.id,
|
||||
);
|
||||
setSelectedPublishers(publisherIds);
|
||||
}
|
||||
|
|
@ -696,7 +705,7 @@ export default function FormTeksDetail() {
|
|||
type: string,
|
||||
url: string,
|
||||
names: string,
|
||||
format: string
|
||||
format: string,
|
||||
) => {
|
||||
console.log("Test 3 :", type, url, names, format);
|
||||
setMain({
|
||||
|
|
@ -723,7 +732,7 @@ export default function FormTeksDetail() {
|
|||
const setupPlacement = (
|
||||
index: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
checked: boolean,
|
||||
) => {
|
||||
let temp = [...filePlacements];
|
||||
if (checked) {
|
||||
|
|
@ -734,7 +743,7 @@ export default function FormTeksDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[index] || new Set()
|
||||
newArray[index] || new Set(),
|
||||
);
|
||||
|
||||
// Checklist semua item di modal
|
||||
|
|
@ -784,7 +793,7 @@ export default function FormTeksDetail() {
|
|||
// Hanya auto-checklist "all" jika polda, polres, dan mabes ter-checklist
|
||||
// JANGAN include satker dalam perhitungan auto "all"
|
||||
const nonSatkerItems = now.filter(
|
||||
(item) => item !== "satker" && item !== "all"
|
||||
(item) => item !== "satker" && item !== "all",
|
||||
);
|
||||
if (nonSatkerItems.length === 3 && !now.includes("all")) {
|
||||
now.push("all");
|
||||
|
|
@ -799,7 +808,7 @@ export default function FormTeksDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[index] || new Set()
|
||||
newArray[index] || new Set(),
|
||||
);
|
||||
|
||||
// Unchecklist semua item di modal
|
||||
|
|
@ -833,7 +842,7 @@ export default function FormTeksDetail() {
|
|||
// Hapus "all" jika tidak semua item ter-checklist
|
||||
if (now.includes("all")) {
|
||||
const nonSatkerItems = now.filter(
|
||||
(item) => item !== "satker" && item !== "all"
|
||||
(item) => item !== "satker" && item !== "all",
|
||||
);
|
||||
if (nonSatkerItems.length < 3) {
|
||||
const newData = now.filter((b) => b !== "all");
|
||||
|
|
@ -851,7 +860,7 @@ export default function FormTeksDetail() {
|
|||
const updateModalChecklistLevels = (
|
||||
fileIndex: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
checked: boolean,
|
||||
) => {
|
||||
if (!listDest || listDest.length === 0) return;
|
||||
|
||||
|
|
@ -884,7 +893,7 @@ export default function FormTeksDetail() {
|
|||
} else if (placement === "satker") {
|
||||
// Checklist SATKER POLRI dan semua sub-item di bawahnya
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
currentFileLevels.add(Number(satkerItem.id));
|
||||
|
|
@ -920,7 +929,7 @@ export default function FormTeksDetail() {
|
|||
} else if (placement === "satker") {
|
||||
// Unchecklist SATKER POLRI dan semua sub-item di bawahnya
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
currentFileLevels.delete(Number(satkerItem.id));
|
||||
|
|
@ -953,7 +962,7 @@ export default function FormTeksDetail() {
|
|||
// Fungsi untuk mengupdate checklist levels untuk file tertentu
|
||||
const handleFileCheckboxChangePlacement = (
|
||||
fileIndex: number,
|
||||
levelId: number
|
||||
levelId: number,
|
||||
) => {
|
||||
setFileCheckedLevels((prev) => {
|
||||
const newArray = [...prev];
|
||||
|
|
@ -965,7 +974,7 @@ export default function FormTeksDetail() {
|
|||
|
||||
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
|
||||
const poldaItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (
|
||||
poldaItem &&
|
||||
|
|
@ -988,7 +997,7 @@ export default function FormTeksDetail() {
|
|||
|
||||
// Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya
|
||||
const satkerItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (satkerItem && satkerItem.name === "SATKER POLRI") {
|
||||
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
|
||||
|
|
@ -1019,7 +1028,7 @@ export default function FormTeksDetail() {
|
|||
|
||||
// Hitung total POLDA yang ada (bukan SATKER POLRI)
|
||||
const totalPoldaCount = listDest.filter(
|
||||
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
|
||||
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI",
|
||||
).length;
|
||||
|
||||
// Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI)
|
||||
|
|
@ -1046,7 +1055,7 @@ export default function FormTeksDetail() {
|
|||
}
|
||||
return total;
|
||||
},
|
||||
0
|
||||
0,
|
||||
);
|
||||
|
||||
// Hitung berapa banyak POLRES yang ter-checklist
|
||||
|
|
@ -1056,7 +1065,7 @@ export default function FormTeksDetail() {
|
|||
return (
|
||||
total +
|
||||
item.subDestination.filter((sub: any) =>
|
||||
currentFileLevels.has(Number(sub.id))
|
||||
currentFileLevels.has(Number(sub.id)),
|
||||
).length
|
||||
);
|
||||
}
|
||||
|
|
@ -1065,7 +1074,7 @@ export default function FormTeksDetail() {
|
|||
|
||||
// Cek apakah SATKER POLRI ter-checklist
|
||||
const satkerItem = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
const isSatkerChecked =
|
||||
satkerItem && currentFileLevels.has(Number(satkerItem.id));
|
||||
|
|
@ -1099,7 +1108,7 @@ export default function FormTeksDetail() {
|
|||
|
||||
// Cek apakah semua sub-items sudah ter-checklist
|
||||
const allSubItemsChecked = polda.subDestination?.every((sub: any) =>
|
||||
currentFileLevels.has(Number(sub.id))
|
||||
currentFileLevels.has(Number(sub.id)),
|
||||
);
|
||||
|
||||
if (allSubItemsChecked) {
|
||||
|
|
@ -1182,7 +1191,7 @@ export default function FormTeksDetail() {
|
|||
{detail &&
|
||||
!categories.find(
|
||||
(cat) =>
|
||||
String(cat.id) === String(detail.category.id)
|
||||
String(cat.id) === String(detail.category.id),
|
||||
) && (
|
||||
<SelectItem
|
||||
key={String(detail.category.id)}
|
||||
|
|
@ -1465,7 +1474,7 @@ export default function FormTeksDetail() {
|
|||
Tingkat Distribusi:
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{/* {[
|
||||
{ key: "semua", label: "Semua" },
|
||||
{ key: "nasional", label: "Nasional" },
|
||||
{ key: "wilayah", label: "Wilayah" },
|
||||
|
|
@ -1473,6 +1482,39 @@ export default function FormTeksDetail() {
|
|||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
] */}
|
||||
{[
|
||||
{ key: "semua", label: "Semua" },
|
||||
|
||||
...(isContentFromMabesOrPolda
|
||||
? [
|
||||
{
|
||||
key: "nasional",
|
||||
label: "Nasional",
|
||||
},
|
||||
{
|
||||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
{
|
||||
key: "wilayah",
|
||||
label: "Wilayah",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
...(isContentFromSatker
|
||||
? [
|
||||
{
|
||||
key: "nasional",
|
||||
label: "Nasional",
|
||||
},
|
||||
{
|
||||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
|
|
@ -1489,12 +1531,12 @@ export default function FormTeksDetail() {
|
|||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean
|
||||
value as boolean,
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value)
|
||||
Boolean(value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -1510,251 +1552,262 @@ export default function FormTeksDetail() {
|
|||
</div>
|
||||
|
||||
{/* Detail Wilayah */}
|
||||
{fileUnitSelections[index]?.wilayah && (
|
||||
<div className="border-t border-gray-200 pt-2">
|
||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||
Detail Wilayah:
|
||||
</p>
|
||||
{/* {fileUnitSelections[index]?.wilayah && ( */}
|
||||
{!isContentFromSatker &&
|
||||
fileUnitSelections[index]?.wilayah && (
|
||||
<div className="border-t border-gray-200 pt-2">
|
||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||
Detail Wilayah:
|
||||
</p>
|
||||
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
<Checkbox
|
||||
id={`${item.key}-${index}`}
|
||||
checked={
|
||||
fileUnitSelections[index]?.[
|
||||
item.key as keyof typeof unitSelection
|
||||
] || false
|
||||
}
|
||||
onCheckedChange={(value) => {
|
||||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`${item.key}-${index}`}
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
{item.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
<Checkbox
|
||||
id={`${item.key}-${index}`}
|
||||
checked={
|
||||
fileUnitSelections[index]?.[
|
||||
item.key as keyof typeof unitSelection
|
||||
] || false
|
||||
}
|
||||
onCheckedChange={(value) => {
|
||||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean,
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`${item.key}-${index}`}
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
{t("custom", {
|
||||
defaultValue: "Kustom",
|
||||
})}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
Daftar Wilayah POLDA dan POLRES
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||
{listDest.map((polda: any) => (
|
||||
<div
|
||||
key={polda.id}
|
||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||
>
|
||||
{/* Header POLDA */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(polda.id)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(polda.id)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="font-semibold text-gray-900 text-sm">
|
||||
{polda.name}
|
||||
</span>
|
||||
</Label>
|
||||
{polda.subDestination && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleExpand(
|
||||
polda.id
|
||||
);
|
||||
}}
|
||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
expandedPolda[
|
||||
polda.id
|
||||
]
|
||||
? "mdi:chevron-up"
|
||||
: "mdi:chevron-down"
|
||||
}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{item.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Sub-items */}
|
||||
{polda.subDestination &&
|
||||
expandedPolda[polda.id] && (
|
||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||
{/* Tombol Pilih Semua untuk sub-items */}
|
||||
<div className="mb-2 flex justify-start">
|
||||
{(() => {
|
||||
const allSubItemsChecked =
|
||||
polda.subDestination?.every(
|
||||
(sub: any) =>
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
polda
|
||||
)
|
||||
}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-indeterminate-small"
|
||||
width={12}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Batal Semua
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-all"
|
||||
width={12}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Pilih Semua
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
{t("custom", {
|
||||
defaultValue: "Kustom",
|
||||
})}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
Daftar Wilayah POLDA dan
|
||||
POLRES
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||
{listDest.map((polda: any) => (
|
||||
<div
|
||||
key={polda.id}
|
||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||
>
|
||||
{/* Header POLDA */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(polda.id),
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(polda.id),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="font-semibold text-gray-900 text-sm">
|
||||
{polda.name}
|
||||
</span>
|
||||
</Label>
|
||||
{polda.subDestination && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleExpand(
|
||||
polda.id,
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{polda.subDestination.map(
|
||||
(sub: any) => (
|
||||
<Label
|
||||
key={sub.id}
|
||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||
>
|
||||
<Checkbox
|
||||
checked={
|
||||
}}
|
||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
expandedPolda[
|
||||
polda.id
|
||||
]
|
||||
? "mdi:chevron-up"
|
||||
: "mdi:chevron-down"
|
||||
}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Sub-items */}
|
||||
{polda.subDestination &&
|
||||
expandedPolda[
|
||||
polda.id
|
||||
] && (
|
||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||
{/* Tombol Pilih Semua untuk sub-items */}
|
||||
<div className="mb-2 flex justify-start">
|
||||
{(() => {
|
||||
const allSubItemsChecked =
|
||||
polda.subDestination?.every(
|
||||
(sub: any) =>
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
sub.id,
|
||||
),
|
||||
),
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
polda,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
)
|
||||
)}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-indeterminate-small"
|
||||
width={
|
||||
12
|
||||
}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Batal
|
||||
Semua
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-all"
|
||||
width={
|
||||
12
|
||||
}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Pilih
|
||||
Semua
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{polda.subDestination.map(
|
||||
(sub: any) => (
|
||||
<Label
|
||||
key={sub.id}
|
||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||
>
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id,
|
||||
),
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(
|
||||
sub.id,
|
||||
),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{/* {t("save", {
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{/* {t("save", {
|
||||
defaultValue: "Simpan",
|
||||
})} */}
|
||||
Simpan
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
Simpan
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -1833,7 +1886,7 @@ export default function FormTeksDetail() {
|
|||
>
|
||||
{template}
|
||||
</Button>
|
||||
)
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1776,7 +1776,7 @@ export default function FormTeks() {
|
|||
{/* <Button type="submit" color="primary">
|
||||
{t("submit", { defaultValue: "Submit" })}
|
||||
</Button> */}
|
||||
{levelNumber !== "2" && levelNumber !== "3" && (
|
||||
{levelNumber !== "2" && (
|
||||
<Button type="submit" color="primary">
|
||||
{t("submit", { defaultValue: "Submit" })}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ const ViewEditor = dynamic(
|
|||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
interface Destination {
|
||||
|
|
@ -190,6 +190,12 @@ export default function FormVideoDetail() {
|
|||
satker: boolean;
|
||||
}>
|
||||
>([]);
|
||||
const [creatorLevelNumber, setCreatorLevelNumber] = useState<number | null>(
|
||||
null,
|
||||
);
|
||||
const isContentFromSatker = creatorLevelNumber === 3;
|
||||
const isContentFromMabesOrPolda =
|
||||
creatorLevelNumber === 1 || creatorLevelNumber === 2;
|
||||
|
||||
useEffect(() => {
|
||||
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
|
||||
|
|
@ -201,7 +207,7 @@ export default function FormVideoDetail() {
|
|||
const handleFileUnitChange = (
|
||||
fileIndex: number,
|
||||
key: keyof typeof unitSelection,
|
||||
value: boolean
|
||||
value: boolean,
|
||||
) => {
|
||||
setFileUnitSelections((prev) => {
|
||||
const newSelections = [...prev];
|
||||
|
|
@ -221,7 +227,7 @@ export default function FormVideoDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[fileIndex] || new Set()
|
||||
newArray[fileIndex] || new Set(),
|
||||
);
|
||||
|
||||
if (value) {
|
||||
|
|
@ -252,12 +258,12 @@ export default function FormVideoDetail() {
|
|||
(item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
currentFileCheckedLevels.has(Number(item.id))
|
||||
currentFileCheckedLevels.has(Number(item.id)),
|
||||
);
|
||||
|
||||
if (!hasSelectedPolda) {
|
||||
alert(
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
|
||||
);
|
||||
return prev; // Batalkan perubahan
|
||||
}
|
||||
|
|
@ -296,7 +302,7 @@ export default function FormVideoDetail() {
|
|||
useEffect(() => {
|
||||
if (detail?.assignedToTopLevel) {
|
||||
const outputSet = new Set(
|
||||
detail.assignedToTopLevel.split(",").map(Number)
|
||||
detail.assignedToTopLevel.split(",").map(Number),
|
||||
);
|
||||
setUnitSelection({
|
||||
semua: outputSet.has(0),
|
||||
|
|
@ -321,7 +327,7 @@ export default function FormVideoDetail() {
|
|||
acc[polda.id] = false;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
{},
|
||||
);
|
||||
setExpandedPolda(initialExpandedState);
|
||||
console.log("polres", initialExpandedState);
|
||||
|
|
@ -356,7 +362,7 @@ export default function FormVideoDetail() {
|
|||
|
||||
const handleUnitChange = (
|
||||
key: keyof typeof unitSelection,
|
||||
value: boolean
|
||||
value: boolean,
|
||||
) => {
|
||||
if (key === "semua") {
|
||||
const newState = {
|
||||
|
|
@ -435,7 +441,7 @@ export default function FormVideoDetail() {
|
|||
|
||||
const handleCheckboxChange = (id: number) => {
|
||||
setSelectedPublishers((prev) =>
|
||||
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
||||
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id],
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -457,7 +463,7 @@ export default function FormVideoDetail() {
|
|||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
o.name.toLowerCase().includes("pers rilis"),
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
|
|
@ -480,6 +486,10 @@ export default function FormVideoDetail() {
|
|||
console.log("detail", details);
|
||||
setFiles(details?.files);
|
||||
setDetail(details);
|
||||
if (details?.uploadedBy?.userLevel?.levelNumber) {
|
||||
setCreatorLevelNumber(details.uploadedBy.userLevel.levelNumber);
|
||||
}
|
||||
|
||||
setMain({
|
||||
type: details?.fileType.name,
|
||||
url: details?.files[0]?.url,
|
||||
|
|
@ -489,14 +499,14 @@ export default function FormVideoDetail() {
|
|||
|
||||
if (details?.assignedToLevel) {
|
||||
const levels = new Set(
|
||||
details.assignedToLevel.split(",").map(Number)
|
||||
details.assignedToLevel.split(",").map(Number),
|
||||
);
|
||||
setCheckedLevels(levels);
|
||||
}
|
||||
|
||||
if (details?.publishedForObject) {
|
||||
const publisherIds = details?.publishedForObject?.map(
|
||||
(obj: any) => obj.id
|
||||
(obj: any) => obj.id,
|
||||
);
|
||||
setSelectedPublishers(publisherIds);
|
||||
}
|
||||
|
|
@ -506,7 +516,7 @@ export default function FormVideoDetail() {
|
|||
|
||||
const filesData = details?.files || [];
|
||||
const fileUrls = filesData.map((files: { url: string }) =>
|
||||
files.url ? files.url : "default-image.jpg"
|
||||
files.url ? files.url : "default-image.jpg",
|
||||
);
|
||||
setDetailVideo(fileUrls);
|
||||
|
||||
|
|
@ -684,7 +694,7 @@ export default function FormVideoDetail() {
|
|||
const setupPlacement = (
|
||||
index: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
checked: boolean,
|
||||
) => {
|
||||
let temp = [...filePlacements];
|
||||
if (checked) {
|
||||
|
|
@ -695,7 +705,7 @@ export default function FormVideoDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[index] || new Set()
|
||||
newArray[index] || new Set(),
|
||||
);
|
||||
|
||||
// Checklist semua item di modal
|
||||
|
|
@ -745,7 +755,7 @@ export default function FormVideoDetail() {
|
|||
// Hanya auto-checklist "all" jika polda, polres, dan mabes ter-checklist
|
||||
// JANGAN include satker dalam perhitungan auto "all"
|
||||
const nonSatkerItems = now.filter(
|
||||
(item) => item !== "satker" && item !== "all"
|
||||
(item) => item !== "satker" && item !== "all",
|
||||
);
|
||||
if (nonSatkerItems.length === 3 && !now.includes("all")) {
|
||||
now.push("all");
|
||||
|
|
@ -760,7 +770,7 @@ export default function FormVideoDetail() {
|
|||
setFileCheckedLevels((prevLevels) => {
|
||||
const newArray = [...prevLevels];
|
||||
const currentFileLevels = new Set<number>(
|
||||
newArray[index] || new Set()
|
||||
newArray[index] || new Set(),
|
||||
);
|
||||
|
||||
// Unchecklist semua item di modal
|
||||
|
|
@ -794,7 +804,7 @@ export default function FormVideoDetail() {
|
|||
// Hapus "all" jika tidak semua item ter-checklist
|
||||
if (now.includes("all")) {
|
||||
const nonSatkerItems = now.filter(
|
||||
(item) => item !== "satker" && item !== "all"
|
||||
(item) => item !== "satker" && item !== "all",
|
||||
);
|
||||
if (nonSatkerItems.length < 3) {
|
||||
const newData = now.filter((b) => b !== "all");
|
||||
|
|
@ -812,7 +822,7 @@ export default function FormVideoDetail() {
|
|||
const updateModalChecklistLevels = (
|
||||
fileIndex: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
checked: boolean,
|
||||
) => {
|
||||
if (!listDest || listDest.length === 0) return;
|
||||
|
||||
|
|
@ -845,7 +855,7 @@ export default function FormVideoDetail() {
|
|||
} else if (placement === "satker") {
|
||||
// Checklist SATKER POLRI dan semua sub-item di bawahnya
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
currentFileLevels.add(Number(satkerItem.id));
|
||||
|
|
@ -881,7 +891,7 @@ export default function FormVideoDetail() {
|
|||
} else if (placement === "satker") {
|
||||
// Unchecklist SATKER POLRI dan semua sub-item di bawahnya
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
if (satkerItem) {
|
||||
currentFileLevels.delete(Number(satkerItem.id));
|
||||
|
|
@ -910,7 +920,7 @@ export default function FormVideoDetail() {
|
|||
type: string,
|
||||
url: string,
|
||||
names: string,
|
||||
format: string
|
||||
format: string,
|
||||
) => {
|
||||
console.log("Test 3 :", type, url, names, format);
|
||||
setMain({
|
||||
|
|
@ -951,7 +961,7 @@ export default function FormVideoDetail() {
|
|||
// Fungsi untuk mengupdate checklist levels untuk file tertentu
|
||||
const handleFileCheckboxChangePlacement = (
|
||||
fileIndex: number,
|
||||
levelId: number
|
||||
levelId: number,
|
||||
) => {
|
||||
setFileCheckedLevels((prev) => {
|
||||
const newArray = [...prev];
|
||||
|
|
@ -963,7 +973,7 @@ export default function FormVideoDetail() {
|
|||
|
||||
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
|
||||
const poldaItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (
|
||||
poldaItem &&
|
||||
|
|
@ -986,7 +996,7 @@ export default function FormVideoDetail() {
|
|||
|
||||
// Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya
|
||||
const satkerItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
(item: any) => Number(item.id) === levelId,
|
||||
) as any;
|
||||
if (satkerItem && satkerItem.name === "SATKER POLRI") {
|
||||
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
|
||||
|
|
@ -1017,7 +1027,7 @@ export default function FormVideoDetail() {
|
|||
|
||||
// Hitung total POLDA yang ada (bukan SATKER POLRI)
|
||||
const totalPoldaCount = listDest.filter(
|
||||
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
|
||||
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI",
|
||||
).length;
|
||||
|
||||
// Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI)
|
||||
|
|
@ -1044,7 +1054,7 @@ export default function FormVideoDetail() {
|
|||
}
|
||||
return total;
|
||||
},
|
||||
0
|
||||
0,
|
||||
);
|
||||
|
||||
// Hitung berapa banyak POLRES yang ter-checklist
|
||||
|
|
@ -1054,7 +1064,7 @@ export default function FormVideoDetail() {
|
|||
return (
|
||||
total +
|
||||
item.subDestination.filter((sub: any) =>
|
||||
currentFileLevels.has(Number(sub.id))
|
||||
currentFileLevels.has(Number(sub.id)),
|
||||
).length
|
||||
);
|
||||
}
|
||||
|
|
@ -1063,7 +1073,7 @@ export default function FormVideoDetail() {
|
|||
|
||||
// Cek apakah SATKER POLRI ter-checklist
|
||||
const satkerItem = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
(item: any) => item.name === "SATKER POLRI",
|
||||
);
|
||||
const isSatkerChecked =
|
||||
satkerItem && currentFileLevels.has(Number(satkerItem.id));
|
||||
|
|
@ -1097,7 +1107,7 @@ export default function FormVideoDetail() {
|
|||
|
||||
// Cek apakah semua sub-items sudah ter-checklist
|
||||
const allSubItemsChecked = polda.subDestination?.every((sub: any) =>
|
||||
currentFileLevels.has(Number(sub.id))
|
||||
currentFileLevels.has(Number(sub.id)),
|
||||
);
|
||||
|
||||
if (allSubItemsChecked) {
|
||||
|
|
@ -1180,7 +1190,7 @@ export default function FormVideoDetail() {
|
|||
{detail &&
|
||||
!categories.find(
|
||||
(cat) =>
|
||||
String(cat.id) === String(detail.category.id)
|
||||
String(cat.id) === String(detail.category.id),
|
||||
) && (
|
||||
<SelectItem
|
||||
key={String(detail.category.id)}
|
||||
|
|
@ -1465,7 +1475,7 @@ export default function FormVideoDetail() {
|
|||
Tingkat Distribusi:
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{/* {[
|
||||
{ key: "semua", label: "Semua" },
|
||||
{ key: "nasional", label: "Nasional" },
|
||||
{ key: "wilayah", label: "Wilayah" },
|
||||
|
|
@ -1473,6 +1483,39 @@ export default function FormVideoDetail() {
|
|||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
] */}
|
||||
{[
|
||||
{ key: "semua", label: "Semua" },
|
||||
|
||||
...(isContentFromMabesOrPolda
|
||||
? [
|
||||
{
|
||||
key: "nasional",
|
||||
label: "Nasional",
|
||||
},
|
||||
{
|
||||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
{
|
||||
key: "wilayah",
|
||||
label: "Wilayah",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
...(isContentFromSatker
|
||||
? [
|
||||
{
|
||||
key: "nasional",
|
||||
label: "Nasional",
|
||||
},
|
||||
{
|
||||
key: "international",
|
||||
label: "Internasional",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
|
|
@ -1489,12 +1532,12 @@ export default function FormVideoDetail() {
|
|||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean
|
||||
value as boolean,
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value)
|
||||
Boolean(value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -1510,251 +1553,262 @@ export default function FormVideoDetail() {
|
|||
</div>
|
||||
|
||||
{/* Detail Wilayah */}
|
||||
{fileUnitSelections[index]?.wilayah && (
|
||||
<div className="border-t border-gray-200 pt-2">
|
||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||
Detail Wilayah:
|
||||
</p>
|
||||
{/* {fileUnitSelections[index]?.wilayah && ( */}
|
||||
{!isContentFromSatker &&
|
||||
fileUnitSelections[index]?.wilayah && (
|
||||
<div className="border-t border-gray-200 pt-2">
|
||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||
Detail Wilayah:
|
||||
</p>
|
||||
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
<Checkbox
|
||||
id={`${item.key}-${index}`}
|
||||
checked={
|
||||
fileUnitSelections[index]?.[
|
||||
item.key as keyof typeof unitSelection
|
||||
] || false
|
||||
}
|
||||
onCheckedChange={(value) => {
|
||||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`${item.key}-${index}`}
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
{item.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
<Checkbox
|
||||
id={`${item.key}-${index}`}
|
||||
checked={
|
||||
fileUnitSelections[index]?.[
|
||||
item.key as keyof typeof unitSelection
|
||||
] || false
|
||||
}
|
||||
onCheckedChange={(value) => {
|
||||
handleFileUnitChange(
|
||||
index,
|
||||
item.key as keyof typeof unitSelection,
|
||||
value as boolean,
|
||||
);
|
||||
setupPlacement(
|
||||
index,
|
||||
item.key,
|
||||
Boolean(value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`${item.key}-${index}`}
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
{t("custom", {
|
||||
defaultValue: "Kustom",
|
||||
})}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
Daftar Wilayah POLDA dan POLRES
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||
{listDest.map((polda: any) => (
|
||||
<div
|
||||
key={polda.id}
|
||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||
>
|
||||
{/* Header POLDA */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(polda.id)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(polda.id)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="font-semibold text-gray-900 text-sm">
|
||||
{polda.name}
|
||||
</span>
|
||||
</Label>
|
||||
{polda.subDestination && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleExpand(
|
||||
polda.id
|
||||
);
|
||||
}}
|
||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
expandedPolda[
|
||||
polda.id
|
||||
]
|
||||
? "mdi:chevron-up"
|
||||
: "mdi:chevron-down"
|
||||
}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{item.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Sub-items */}
|
||||
{polda.subDestination &&
|
||||
expandedPolda[polda.id] && (
|
||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||
{/* Tombol Pilih Semua untuk sub-items */}
|
||||
<div className="mb-2 flex justify-start">
|
||||
{(() => {
|
||||
const allSubItemsChecked =
|
||||
polda.subDestination?.every(
|
||||
(sub: any) =>
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
polda
|
||||
)
|
||||
}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-indeterminate-small"
|
||||
width={12}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Batal Semua
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-all"
|
||||
width={12}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Pilih Semua
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
{t("custom", {
|
||||
defaultValue: "Kustom",
|
||||
})}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
Daftar Wilayah POLDA dan
|
||||
POLRES
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||
{listDest.map((polda: any) => (
|
||||
<div
|
||||
key={polda.id}
|
||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||
>
|
||||
{/* Header POLDA */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(polda.id),
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(polda.id),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="font-semibold text-gray-900 text-sm">
|
||||
{polda.name}
|
||||
</span>
|
||||
</Label>
|
||||
{polda.subDestination && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleExpand(
|
||||
polda.id,
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{polda.subDestination.map(
|
||||
(sub: any) => (
|
||||
<Label
|
||||
key={sub.id}
|
||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||
>
|
||||
<Checkbox
|
||||
checked={
|
||||
}}
|
||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
expandedPolda[
|
||||
polda.id
|
||||
]
|
||||
? "mdi:chevron-up"
|
||||
: "mdi:chevron-down"
|
||||
}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Sub-items */}
|
||||
{polda.subDestination &&
|
||||
expandedPolda[
|
||||
polda.id
|
||||
] && (
|
||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||
{/* Tombol Pilih Semua untuk sub-items */}
|
||||
<div className="mb-2 flex justify-start">
|
||||
{(() => {
|
||||
const allSubItemsChecked =
|
||||
polda.subDestination?.every(
|
||||
(sub: any) =>
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
sub.id,
|
||||
),
|
||||
),
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
polda,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
)
|
||||
)}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-indeterminate-small"
|
||||
width={
|
||||
12
|
||||
}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Batal
|
||||
Semua
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon
|
||||
icon="material-symbols:check-all"
|
||||
width={
|
||||
12
|
||||
}
|
||||
height={
|
||||
12
|
||||
}
|
||||
className="mr-1"
|
||||
/>
|
||||
Pilih
|
||||
Semua
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{polda.subDestination.map(
|
||||
(sub: any) => (
|
||||
<Label
|
||||
key={sub.id}
|
||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||
>
|
||||
<Checkbox
|
||||
checked={
|
||||
fileCheckedLevels[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id,
|
||||
),
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
index,
|
||||
Number(
|
||||
sub.id,
|
||||
),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{/* {t("save", {
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{/* {t("save", {
|
||||
defaultValue: "Simpan",
|
||||
})} */}
|
||||
Simpan
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
Simpan
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -1833,7 +1887,7 @@ export default function FormVideoDetail() {
|
|||
>
|
||||
{template}
|
||||
</Button>
|
||||
)
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const CustomEditor = dynamic(
|
|||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
|
|
@ -111,11 +111,11 @@ export default function FormVideo() {
|
|||
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
||||
null
|
||||
null,
|
||||
);
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [publishedForError, setPublishedForError] = useState<string | null>(
|
||||
null
|
||||
null,
|
||||
);
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailData, setDetailData] = useState<any>(null);
|
||||
|
|
@ -184,7 +184,7 @@ export default function FormVideo() {
|
|||
}
|
||||
|
||||
const filesWithPreview = acceptedFiles.map((file) =>
|
||||
Object.assign(file, { preview: URL.createObjectURL(file) })
|
||||
Object.assign(file, { preview: URL.createObjectURL(file) }),
|
||||
);
|
||||
|
||||
setFiles((prev) => {
|
||||
|
|
@ -211,11 +211,11 @@ export default function FormVideo() {
|
|||
.refine(
|
||||
(files) =>
|
||||
files.every((file: File) => ACCEPTED_FILE_TYPES.includes(file.type)),
|
||||
{ message: "File harus berformat mp4 atau mov" }
|
||||
{ message: "File harus berformat mp4 atau mov" },
|
||||
)
|
||||
.refine(
|
||||
(files) => files.every((file: File) => file.size <= MAX_FILE_SIZE),
|
||||
{ message: "Ukuran file maksimal 100 MB" }
|
||||
{ message: "Ukuran file maksimal 100 MB" },
|
||||
),
|
||||
publishedFor: z
|
||||
.array(z.string())
|
||||
|
|
@ -423,7 +423,7 @@ export default function FormVideo() {
|
|||
const articleData = await waitForStatusUpdate();
|
||||
const cleanArticleBody = articleData?.articleBody?.replace(
|
||||
/<img[^>]*>/g,
|
||||
""
|
||||
"",
|
||||
);
|
||||
const articleImagesData = articleData?.imagesUrl?.split(",");
|
||||
setValue("description", cleanArticleBody || "");
|
||||
|
|
@ -479,7 +479,7 @@ export default function FormVideo() {
|
|||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
o.name.toLowerCase().includes("pers rilis"),
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
|
|
@ -502,7 +502,7 @@ export default function FormVideo() {
|
|||
setPublishedFor(
|
||||
options
|
||||
.filter((opt: any) => opt.id !== "all")
|
||||
.map((opt: any) => opt.id)
|
||||
.map((opt: any) => opt.id),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -561,8 +561,8 @@ export default function FormVideo() {
|
|||
translatedTitle && translatedTitle.trim() !== ""
|
||||
? translatedTitle
|
||||
: isSwitchOn
|
||||
? title
|
||||
: data.title;
|
||||
? title
|
||||
: data.title;
|
||||
|
||||
// Tentukan deskripsi final:
|
||||
// Jika ada hasil translate, kirim itu ke backend
|
||||
|
|
@ -570,10 +570,10 @@ export default function FormVideo() {
|
|||
translatedContent && translatedContent.trim() !== ""
|
||||
? translatedContent
|
||||
: isSwitchOn
|
||||
? data.description
|
||||
: selectedFileType === "rewrite"
|
||||
? data.rewriteDescription
|
||||
: data.descriptionOri;
|
||||
? data.description
|
||||
: selectedFileType === "rewrite"
|
||||
? data.rewriteDescription
|
||||
: data.descriptionOri;
|
||||
|
||||
if (!finalDescription?.trim()) {
|
||||
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
|
||||
|
|
@ -695,7 +695,7 @@ export default function FormVideo() {
|
|||
idx: number,
|
||||
id: string,
|
||||
file: any,
|
||||
duration: string
|
||||
duration: string,
|
||||
) {
|
||||
console.log(idx, id, file, duration);
|
||||
|
||||
|
|
@ -731,7 +731,7 @@ export default function FormVideo() {
|
|||
onChunkComplete: (
|
||||
chunkSize: any,
|
||||
bytesAccepted: any,
|
||||
bytesTotal: any
|
||||
bytesTotal: any,
|
||||
) => {
|
||||
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
||||
progressInfo[idx].percentage = uploadPersen;
|
||||
|
|
@ -791,8 +791,8 @@ export default function FormVideo() {
|
|||
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
setThumbnail(file);
|
||||
setPreview(URL.createObjectURL(file));
|
||||
setThumbnail(file);
|
||||
setPreview(URL.createObjectURL(file));
|
||||
console.log("Selected Thumbnail:", file);
|
||||
}
|
||||
};
|
||||
|
|
@ -823,7 +823,7 @@ export default function FormVideo() {
|
|||
<div
|
||||
key={file.name}
|
||||
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
||||
>
|
||||
>
|
||||
<div className="flex gap-3 items-center">
|
||||
{/* <div className="file-preview">{renderFilePreview(file)}</div> */}
|
||||
<svg
|
||||
|
|
@ -1732,7 +1732,7 @@ export default function FormVideo() {
|
|||
type="button"
|
||||
onClick={() => {
|
||||
const updatedTags = field.value.filter(
|
||||
(_, i) => i !== index
|
||||
(_, i) => i !== index,
|
||||
);
|
||||
field.onChange(updatedTags);
|
||||
}}
|
||||
|
|
@ -1825,7 +1825,7 @@ export default function FormVideo() {
|
|||
{/* <Button type="submit" color="primary">
|
||||
{t("submit", { defaultValue: "Submit" })}
|
||||
</Button> */}
|
||||
{levelNumber !== "2" && levelNumber !== "3" && (
|
||||
{levelNumber !== "2" && (
|
||||
<Button type="submit" color="primary">
|
||||
{t("submit", { defaultValue: "Submit" })}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ interface Detail {
|
|||
youtubeUrl: string;
|
||||
needApprovalFrom: number;
|
||||
uploadedById: number;
|
||||
statusId?: number;
|
||||
}
|
||||
|
||||
export default function FormDetailLiveReport() {
|
||||
|
|
@ -101,6 +102,11 @@ export default function FormDetailLiveReport() {
|
|||
const [status, setStatus] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [uploaderLevelNumber, setUploaderLevelNumber] = useState<number | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
const isScheduleFromSatker = uploaderLevelNumber === 3;
|
||||
|
||||
const {
|
||||
control,
|
||||
|
|
@ -133,6 +139,11 @@ export default function FormDetailLiveReport() {
|
|||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
|
||||
if (details?.uploaderLevelNumber !== undefined) {
|
||||
setUploaderLevelNumber(details.uploaderLevelNumber);
|
||||
}
|
||||
|
||||
if (details) {
|
||||
setDate({
|
||||
from: parseISO(details.startDate),
|
||||
|
|
@ -176,21 +187,71 @@ export default function FormDetailLiveReport() {
|
|||
statusId: Number(status),
|
||||
message: description,
|
||||
isPublish: status === "2",
|
||||
placements: schedulePlacements?.filter((val) => val != "all")?.join(","),
|
||||
// placements: schedulePlacements?.filter((val) => val !== "all")?.join(","),
|
||||
placements: isScheduleFromSatker
|
||||
? "satker"
|
||||
: schedulePlacements?.filter((val) => val !== "all")?.join(","),
|
||||
};
|
||||
|
||||
loading();
|
||||
const response = await postApprovalSchedule(data);
|
||||
close();
|
||||
setModalOpen(false);
|
||||
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
error(response?.message || "Gagal menyimpan data");
|
||||
return;
|
||||
}
|
||||
initState();
|
||||
return false;
|
||||
|
||||
// ✅ update UI lokal (optimistic)
|
||||
setDetail((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
statusId: Number(status),
|
||||
}
|
||||
: prev,
|
||||
);
|
||||
|
||||
Swal.fire({
|
||||
icon: "success",
|
||||
title: "Berhasil",
|
||||
text:
|
||||
status === "2"
|
||||
? "Jadwal berhasil disetujui"
|
||||
: status === "3"
|
||||
? "Jadwal dikembalikan untuk revisi"
|
||||
: "Jadwal berhasil ditolak",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/contributor/schedule/live-report");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// async function save() {
|
||||
// const data = {
|
||||
// scheduleId: Number(id),
|
||||
// statusId: Number(status),
|
||||
// message: description,
|
||||
// isPublish: status === "2",
|
||||
// placements: schedulePlacements?.filter((val) => val != "all")?.join(","),
|
||||
// };
|
||||
|
||||
// loading();
|
||||
// const response = await postApprovalSchedule(data);
|
||||
// close();
|
||||
// setModalOpen(false);
|
||||
|
||||
// if (response?.error) {
|
||||
// error(response?.message);
|
||||
// return false;
|
||||
// }
|
||||
// initState();
|
||||
// return false;
|
||||
// }
|
||||
|
||||
const [schedulePlacements, setSchedulePlacements] = useState<string[]>([]);
|
||||
|
||||
const setupPlacement = (placement: string, checked: boolean) => {
|
||||
|
|
@ -224,6 +285,15 @@ export default function FormDetailLiveReport() {
|
|||
setSchedulePlacements(temp);
|
||||
};
|
||||
|
||||
const isCreator = Number(detail?.uploadedById) === Number(userId);
|
||||
|
||||
const isApprover =
|
||||
Number(detail?.needApprovalFrom) === Number(userLevelId) &&
|
||||
Number(userLevelNumber) < 2;
|
||||
|
||||
const isAlreadyProcessed =
|
||||
detail?.statusId === 2 || detail?.statusId === 3 || detail?.statusId === 4;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col lg:flex-row gap-2">
|
||||
<Card className="w-full lg:w-9/12">
|
||||
|
|
@ -284,7 +354,7 @@ export default function FormDetailLiveReport() {
|
|||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[280px] lg:w-[250px] justify-start text-left font-normal px-0 md:px-0 lg:px-4",
|
||||
!date && "text-muted-foreground"
|
||||
!date && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
<CalendarIcon size={15} className="mr-3" />
|
||||
|
|
@ -494,7 +564,38 @@ export default function FormDetailLiveReport() {
|
|||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
||||
{Number(detail?.needApprovalFrom) == Number(userLevelId) &&
|
||||
{(isApprover || isCreator) && !isAlreadyProcessed && (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" />
|
||||
{t("accept", { defaultValue: "Accept" })}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" />
|
||||
{t("revision", { defaultValue: "Revision" })}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject", { defaultValue: "Reject" })}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* {Number(detail?.needApprovalFrom) == Number(userLevelId) &&
|
||||
Number(userLevelNumber) < 2 ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
|
|
@ -527,13 +628,15 @@ export default function FormDetailLiveReport() {
|
|||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
)} */}
|
||||
</Card>
|
||||
|
||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||
<DialogContent className="overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("leave-comment", { defaultValue: "Leave Comment" })}</DialogTitle>
|
||||
<DialogTitle>
|
||||
{t("leave-comment", { defaultValue: "Leave Comment" })}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-1 text-sm">
|
||||
<p>
|
||||
|
|
@ -544,18 +647,19 @@ export default function FormDetailLiveReport() {
|
|||
status === "2"
|
||||
? "text-primary"
|
||||
: status === "3"
|
||||
? "text-warning"
|
||||
: "text-destructive"
|
||||
? "text-warning"
|
||||
: "text-destructive"
|
||||
}
|
||||
>
|
||||
{status === "2"
|
||||
? "Disetujui"
|
||||
: status === "3"
|
||||
? "Revisi"
|
||||
: "Ditolak"}
|
||||
? "Revisi"
|
||||
: "Ditolak"}
|
||||
</span>
|
||||
</p>
|
||||
{status === "2" && (
|
||||
{/* {status === "2" && ( */}
|
||||
{status === "2" && !isScheduleFromSatker && (
|
||||
<div className="flex flex-row gap-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
|
|
@ -630,9 +734,15 @@ export default function FormDetailLiveReport() {
|
|||
type="button"
|
||||
color="primary"
|
||||
onClick={() => submit()}
|
||||
// disabled={
|
||||
// description.length < 1 ||
|
||||
// (schedulePlacements.length < 1 && status === "2")
|
||||
// }
|
||||
disabled={
|
||||
description.length < 1 ||
|
||||
(schedulePlacements.length < 1 && status === "2")
|
||||
(!isScheduleFromSatker &&
|
||||
schedulePlacements.length < 1 &&
|
||||
status === "2")
|
||||
}
|
||||
>
|
||||
{t("submit", { defaultValue: "Submit" })}
|
||||
|
|
|
|||
|
|
@ -530,7 +530,7 @@ export function TambahIklanDetail() {
|
|||
</p>
|
||||
|
||||
<Image
|
||||
src={`https://netidhub.com/api/advertisements/viewer/${id}`}
|
||||
src={`https://new.netidhub.com/api/advertisements/viewer/${id}`}
|
||||
alt="Thumbnail Gambar Utama"
|
||||
className=" rounded-md my-3"
|
||||
width={300}
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ export function TambahIklanUpdate() {
|
|||
formMedia.append("file", imageFiles[0]);
|
||||
} else if (detail?.id) {
|
||||
const existingFile = await fetchExistingImageAsFile(
|
||||
`https://netidhub.com/api/advertisements/viewer/${detail.id}`,
|
||||
`https://new.netidhub.com/api/advertisements/viewer/${detail.id}`,
|
||||
"existing-image.jpg"
|
||||
);
|
||||
formMedia.append("file", existingFile);
|
||||
|
|
@ -631,7 +631,7 @@ export function TambahIklanUpdate() {
|
|||
))}
|
||||
|
||||
<Image
|
||||
src={`https://netidhub.com/api/advertisements/viewer/${id}`}
|
||||
src={`https://new.netidhub.com/api/advertisements/viewer/${id}`}
|
||||
alt="Thumbnail Gambar Utama"
|
||||
className=" rounded-md my-3"
|
||||
width={300}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ import { getCsrfToken } from "@/service/auth";
|
|||
import { loading } from "@/lib/swal";
|
||||
import { useTranslations } from "next-intl";
|
||||
import dynamic from "next/dynamic";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
|
|
@ -98,7 +98,7 @@ const CustomEditor = dynamic(
|
|||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
export default function FormTaskTa() {
|
||||
|
|
@ -136,7 +136,7 @@ export default function FormTaskTa() {
|
|||
const [userLevels, setUserLevels] = useState<any>();
|
||||
const [userCompetencies, setUserCompetencies] = useState<any[]>([]);
|
||||
const [selectedCompetencies, setSelectedCompetencies] = useState<Set<number>>(
|
||||
new Set()
|
||||
new Set(),
|
||||
);
|
||||
const [listExpert, setListExpert] = useState<any[]>([]);
|
||||
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
|
||||
|
|
@ -181,6 +181,33 @@ export default function FormTaskTa() {
|
|||
mode: "all",
|
||||
});
|
||||
|
||||
const [profile, setProfile] = useState<any>(null);
|
||||
const userLevelId = Number(getCookiesDecrypt("ulie"));
|
||||
const roleId = Number(getCookiesDecrypt("urie"));
|
||||
const userId = Number(getCookiesDecrypt("uie"));
|
||||
|
||||
const MABES_LEVEL_ID = 216; // userLevelId Mabes Polri
|
||||
const APPROVER_ROLE_ID = 3; // roleId Approver
|
||||
const isMabes = userLevelId === MABES_LEVEL_ID;
|
||||
const isApprover = roleId === APPROVER_ROLE_ID;
|
||||
|
||||
const isMabesApprover =
|
||||
userLevelId === MABES_LEVEL_ID && roleId === APPROVER_ROLE_ID;
|
||||
|
||||
const shouldHideExpert = isMabes && isApprover;
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchUserLevel() {
|
||||
try {
|
||||
const res = await getUserLevelForAssignments();
|
||||
setProfile(res?.data?.data);
|
||||
} catch (e) {
|
||||
console.error("Failed fetch user level", e);
|
||||
}
|
||||
}
|
||||
fetchUserLevel();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getDataAdditional();
|
||||
}, []);
|
||||
|
|
@ -218,7 +245,7 @@ export default function FormTaskTa() {
|
|||
}
|
||||
|
||||
const uniqueExperts = Array.from(
|
||||
new Map(allExperts.map((e) => [e.id, e])).values()
|
||||
new Map(allExperts.map((e) => [e.id, e])).values(),
|
||||
);
|
||||
|
||||
setListExpert(uniqueExperts);
|
||||
|
|
@ -352,55 +379,124 @@ export default function FormTaskTa() {
|
|||
// // });
|
||||
// };
|
||||
|
||||
// const save = async (data: TaskSchema) => {
|
||||
// const cleanedLinks = links
|
||||
// .map((link) => link.trim())
|
||||
// .filter((link) => link.startsWith("http"));
|
||||
|
||||
// const requestData = {
|
||||
// ...data,
|
||||
// // assignedToUsers: handleExpertChange(),
|
||||
// assignedToUsers: isMabesApprover ? "464" : handleExpertChange(),
|
||||
// assignmentType: taskType,
|
||||
// assignmentTypeId: type,
|
||||
// expertCompetencies: Array.from(selectedCompetencies).join(","),
|
||||
// attachmentUrl: cleanedLinks,
|
||||
// };
|
||||
|
||||
// console.log("FINAL ASSIGNED TO:", {
|
||||
// isMabesApprover,
|
||||
// assignedToUsers: isMabesApprover
|
||||
// ? String(roleId)
|
||||
// : handleExpertChange(),
|
||||
// });
|
||||
|
||||
// const response = await createTaskTa(requestData);
|
||||
// const id = String(response?.data?.data.id);
|
||||
|
||||
// // Set block table TA
|
||||
// localStorage.setItem("TA_UPLOAD_IN_PROGRESS", "true");
|
||||
|
||||
// loading(); // SHOW SWAL LOADING
|
||||
|
||||
// // Kumpulkan semua upload
|
||||
// const allUploads: Promise<any>[] = [];
|
||||
|
||||
// imageFiles.forEach((item, idx) =>
|
||||
// allUploads.push(uploadResumableFile(idx, id, item, "1", "0"))
|
||||
// );
|
||||
|
||||
// videoFiles.forEach((item, idx) =>
|
||||
// allUploads.push(uploadResumableFile(idx, id, item, "2", "0"))
|
||||
// );
|
||||
|
||||
// textFiles.forEach((item, idx) =>
|
||||
// allUploads.push(uploadResumableFile(idx, id, item, "3", "0"))
|
||||
// );
|
||||
|
||||
// audioFiles.forEach((item, idx) =>
|
||||
// allUploads.push(uploadResumableFile(idx, id, item, "4", "0"))
|
||||
// );
|
||||
|
||||
// // Tunggu upload selesai
|
||||
// await Promise.all(allUploads);
|
||||
|
||||
// // Hapus flag
|
||||
// localStorage.removeItem("TA_UPLOAD_IN_PROGRESS");
|
||||
|
||||
// // Close loading + redirect
|
||||
// successSubmit("/in/contributor/task-ta");
|
||||
// };
|
||||
|
||||
const save = async (data: TaskSchema) => {
|
||||
const cleanedLinks = links
|
||||
.map((link) => link.trim())
|
||||
.filter((link) => link.startsWith("http"));
|
||||
try {
|
||||
loading();
|
||||
|
||||
const requestData = {
|
||||
...data,
|
||||
assignedToUsers: handleExpertChange(),
|
||||
assignmentType: taskType,
|
||||
assignmentTypeId: type,
|
||||
expertCompetencies: Array.from(selectedCompetencies).join(","),
|
||||
attachmentUrl: cleanedLinks,
|
||||
};
|
||||
const cleanedLinks = links
|
||||
.map((link) => link.trim())
|
||||
.filter((link) => link.startsWith("http"));
|
||||
|
||||
const response = await createTaskTa(requestData);
|
||||
const id = String(response?.data?.data.id);
|
||||
const requestData = {
|
||||
...data,
|
||||
// assignedToUsers: isMabesApprover ? "464" : handleExpertChange(),
|
||||
assignedToUsers: isMabesApprover ? "464,8258" : handleExpertChange(),
|
||||
assignmentType: taskType,
|
||||
assignmentTypeId: type,
|
||||
expertCompetencies: Array.from(selectedCompetencies).join(","),
|
||||
attachmentUrl: cleanedLinks,
|
||||
};
|
||||
|
||||
// Set block table TA
|
||||
localStorage.setItem("TA_UPLOAD_IN_PROGRESS", "true");
|
||||
const response = await createTaskTa(requestData);
|
||||
|
||||
loading(); // SHOW SWAL LOADING
|
||||
if (!response?.data?.data?.id) {
|
||||
throw new Error("Gagal membuat task");
|
||||
}
|
||||
|
||||
// Kumpulkan semua upload
|
||||
const allUploads: Promise<any>[] = [];
|
||||
const assignmentId = String(response.data.data.id);
|
||||
|
||||
imageFiles.forEach((item, idx) =>
|
||||
allUploads.push(uploadResumableFile(idx, id, item, "1", "0"))
|
||||
);
|
||||
const uploads: Promise<any>[] = [];
|
||||
|
||||
videoFiles.forEach((item, idx) =>
|
||||
allUploads.push(uploadResumableFile(idx, id, item, "2", "0"))
|
||||
);
|
||||
imageFiles.forEach((file, i) =>
|
||||
uploads.push(uploadResumableFile(i, assignmentId, file, "1", "0")),
|
||||
);
|
||||
|
||||
textFiles.forEach((item, idx) =>
|
||||
allUploads.push(uploadResumableFile(idx, id, item, "3", "0"))
|
||||
);
|
||||
videoFiles.forEach((file, i) =>
|
||||
uploads.push(uploadResumableFile(i, assignmentId, file, "2", "0")),
|
||||
);
|
||||
|
||||
audioFiles.forEach((item, idx) =>
|
||||
allUploads.push(uploadResumableFile(idx, id, item, "4", "0"))
|
||||
);
|
||||
textFiles.forEach((file, i) =>
|
||||
uploads.push(uploadResumableFile(i, assignmentId, file, "3", "0")),
|
||||
);
|
||||
|
||||
// Tunggu upload selesai
|
||||
await Promise.all(allUploads);
|
||||
audioFiles.forEach((file, i) =>
|
||||
uploads.push(uploadResumableFile(i, assignmentId, file, "4", "0")),
|
||||
);
|
||||
|
||||
// Hapus flag
|
||||
localStorage.removeItem("TA_UPLOAD_IN_PROGRESS");
|
||||
await Promise.all(uploads);
|
||||
|
||||
// Close loading + redirect
|
||||
successSubmit("/in/contributor/task-ta");
|
||||
successSubmit("/in/contributor/task-ta");
|
||||
} catch (err: any) {
|
||||
console.error("SUBMIT ERROR:", err);
|
||||
|
||||
Swal.fire({
|
||||
icon: "error",
|
||||
title: "Gagal",
|
||||
text:
|
||||
err?.response?.data?.message ||
|
||||
err?.message ||
|
||||
"Terjadi kesalahan, data tidak tersimpan",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = (data: TaskSchema) => {
|
||||
|
|
@ -461,7 +557,7 @@ export default function FormTaskTa() {
|
|||
// Convert Blob to File and add preview
|
||||
const fileWithPreview: FileWithPreview = Object.assign(
|
||||
new File([blob], "voiceNote.webm", { type: "audio/webm" }),
|
||||
{ preview: url }
|
||||
{ preview: url },
|
||||
);
|
||||
|
||||
// Add to state
|
||||
|
|
@ -543,46 +639,91 @@ export default function FormTaskTa() {
|
|||
|
||||
// upload.start();
|
||||
// }
|
||||
// function uploadResumableFile(
|
||||
// idx: number,
|
||||
// id: string,
|
||||
// file: any,
|
||||
// fileTypeId: string,
|
||||
// duration: string
|
||||
// ) {
|
||||
// return new Promise(async (resolve, reject) => {
|
||||
// const resCsrf = await getCsrfToken();
|
||||
// const csrfToken = resCsrf?.data?.token;
|
||||
|
||||
// const upload = new Upload(file, {
|
||||
// endpoint: `${process.env.NEXT_PUBLIC_API}/assignment-expert/file/upload`,
|
||||
// headers: { "X-XSRF-TOKEN": csrfToken },
|
||||
// retryDelays: [0, 3000, 6000, 12000],
|
||||
// chunkSize: 20000,
|
||||
// metadata: {
|
||||
// assignmentId: id,
|
||||
// filename: file.name,
|
||||
// contentType: file.type,
|
||||
// fileTypeId,
|
||||
// duration,
|
||||
// },
|
||||
|
||||
// onBeforeRequest(req) {
|
||||
// req.getUnderlyingObject().withCredentials = true;
|
||||
// },
|
||||
|
||||
// onError(err) {
|
||||
// console.error("Upload error:", err);
|
||||
// reject(err);
|
||||
// },
|
||||
|
||||
// onSuccess() {
|
||||
// console.log("Upload selesai:", file.name);
|
||||
// resolve(true);
|
||||
// },
|
||||
// });
|
||||
|
||||
// upload.start();
|
||||
// });
|
||||
// }
|
||||
|
||||
function uploadResumableFile(
|
||||
idx: number,
|
||||
id: string,
|
||||
file: any,
|
||||
file: File,
|
||||
fileTypeId: string,
|
||||
duration: string
|
||||
duration: string,
|
||||
) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
try {
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
|
||||
const upload = new Upload(file, {
|
||||
endpoint: `${process.env.NEXT_PUBLIC_API}/assignment-expert/file/upload`,
|
||||
headers: { "X-XSRF-TOKEN": csrfToken },
|
||||
retryDelays: [0, 3000, 6000, 12000],
|
||||
chunkSize: 20000,
|
||||
metadata: {
|
||||
assignmentId: id,
|
||||
filename: file.name,
|
||||
contentType: file.type,
|
||||
fileTypeId,
|
||||
duration,
|
||||
},
|
||||
const upload = new Upload(file, {
|
||||
endpoint: `${process.env.NEXT_PUBLIC_API}/assignment-expert/file/upload`,
|
||||
headers: { "X-XSRF-TOKEN": csrfToken },
|
||||
retryDelays: [0, 3000, 6000],
|
||||
chunkSize: 20000,
|
||||
metadata: {
|
||||
assignmentId: id,
|
||||
filename: file.name,
|
||||
contentType: file.type,
|
||||
fileTypeId,
|
||||
duration,
|
||||
},
|
||||
|
||||
onBeforeRequest(req) {
|
||||
req.getUnderlyingObject().withCredentials = true;
|
||||
},
|
||||
onBeforeRequest(req) {
|
||||
req.getUnderlyingObject().withCredentials = true;
|
||||
},
|
||||
|
||||
onError(err) {
|
||||
console.error("Upload error:", err);
|
||||
reject(err);
|
||||
},
|
||||
onError(error) {
|
||||
reject(error);
|
||||
},
|
||||
|
||||
onSuccess() {
|
||||
console.log("Upload selesai:", file.name);
|
||||
resolve(true);
|
||||
},
|
||||
});
|
||||
onSuccess() {
|
||||
resolve(true);
|
||||
},
|
||||
});
|
||||
|
||||
upload.start();
|
||||
upload.start();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -671,12 +812,29 @@ export default function FormTaskTa() {
|
|||
value={taskType}
|
||||
onValueChange={(value) => setTaskType(String(value))}
|
||||
className="flex flex-wrap gap-3"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="atensi-khusus" id="khusus" />
|
||||
<Label htmlFor="khusus">Atensi Khusus</Label>
|
||||
</div>
|
||||
|
||||
{!isMabes && (
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="tugas-harian" id="harian" />
|
||||
<Label htmlFor="harian">Tugas Harian</Label>
|
||||
</div>
|
||||
)}
|
||||
</RadioGroup>
|
||||
{/* <RadioGroup
|
||||
value={taskType}
|
||||
onValueChange={(value) => setTaskType(String(value))}
|
||||
className="flex flex-wrap gap-3"
|
||||
>
|
||||
<RadioGroupItem value="atensi-khusus" id="khusus" />
|
||||
<Label htmlFor="atensi-khusus">Atensi Khusus</Label>
|
||||
<RadioGroupItem value="tugas-harian" id="harian" />
|
||||
<Label htmlFor="tugas-harian">Tugas Harian</Label>
|
||||
</RadioGroup>
|
||||
</RadioGroup> */}
|
||||
</div>
|
||||
<div className="flex flex-col space-y-2 mt-5">
|
||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||
|
|
@ -688,7 +846,7 @@ export default function FormTaskTa() {
|
|||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[280px] lg:w-[250px] justify-start text-left font-normal border border-slate-300 px-0 md:px-0 lg:px-4",
|
||||
!date && "text-muted-foreground"
|
||||
!date && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
<CalendarIcon size={15} className="mr-3" />
|
||||
|
|
@ -718,101 +876,109 @@ export default function FormTaskTa() {
|
|||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>
|
||||
{t("areas-expertise", { defaultValue: "Areas Expertise" })}
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{userCompetencies?.map((item: any) => (
|
||||
<div className="flex items-center gap-2" key={item.id}>
|
||||
<Checkbox
|
||||
id={`comp-${item.id}`}
|
||||
checked={selectedCompetencies.has(item.id)}
|
||||
onCheckedChange={() => handleCompetencyChange(item.id)}
|
||||
/>
|
||||
<Label htmlFor={`comp-${item.id}`}>{item.name}</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>
|
||||
{t("choose-expert", { defaultValue: "Choose Expert" })}
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="soft" size="sm" color="primary">
|
||||
[{"Pilih Tenaga Ahli"}]
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Daftar Tenaga Ahli</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||
{listExpert?.map((expert: any) => (
|
||||
<div key={expert.id} className="border p-2">
|
||||
<Label className="flex items-center">
|
||||
<Checkbox
|
||||
checked={checkedLevels.has(expert.id)}
|
||||
onCheckedChange={() =>
|
||||
handleCheckboxChange(expert.id)
|
||||
}
|
||||
className="mr-3"
|
||||
/>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="font-bold">{expert.fullname}</div>
|
||||
<div className="italic">({expert.username})</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
{!isMabesApprover && (
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>
|
||||
{t("areas-expertise", { defaultValue: "Areas Expertise" })}
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{userCompetencies?.map((item: any) => (
|
||||
<div className="flex items-center gap-2" key={item.id}>
|
||||
<Checkbox
|
||||
id={`comp-${item.id}`}
|
||||
checked={selectedCompetencies.has(item.id)}
|
||||
onCheckedChange={() => handleCompetencyChange(item.id)}
|
||||
/>
|
||||
<Label htmlFor={`comp-${item.id}`}>{item.name}</Label>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
{checkedLevels.size > 0 && (
|
||||
<div className="mt-3">
|
||||
<Label className="text-sm text-gray-600 mb-2 block">
|
||||
Tenaga Ahli Terpilih ({checkedLevels.size})
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Array.from(checkedLevels).map((expertId) => {
|
||||
const expert = listExpert?.find(
|
||||
(exp: any) => exp.id === expertId
|
||||
);
|
||||
return expert ? (
|
||||
<div
|
||||
key={expert.id}
|
||||
className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1.5 rounded-full border border-blue-200"
|
||||
>
|
||||
<span>{expert.fullname}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCheckboxChange(expert.id)}
|
||||
className="ml-1 text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-0.5 transition-colors"
|
||||
title="Remove expert"
|
||||
>
|
||||
<svg
|
||||
className="w-3 h-3"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isMabesApprover && (
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>
|
||||
{t("choose-expert", { defaultValue: "Choose Expert" })}
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="soft" size="sm" color="primary">
|
||||
[{"Pilih Tenaga Ahli"}]
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Daftar Tenaga Ahli</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||
{listExpert?.map((expert: any) => (
|
||||
<div key={expert.id} className="border p-2">
|
||||
<Label className="flex items-center">
|
||||
<Checkbox
|
||||
checked={checkedLevels.has(expert.id)}
|
||||
onCheckedChange={() =>
|
||||
handleCheckboxChange(expert.id)
|
||||
}
|
||||
className="mr-3"
|
||||
/>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="font-bold">
|
||||
{expert.fullname}
|
||||
</div>
|
||||
<div className="italic">
|
||||
({expert.username})
|
||||
</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
{checkedLevels.size > 0 && (
|
||||
<div className="mt-3">
|
||||
<Label className="text-sm text-gray-600 mb-2 block">
|
||||
Tenaga Ahli Terpilih ({checkedLevels.size})
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Array.from(checkedLevels).map((expertId) => {
|
||||
const expert = listExpert?.find(
|
||||
(exp: any) => exp.id === expertId,
|
||||
);
|
||||
return expert ? (
|
||||
<div
|
||||
key={expert.id}
|
||||
className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1.5 rounded-full border border-blue-200"
|
||||
>
|
||||
<span>{expert.fullname}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCheckboxChange(expert.id)}
|
||||
className="ml-1 text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-0.5 transition-colors"
|
||||
title="Remove expert"
|
||||
>
|
||||
<svg
|
||||
className="w-3 h-3"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("description", { defaultValue: "Description" })}</Label>
|
||||
<Controller
|
||||
|
|
@ -946,7 +1112,9 @@ export default function FormTaskTa() {
|
|||
<Input
|
||||
type="url"
|
||||
className="border rounded p-2 w-full"
|
||||
placeholder={`Masukkan link berita ${index + 1}`}
|
||||
placeholder={`Masukkan link berita ${
|
||||
index + 1
|
||||
} | Contoh: https://www.detik.com`}
|
||||
value={link}
|
||||
onChange={(e) =>
|
||||
handleLinkChange(index, e.target.value)
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@
|
|||
// };
|
||||
|
||||
// export default AdvertisementPlacements;
|
||||
import { listDataAdvertisements } from "@/service/broadcast/broadcast";
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import * as React from "react";
|
||||
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
||||
|
|
@ -93,35 +94,40 @@ interface Advertisement {
|
|||
[key: string]: any;
|
||||
}
|
||||
|
||||
const AdvertisementPlacements = (props: {
|
||||
interface AdvertisementPlacementsProps {
|
||||
placement: string;
|
||||
data: Advertisement[];
|
||||
data?: Advertisement[]; // ⬅️ PENTING: optional
|
||||
loading: boolean;
|
||||
}) => {
|
||||
const [ads, setAds] = useState<Advertisement[] | undefined[]>([]);
|
||||
}
|
||||
|
||||
const AdvertisementPlacements = ({
|
||||
placement,
|
||||
data = [], // ⬅️ DEFAULT VALUE (KUNCI UTAMA)
|
||||
loading,
|
||||
}: AdvertisementPlacementsProps) => {
|
||||
const [ads, setAds] = useState<(Advertisement | undefined)[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.loading && props.data.length > 0) {
|
||||
const filtered = props.data.filter((a) =>
|
||||
a.placements.includes(props.placement)
|
||||
);
|
||||
if (loading || data.length === 0) return;
|
||||
|
||||
let temps: Advertisement[] | undefined[] = [];
|
||||
temps[0] = filtered.find((b) => b.placements.includes("top"));
|
||||
temps[1] = filtered.find((b) => b.placements.includes("bottom"));
|
||||
setAds(temps);
|
||||
}
|
||||
}, [props.data, props.loading, props.placement]);
|
||||
const filtered = data.filter((a) => a.placements?.includes(placement));
|
||||
|
||||
const temps: (Advertisement | undefined)[] = [];
|
||||
temps[0] = filtered.find((b) => b.placements?.includes("top"));
|
||||
temps[1] = filtered.find((b) => b.placements?.includes("bottom"));
|
||||
|
||||
setAds(temps);
|
||||
}, [data, loading, placement]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`sticky top-0 space-y-4 ${
|
||||
props.placement === "left" ? "ml-14" : "mr-14"
|
||||
placement === "left" ? "ml-14" : "mr-14"
|
||||
}`}
|
||||
>
|
||||
{props.loading && <p className="text-sm text-gray-500">Loading...</p>}
|
||||
{loading && <p className="text-sm text-gray-500">Loading...</p>}
|
||||
|
||||
{ads?.map(
|
||||
{ads.map(
|
||||
(ad) =>
|
||||
ad && (
|
||||
<Dialog key={ad.id}>
|
||||
|
|
@ -132,6 +138,7 @@ const AdvertisementPlacements = (props: {
|
|||
className="w-full cursor-pointer rounded-lg shadow-md hover:opacity-90 transition"
|
||||
/>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent className="max-w-4xl p-0 bg-transparent border-0 shadow-none">
|
||||
<img
|
||||
src={ad.contentFileUrl || ad.imageUrl}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ type HeroModalProps = {
|
|||
|
||||
// useEffect(() => {
|
||||
// async function fetchCategories() {
|
||||
// const url = "https://netidhub.com/api/csrf";
|
||||
// const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
// try {
|
||||
// const response = await fetch(url);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ const HeroModal = ({ onClose }: { onClose: () => void }) => {
|
|||
|
||||
useEffect(() => {
|
||||
async function fetchCategories() {
|
||||
const url = "https://netidhub.com/api/csrf";
|
||||
const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
|
@ -268,7 +268,7 @@ const Hero = (props: { group?: string }) => {
|
|||
|
||||
useEffect(() => {
|
||||
async function fetchCategories() {
|
||||
const url = "https://netidhub.com/api/csrf";
|
||||
const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ const NewContent = (props: { group: string; type: string }) => {
|
|||
? "1"
|
||||
: selectedTab == "video"
|
||||
? "2"
|
||||
: selectedTab == "text"
|
||||
: selectedTab == "document"
|
||||
? "3"
|
||||
: selectedTab == "audio"
|
||||
? "4"
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const ScrollableContentPolda = () => {
|
|||
|
||||
useEffect(() => {
|
||||
async function fetchCategories() {
|
||||
const url = "https://netidhub.com/api/csrf";
|
||||
const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const ScrollableContentSatker = () => {
|
|||
: "";
|
||||
useEffect(() => {
|
||||
async function fetchCategories() {
|
||||
const url = "https://netidhub.com/api/csrf";
|
||||
const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const ScrollableContent = () => {
|
|||
|
||||
useEffect(() => {
|
||||
async function fetchCategories() {
|
||||
const url = "https://netidhub.com/api/csrf";
|
||||
const url = "https://new.netidhub.com/api/csrf";
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ const DashCodeFooter = () => {
|
|||
>
|
||||
<div className="h-[50px] w-[50px] rounded-full relative left-[0px] top-[0px] custom-dropshadow">
|
||||
<Image
|
||||
src={"https://netidhub.com/assets/img/user-avatar.png"}
|
||||
src={"https://new.netidhub.com/assets/img/user-avatar.png"}
|
||||
alt={"Image"}
|
||||
width={50}
|
||||
height={50}
|
||||
|
|
|
|||
218
lib/menus.ts
218
lib/menus.ts
|
|
@ -168,6 +168,13 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
icon: "heroicons:shopping-cart",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
href: "/contributor/task-ta",
|
||||
label: "penugasan TA",
|
||||
active: pathname.includes("/contributor/task-ta"),
|
||||
icon: "heroicons:shopping-cart",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
@ -2422,7 +2429,10 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
},
|
||||
];
|
||||
}
|
||||
} else if (Number(roleId) == 4 && Number(levelNumber) == 3) {
|
||||
} else if (
|
||||
Number(roleId) == 4 ||
|
||||
(Number(roleId) == 3 && Number(levelNumber) == 3)
|
||||
) {
|
||||
menusSelected = [
|
||||
{
|
||||
groupLabel: t("apps"),
|
||||
|
|
@ -2509,35 +2519,35 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "planning",
|
||||
menus: [
|
||||
{
|
||||
id: "planning",
|
||||
href: "/contributor/planning",
|
||||
label: t("planning"),
|
||||
active: pathname.includes("/planning"),
|
||||
icon: "pajamas:planning",
|
||||
submenus: [
|
||||
{
|
||||
href: "/contributor/planning/mediahub",
|
||||
label: "mediaHub",
|
||||
active: pathname.includes("/planning/mediahub"),
|
||||
icon: "heroicons:arrow-trending-up",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
href: "/contributor/planning/medsos-mediahub",
|
||||
label: "medsos mediahub",
|
||||
active: pathname.includes("/planning/medsos-mediahub"),
|
||||
icon: "heroicons:shopping-cart",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// groupLabel: "",
|
||||
// id: "planning",
|
||||
// menus: [
|
||||
// {
|
||||
// id: "planning",
|
||||
// href: "/contributor/planning",
|
||||
// label: t("planning"),
|
||||
// active: pathname.includes("/planning"),
|
||||
// icon: "pajamas:planning",
|
||||
// submenus: [
|
||||
// {
|
||||
// href: "/contributor/planning/mediahub",
|
||||
// label: "mediaHub",
|
||||
// active: pathname.includes("/planning/mediahub"),
|
||||
// icon: "heroicons:arrow-trending-up",
|
||||
// children: [],
|
||||
// },
|
||||
// {
|
||||
// href: "/contributor/planning/medsos-mediahub",
|
||||
// label: "medsos mediahub",
|
||||
// active: pathname.includes("/planning/medsos-mediahub"),
|
||||
// icon: "heroicons:shopping-cart",
|
||||
// children: [],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "task",
|
||||
|
|
@ -2564,44 +2574,51 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
icon: "uil:schedule",
|
||||
submenus: [
|
||||
{
|
||||
href: "/contributor/schedule/press-conference",
|
||||
label: t("press-conference"),
|
||||
active: pathname.includes("/schedule/press-conference"),
|
||||
href: "/contributor/schedule/live-report",
|
||||
label: t("live-report"),
|
||||
active: pathname.includes("/schedule/live-report"),
|
||||
icon: "heroicons:arrow-trending-up",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
href: "/contributor/schedule/event",
|
||||
label: t("event"),
|
||||
active: pathname.includes("/schedule/event"),
|
||||
icon: "heroicons:shopping-cart",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
href: "/contributor/schedule/press-release",
|
||||
label: t("press-release"),
|
||||
active: pathname.includes("/schedule/press-release"),
|
||||
icon: "heroicons:shopping-cart",
|
||||
children: [],
|
||||
},
|
||||
// {
|
||||
// href: "/contributor/schedule/press-conference",
|
||||
// label: t("press-conference"),
|
||||
// active: pathname.includes("/schedule/press-conference"),
|
||||
// icon: "heroicons:arrow-trending-up",
|
||||
// children: [],
|
||||
// },
|
||||
// {
|
||||
// href: "/contributor/schedule/event",
|
||||
// label: t("event"),
|
||||
// active: pathname.includes("/schedule/event"),
|
||||
// icon: "heroicons:shopping-cart",
|
||||
// children: [],
|
||||
// },
|
||||
// {
|
||||
// href: "/contributor/schedule/press-release",
|
||||
// label: t("press-release"),
|
||||
// active: pathname.includes("/schedule/press-release"),
|
||||
// icon: "heroicons:shopping-cart",
|
||||
// children: [],
|
||||
// },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "blog",
|
||||
menus: [
|
||||
{
|
||||
id: "blog",
|
||||
href: "/contributor/blog",
|
||||
label: t("blog"),
|
||||
active: pathname.includes("/blog"),
|
||||
icon: "fluent:clipboard-text-32-regular",
|
||||
submenus: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// groupLabel: "",
|
||||
// id: "blog",
|
||||
// menus: [
|
||||
// {
|
||||
// id: "blog",
|
||||
// href: "/contributor/blog",
|
||||
// label: t("blog"),
|
||||
// active: pathname.includes("/blog"),
|
||||
// icon: "fluent:clipboard-text-32-regular",
|
||||
// submenus: [],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "curatedcontent",
|
||||
|
|
@ -2649,8 +2666,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
(Number(roleId) == 3 || Number(roleId) == 14 || Number(roleId) == 15) &&
|
||||
Number(levelNumber) == 3
|
||||
) {
|
||||
if (Number(userParentLevelId) != 761)
|
||||
{
|
||||
if (Number(userParentLevelId) != 761) {
|
||||
menusSelected = [
|
||||
{
|
||||
groupLabel: t("apps"),
|
||||
|
|
@ -5209,7 +5225,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-area",
|
||||
label: t("areaCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-area"
|
||||
"/charts/appex-charts/charts-appex-area",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5217,7 +5233,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-bar",
|
||||
label: t("barCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-bar"
|
||||
"/charts/appex-charts/charts-appex-bar",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5225,7 +5241,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-boxplot",
|
||||
label: t("boxplotCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-boxplot"
|
||||
"/charts/appex-charts/charts-appex-boxplot",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5233,7 +5249,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-bubble",
|
||||
label: t("bubbleCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-bubble"
|
||||
"/charts/appex-charts/charts-appex-bubble",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5241,7 +5257,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-candlestick",
|
||||
label: t("candlestickCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-candlestick"
|
||||
"/charts/appex-charts/charts-appex-candlestick",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5249,7 +5265,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-column",
|
||||
label: t("columnCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-column"
|
||||
"/charts/appex-charts/charts-appex-column",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5257,7 +5273,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-combo",
|
||||
label: t("comboCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-combo"
|
||||
"/charts/appex-charts/charts-appex-combo",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5266,7 +5282,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-funnel",
|
||||
label: t("funnelCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-funnel"
|
||||
"/charts/appex-charts/charts-appex-funnel",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5274,7 +5290,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-heatmap",
|
||||
label: t("heatmapCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-heatmap"
|
||||
"/charts/appex-charts/charts-appex-heatmap",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5282,7 +5298,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-line",
|
||||
label: t("lineCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-line"
|
||||
"/charts/appex-charts/charts-appex-line",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5290,7 +5306,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-pie",
|
||||
label: t("pieCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-pie"
|
||||
"/charts/appex-charts/charts-appex-pie",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5298,7 +5314,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-polararea",
|
||||
label: t("ploarareaCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-polararea"
|
||||
"/charts/appex-charts/charts-appex-polararea",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5306,7 +5322,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-radar",
|
||||
label: t("radarCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-radar"
|
||||
"/charts/appex-charts/charts-appex-radar",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5314,7 +5330,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-radialbars",
|
||||
label: t("radialbarCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-radialbars"
|
||||
"/charts/appex-charts/charts-appex-radialbars",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5322,7 +5338,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-range",
|
||||
label: t("rangeCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-range"
|
||||
"/charts/appex-charts/charts-appex-range",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5330,7 +5346,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-scatter",
|
||||
label: t("scatterCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-scatter"
|
||||
"/charts/appex-charts/charts-appex-scatter",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5338,7 +5354,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-timeline",
|
||||
label: t("timelineCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-timeline"
|
||||
"/charts/appex-charts/charts-appex-timeline",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5346,7 +5362,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/appex-charts/charts-appex-treemap",
|
||||
label: t("treemapCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/appex-charts/charts-appex-treemap"
|
||||
"/charts/appex-charts/charts-appex-treemap",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5362,7 +5378,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/rechart/charts-rechart-area",
|
||||
label: t("areaCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/rechart/charts-rechart-area"
|
||||
"/charts/rechart/charts-rechart-area",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5370,7 +5386,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/rechart/charts-rechart-bar",
|
||||
label: t("barCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/rechart/charts-rechart-bar"
|
||||
"/charts/rechart/charts-rechart-bar",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5378,7 +5394,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/rechart/charts-rechart-composed",
|
||||
label: t("composedCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/rechart/charts-rechart-composed"
|
||||
"/charts/rechart/charts-rechart-composed",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5386,7 +5402,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/rechart/charts-rechart-line",
|
||||
label: t("lineCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/rechart/charts-rechart-line"
|
||||
"/charts/rechart/charts-rechart-line",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5394,7 +5410,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/rechart/charts-rechart-pie",
|
||||
label: t("pieCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/rechart/charts-rechart-pie"
|
||||
"/charts/rechart/charts-rechart-pie",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5402,7 +5418,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/rechart/charts-rechart-radar",
|
||||
label: t("radarCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/rechart/charts-rechart-radar"
|
||||
"/charts/rechart/charts-rechart-radar",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5410,7 +5426,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/rechart/charts-rechart-radialbar",
|
||||
label: t("radialbarCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/rechart/charts-rechart-radialbar"
|
||||
"/charts/rechart/charts-rechart-radialbar",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5418,7 +5434,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/rechart/charts-rechart-scatter",
|
||||
label: t("scatterCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/rechart/charts-rechart-scatter"
|
||||
"/charts/rechart/charts-rechart-scatter",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5426,7 +5442,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/rechart/charts-rechart-treemap",
|
||||
label: t("treemapCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/rechart/charts-rechart-treemap"
|
||||
"/charts/rechart/charts-rechart-treemap",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5442,7 +5458,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-area",
|
||||
label: t("areaCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-area"
|
||||
"/charts/chart-js/charts-chartjs-area",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5450,7 +5466,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-bar",
|
||||
label: t("barCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-bar"
|
||||
"/charts/chart-js/charts-chartjs-bar",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5458,7 +5474,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-line",
|
||||
label: t("lineCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-line"
|
||||
"/charts/chart-js/charts-chartjs-line",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5466,7 +5482,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-animations",
|
||||
label: t("animationCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-animations"
|
||||
"/charts/chart-js/charts-chartjs-animations",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5474,7 +5490,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-legend",
|
||||
label: t("legendCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-legend"
|
||||
"/charts/chart-js/charts-chartjs-legend",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5482,7 +5498,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-scaleoptions",
|
||||
label: t("scaleOptionCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-scaleoptions"
|
||||
"/charts/chart-js/charts-chartjs-scaleoptions",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5490,7 +5506,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-scales",
|
||||
label: t("scaleCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-scales"
|
||||
"/charts/chart-js/charts-chartjs-scales",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5498,7 +5514,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-scriptable",
|
||||
label: t("scriptableCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-scriptable"
|
||||
"/charts/chart-js/charts-chartjs-scriptable",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5506,7 +5522,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-title",
|
||||
label: t("titleCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-title"
|
||||
"/charts/chart-js/charts-chartjs-title",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5514,7 +5530,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-tooltip",
|
||||
label: t("tooltipChart"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-tooltip"
|
||||
"/charts/chart-js/charts-chartjs-tooltip",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -5522,7 +5538,7 @@ export function getHorizontalMenuList(pathname: string, t: any): Group[] {
|
|||
href: "/charts/chart-js/charts-chartjs-other",
|
||||
label: t("otherCharts"),
|
||||
active: pathname.includes(
|
||||
"/charts/chart-js/charts-chartjs-other"
|
||||
"/charts/chart-js/charts-chartjs-other",
|
||||
),
|
||||
children: [],
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -20,6 +20,35 @@ const nextConfig = {
|
|||
// locales: ["en", "in"],
|
||||
// defaultLocale: "in",
|
||||
// },
|
||||
// images: {
|
||||
// remotePatterns: [
|
||||
// {
|
||||
// protocol: "https",
|
||||
// hostname: "api.lorem.space",
|
||||
// },
|
||||
// {
|
||||
// protocol: "https",
|
||||
// hostname: "lh3.googleusercontent.com",
|
||||
// },
|
||||
// {
|
||||
// protocol: "https",
|
||||
// hostname: "a0.muscache.com",
|
||||
// },
|
||||
// {
|
||||
// protocol: "https",
|
||||
// hostname: "avatars.githubusercontent.com",
|
||||
// },
|
||||
// {
|
||||
// protocol: "https",
|
||||
// hostname: "i.pravatar.cc",
|
||||
// },
|
||||
// { protocol: "https", hostname: "netidhub.com" },
|
||||
// {
|
||||
// protocol: "https",
|
||||
// hostname: "netidhub.com",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
|
|
@ -42,11 +71,15 @@ const nextConfig = {
|
|||
protocol: "https",
|
||||
hostname: "i.pravatar.cc",
|
||||
},
|
||||
{ protocol: "https", hostname: "netidhub.com" },
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "netidhub.com",
|
||||
},
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "new.netidhub.com",
|
||||
pathname: "/**",
|
||||
},
|
||||
],
|
||||
},
|
||||
// eslint: {
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@
|
|||
"dayjs": "^1.11.11",
|
||||
"embla-carousel-autoplay": "^8.1.3",
|
||||
"embla-carousel-react": "^8.1.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"framer-motion": "^11.15.0",
|
||||
"geojson": "^0.5.0",
|
||||
"html-react-parser": "^5.2.0",
|
||||
|
|
@ -137,6 +138,7 @@
|
|||
"uuid": "^13.0.0",
|
||||
"vaul": "^0.9.1",
|
||||
"wavesurfer.js": "^7.8.16",
|
||||
"xlsx": "^0.18.5",
|
||||
"yup": "^1.6.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
|
@ -156,14 +158,15 @@
|
|||
"@types/react": "^18.3.13",
|
||||
"@types/react-geocode": "^0.2.4",
|
||||
"@types/rtl-detect": "^1.0.3",
|
||||
"autoprefixer": "^10.4.24",
|
||||
"cross-env": "^7.0.3",
|
||||
"d3-shape": "^3.2.0",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.3",
|
||||
"jest": "^30.0.4",
|
||||
"jest-environment-jsdom": "^30.0.4",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"typescript": "^5"
|
||||
}
|
||||
},
|
||||
|
|
@ -23691,6 +23694,15 @@
|
|||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/adler-32": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
|
||||
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||
|
|
@ -24146,6 +24158,43 @@
|
|||
"npm": ">=6.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.24",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz",
|
||||
"integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.28.1",
|
||||
"caniuse-lite": "^1.0.30001766",
|
||||
"fraction.js": "^5.3.4",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"autoprefixer": "bin/autoprefixer"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
|
|
@ -24338,6 +24387,18 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
|
||||
"integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"baseline-browser-mapping": "dist/cli.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/big.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
|
|
@ -24421,9 +24482,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.25.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
||||
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
|
||||
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -24438,11 +24499,13 @@
|
|||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001726",
|
||||
"electron-to-chromium": "^1.5.173",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.3"
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
"electron-to-chromium": "^1.5.263",
|
||||
"node-releases": "^2.0.27",
|
||||
"update-browserslist-db": "^1.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
|
|
@ -24696,9 +24759,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001727",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
|
||||
"integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
|
||||
"version": "1.0.30001770",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz",
|
||||
"integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -24712,7 +24775,8 @@
|
|||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
]
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/canvg": {
|
||||
"version": "3.0.11",
|
||||
|
|
@ -24742,6 +24806,19 @@
|
|||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/cfb": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
|
||||
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"crc-32": "~1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
|
|
@ -25214,6 +25291,15 @@
|
|||
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",
|
||||
"integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w=="
|
||||
},
|
||||
"node_modules/codepage": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
||||
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/collect-v8-coverage": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
|
||||
|
|
@ -25417,6 +25503,18 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/crc-32": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"crc32": "bin/crc32.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-env": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
||||
|
|
@ -26677,9 +26775,10 @@
|
|||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.187",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz",
|
||||
"integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA=="
|
||||
"version": "1.5.286",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
|
||||
"integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/elkjs": {
|
||||
"version": "0.9.3",
|
||||
|
|
@ -27871,6 +27970,12 @@
|
|||
"node": "^10.12.0 || >=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-saver": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
|
||||
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/file-selector": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz",
|
||||
|
|
@ -28101,6 +28206,29 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/frac": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
|
||||
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
|
||||
"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "11.18.2",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
|
||||
|
|
@ -34197,9 +34325,10 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="
|
||||
"version": "2.0.27",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/non-layered-tidy-tree-layout": {
|
||||
"version": "2.0.2",
|
||||
|
|
@ -34972,6 +35101,7 @@
|
|||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
|
|
@ -38523,6 +38653,18 @@
|
|||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
||||
},
|
||||
"node_modules/ssf": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
|
||||
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"frac": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ssri": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
|
||||
|
|
@ -39231,9 +39373,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.17",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
||||
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
||||
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"arg": "^5.0.2",
|
||||
|
|
@ -39243,7 +39386,7 @@
|
|||
"fast-glob": "^3.3.2",
|
||||
"glob-parent": "^6.0.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"jiti": "^1.21.6",
|
||||
"jiti": "^1.21.7",
|
||||
"lilconfig": "^3.1.3",
|
||||
"micromatch": "^4.0.8",
|
||||
"normalize-path": "^3.0.0",
|
||||
|
|
@ -39252,7 +39395,7 @@
|
|||
"postcss": "^8.4.47",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-js": "^4.0.1",
|
||||
"postcss-load-config": "^4.0.2",
|
||||
"postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
|
||||
"postcss-nested": "^6.2.0",
|
||||
"postcss-selector-parser": "^6.1.2",
|
||||
"resolve": "^1.22.8",
|
||||
|
|
@ -40319,9 +40462,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
||||
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -40336,6 +40479,7 @@
|
|||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"escalade": "^3.2.0",
|
||||
"picocolors": "^1.1.1"
|
||||
|
|
@ -41270,6 +41414,24 @@
|
|||
"integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/wmf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
|
||||
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/word": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
|
||||
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
|
|
@ -41405,6 +41567,27 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xlsx": {
|
||||
"version": "0.18.5",
|
||||
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
|
||||
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"cfb": "~1.2.1",
|
||||
"codepage": "~1.15.0",
|
||||
"crc-32": "~1.2.1",
|
||||
"ssf": "~0.11.2",
|
||||
"wmf": "~1.0.1",
|
||||
"word": "~0.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"xlsx": "bin/xlsx.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-name-validator": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@
|
|||
"dayjs": "^1.11.11",
|
||||
"embla-carousel-autoplay": "^8.1.3",
|
||||
"embla-carousel-react": "^8.1.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"framer-motion": "^11.15.0",
|
||||
"geojson": "^0.5.0",
|
||||
"html-react-parser": "^5.2.0",
|
||||
|
|
@ -146,6 +147,7 @@
|
|||
"uuid": "^13.0.0",
|
||||
"vaul": "^0.9.1",
|
||||
"wavesurfer.js": "^7.8.16",
|
||||
"xlsx": "^0.18.5",
|
||||
"yup": "^1.6.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
|
@ -165,14 +167,15 @@
|
|||
"@types/react": "^18.3.13",
|
||||
"@types/react-geocode": "^0.2.4",
|
||||
"@types/rtl-detect": "^1.0.3",
|
||||
"autoprefixer": "^10.4.24",
|
||||
"cross-env": "^7.0.3",
|
||||
"d3-shape": "^3.2.0",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.3",
|
||||
"jest": "^30.0.4",
|
||||
"jest-environment-jsdom": "^30.0.4",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
118
pnpm-lock.yaml
118
pnpm-lock.yaml
|
|
@ -10,7 +10,7 @@ importers:
|
|||
dependencies:
|
||||
'@ckeditor/ckeditor5-react':
|
||||
specifier: ^6.2.0
|
||||
version: 6.3.0(@ckeditor/ckeditor5-core@41.3.1)(@ckeditor/ckeditor5-editor-multi-root@47.1.0)(@ckeditor/ckeditor5-engine@41.3.1)(@ckeditor/ckeditor5-utils@41.3.1)(@ckeditor/ckeditor5-watchdog@41.3.1)(react@18.3.1)
|
||||
version: 6.3.0(@ckeditor/ckeditor5-core@47.1.0)(@ckeditor/ckeditor5-editor-multi-root@47.1.0)(@ckeditor/ckeditor5-engine@47.1.0)(@ckeditor/ckeditor5-utils@47.1.0)(@ckeditor/ckeditor5-watchdog@47.1.0)(react@18.3.1)
|
||||
'@dnd-kit/core':
|
||||
specifier: ^6.1.0
|
||||
version: 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
|
@ -224,6 +224,9 @@ importers:
|
|||
embla-carousel-react:
|
||||
specifier: ^8.1.3
|
||||
version: 8.6.0(react@18.3.1)
|
||||
file-saver:
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5
|
||||
framer-motion:
|
||||
specifier: ^11.15.0
|
||||
version: 11.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
|
@ -395,6 +398,9 @@ importers:
|
|||
wavesurfer.js:
|
||||
specifier: ^7.8.16
|
||||
version: 7.11.0
|
||||
xlsx:
|
||||
specifier: ^0.18.5
|
||||
version: 0.18.5
|
||||
yup:
|
||||
specifier: ^1.6.1
|
||||
version: 1.7.1
|
||||
|
|
@ -3033,6 +3039,10 @@ packages:
|
|||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
adler-32@1.3.1:
|
||||
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
agent-base@7.1.4:
|
||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
|
@ -3310,6 +3320,10 @@ packages:
|
|||
ccount@2.0.1:
|
||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||
|
||||
cfb@1.2.2:
|
||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
chalk@2.3.0:
|
||||
resolution: {integrity: sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -3421,6 +3435,10 @@ packages:
|
|||
code-block-writer@12.0.0:
|
||||
resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==}
|
||||
|
||||
codepage@1.15.0:
|
||||
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
collect-v8-coverage@1.0.3:
|
||||
resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==}
|
||||
|
||||
|
|
@ -3542,6 +3560,11 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
|
||||
crc-32@1.2.2:
|
||||
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
|
||||
engines: {node: '>=0.8'}
|
||||
hasBin: true
|
||||
|
||||
cross-env@7.0.3:
|
||||
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||
|
|
@ -4262,6 +4285,9 @@ packages:
|
|||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
|
||||
file-saver@2.0.5:
|
||||
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
||||
|
||||
file-selector@2.1.2:
|
||||
resolution: {integrity: sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==}
|
||||
engines: {node: '>= 12'}
|
||||
|
|
@ -4331,6 +4357,10 @@ packages:
|
|||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
frac@1.1.2:
|
||||
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
framer-motion@11.18.2:
|
||||
resolution: {integrity: sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==}
|
||||
peerDependencies:
|
||||
|
|
@ -6887,6 +6917,10 @@ packages:
|
|||
sprintf-js@1.0.3:
|
||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||
|
||||
ssf@0.11.2:
|
||||
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
stable-hash@0.0.5:
|
||||
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
|
||||
|
||||
|
|
@ -7554,10 +7588,18 @@ packages:
|
|||
engines: {node: '>= 8'}
|
||||
hasBin: true
|
||||
|
||||
wmf@1.0.2:
|
||||
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
word-wrap@1.2.5:
|
||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
word@0.3.0:
|
||||
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -7601,6 +7643,11 @@ packages:
|
|||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
xlsx@0.18.5:
|
||||
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
|
||||
engines: {node: '>=0.8'}
|
||||
hasBin: true
|
||||
|
||||
xml-name-validator@5.0.0:
|
||||
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -7940,8 +7987,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-core': 47.1.0
|
||||
'@ckeditor/ckeditor5-upload': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-alignment@41.3.1':
|
||||
dependencies:
|
||||
|
|
@ -8007,8 +8052,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-ui': 47.1.0
|
||||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-bookmark@47.1.0':
|
||||
dependencies:
|
||||
|
|
@ -8076,8 +8119,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-core': 47.1.0
|
||||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-code-block@41.3.1':
|
||||
dependencies:
|
||||
|
|
@ -8144,8 +8185,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-decoupled@47.1.0':
|
||||
dependencies:
|
||||
|
|
@ -8155,8 +8194,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-inline@47.1.0':
|
||||
dependencies:
|
||||
|
|
@ -8166,8 +8203,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-multi-root@47.1.0':
|
||||
dependencies:
|
||||
|
|
@ -8467,8 +8502,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
'@ckeditor/ckeditor5-widget': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-mention@47.1.0':
|
||||
dependencies:
|
||||
|
|
@ -8527,13 +8560,13 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-engine': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
|
||||
'@ckeditor/ckeditor5-react@6.3.0(@ckeditor/ckeditor5-core@41.3.1)(@ckeditor/ckeditor5-editor-multi-root@47.1.0)(@ckeditor/ckeditor5-engine@41.3.1)(@ckeditor/ckeditor5-utils@41.3.1)(@ckeditor/ckeditor5-watchdog@41.3.1)(react@18.3.1)':
|
||||
'@ckeditor/ckeditor5-react@6.3.0(@ckeditor/ckeditor5-core@47.1.0)(@ckeditor/ckeditor5-editor-multi-root@47.1.0)(@ckeditor/ckeditor5-engine@47.1.0)(@ckeditor/ckeditor5-utils@47.1.0)(@ckeditor/ckeditor5-watchdog@47.1.0)(react@18.3.1)':
|
||||
dependencies:
|
||||
'@ckeditor/ckeditor5-core': 41.3.1
|
||||
'@ckeditor/ckeditor5-core': 47.1.0
|
||||
'@ckeditor/ckeditor5-editor-multi-root': 47.1.0
|
||||
'@ckeditor/ckeditor5-engine': 41.3.1
|
||||
'@ckeditor/ckeditor5-utils': 41.3.1
|
||||
'@ckeditor/ckeditor5-watchdog': 41.3.1
|
||||
'@ckeditor/ckeditor5-engine': 47.1.0
|
||||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
'@ckeditor/ckeditor5-watchdog': 47.1.0
|
||||
prop-types: 15.8.1
|
||||
react: 18.3.1
|
||||
|
||||
|
|
@ -8544,8 +8577,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-ui': 47.1.0
|
||||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-restricted-editing@47.1.0':
|
||||
dependencies:
|
||||
|
|
@ -8593,8 +8624,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-ui': 47.1.0
|
||||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-special-characters@47.1.0':
|
||||
dependencies:
|
||||
|
|
@ -8761,8 +8790,6 @@ snapshots:
|
|||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
ckeditor5: 47.1.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@csstools/color-helpers@5.1.0': {}
|
||||
|
||||
|
|
@ -10900,6 +10927,8 @@ snapshots:
|
|||
|
||||
acorn@8.15.0: {}
|
||||
|
||||
adler-32@1.3.1: {}
|
||||
|
||||
agent-base@7.1.4: {}
|
||||
|
||||
ajv@6.12.6:
|
||||
|
|
@ -11240,6 +11269,11 @@ snapshots:
|
|||
|
||||
ccount@2.0.1: {}
|
||||
|
||||
cfb@1.2.2:
|
||||
dependencies:
|
||||
adler-32: 1.3.1
|
||||
crc-32: 1.2.2
|
||||
|
||||
chalk@2.3.0:
|
||||
dependencies:
|
||||
ansi-styles: 3.2.1
|
||||
|
|
@ -11443,6 +11477,8 @@ snapshots:
|
|||
|
||||
code-block-writer@12.0.0: {}
|
||||
|
||||
codepage@1.15.0: {}
|
||||
|
||||
collect-v8-coverage@1.0.3: {}
|
||||
|
||||
color-convert@1.9.3:
|
||||
|
|
@ -11551,6 +11587,8 @@ snapshots:
|
|||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
crc-32@1.2.2: {}
|
||||
|
||||
cross-env@7.0.3:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
|
|
@ -12094,7 +12132,7 @@ snapshots:
|
|||
eslint: 8.57.1
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
|
||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
|
||||
eslint-plugin-react: 7.37.5(eslint@8.57.1)
|
||||
eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1)
|
||||
|
|
@ -12124,7 +12162,7 @@ snapshots:
|
|||
tinyglobby: 0.2.15
|
||||
unrs-resolver: 1.11.1
|
||||
optionalDependencies:
|
||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
|
||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -12139,7 +12177,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1):
|
||||
eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
|
||||
dependencies:
|
||||
'@rtsao/scc': 1.1.0
|
||||
array-includes: 3.1.9
|
||||
|
|
@ -12460,6 +12498,8 @@ snapshots:
|
|||
dependencies:
|
||||
flat-cache: 3.2.0
|
||||
|
||||
file-saver@2.0.5: {}
|
||||
|
||||
file-selector@2.1.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
|
@ -12530,6 +12570,8 @@ snapshots:
|
|||
|
||||
forwarded@0.2.0: {}
|
||||
|
||||
frac@1.1.2: {}
|
||||
|
||||
framer-motion@11.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
motion-dom: 11.18.1
|
||||
|
|
@ -16050,6 +16092,10 @@ snapshots:
|
|||
|
||||
sprintf-js@1.0.3: {}
|
||||
|
||||
ssf@0.11.2:
|
||||
dependencies:
|
||||
frac: 1.1.2
|
||||
|
||||
stable-hash@0.0.5: {}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
|
|
@ -16867,8 +16913,12 @@ snapshots:
|
|||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
wmf@1.0.2: {}
|
||||
|
||||
word-wrap@1.2.5: {}
|
||||
|
||||
word@0.3.0: {}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
|
|
@ -16898,6 +16948,16 @@ snapshots:
|
|||
|
||||
ws@8.18.3: {}
|
||||
|
||||
xlsx@0.18.5:
|
||||
dependencies:
|
||||
adler-32: 1.3.1
|
||||
cfb: 1.2.2
|
||||
codepage: 1.15.0
|
||||
crc-32: 1.2.2
|
||||
ssf: 0.11.2
|
||||
wmf: 1.0.2
|
||||
word: 0.3.0
|
||||
|
||||
xml-name-validator@5.0.0: {}
|
||||
|
||||
xmlchars@2.2.0: {}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export async function getCsrfToken() {
|
|||
"content-type": "application/json",
|
||||
};
|
||||
return httpGet(pathUrl, headers);
|
||||
// const url = 'https://netidhub.com/api/csrf';
|
||||
// const url = 'https://new.netidhub.com/api/csrf';
|
||||
// try {
|
||||
// const response = await fetch(url, {
|
||||
// method: 'GET',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import axios from "axios";
|
||||
|
||||
const baseURL = "https://netidhub.com/api/";
|
||||
const baseURL = "https://new.netidhub.com/api/";
|
||||
|
||||
const axiosBaseInstance = axios.create({
|
||||
baseURL,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import axios from "axios";
|
|||
import Cookies from "js-cookie";
|
||||
import { getCsrfToken, login } from "../auth";
|
||||
|
||||
const baseURL = "https://netidhub.com/api/";
|
||||
const baseURL = "https://new.netidhub.com/api/";
|
||||
|
||||
const refreshToken = Cookies.get("refresh_token");
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export async function getCsrfToken() {
|
|||
"content-type": "application/json",
|
||||
};
|
||||
return httpGet(pathUrl, headers);
|
||||
// const url = 'https://netidhub.com/api/csrf';
|
||||
// const url = 'https://new.netidhub.com/api/csrf';
|
||||
// try {
|
||||
// const response = await fetch(url, {
|
||||
// method: 'GET',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import api from "@/src/lib/api";
|
||||
import {
|
||||
httpGetInterceptor,
|
||||
httpPostInterceptor,
|
||||
httpPutInterceptor,
|
||||
} from "../http-config/http-interceptor-service";
|
||||
|
||||
export async function getMediaTrackingMonitoring(page: number, size: number) {
|
||||
|
|
@ -67,3 +69,36 @@ export async function listDataAllNonPagination(search: string) {
|
|||
`media/public/list?enablePage=0&sort=desc&title=${search || ""}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function validateMediaLink(resultId: number, isRelevant: boolean) {
|
||||
const url = "media/tracking/monitoring/results/relevant";
|
||||
|
||||
const payload = {
|
||||
resultId,
|
||||
isRelevant,
|
||||
};
|
||||
|
||||
return httpPutInterceptor(url, payload);
|
||||
}
|
||||
|
||||
// export const validateMediaLink = async (
|
||||
// resultId: number,
|
||||
// isRelevant: boolean
|
||||
// ) => {
|
||||
// try {
|
||||
// const res = await api.put(
|
||||
// "/media/tracking/monitoring/results/relevant",
|
||||
// {
|
||||
// resultId,
|
||||
// isRelevant,
|
||||
// }
|
||||
// );
|
||||
|
||||
// return res.data;
|
||||
// } catch (error: any) {
|
||||
// throw new Error(
|
||||
// error?.response?.data?.messages?.[0] ||
|
||||
// "Gagal memperbarui status relevansi"
|
||||
// );
|
||||
// }
|
||||
// };
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import axios from "axios";
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: "https://new.netidhub.com/api",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
export default api;
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// src/types/react-table.d.ts
|
||||
import "@tanstack/react-table";
|
||||
|
||||
declare module "@tanstack/react-table" {
|
||||
interface TableMeta<TData> {
|
||||
updateData: (rowIndex: number, value: Partial<TData>) => void;
|
||||
refetchData?: () => void;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
declare module "file-saver" {
|
||||
export function saveAs(
|
||||
data: Blob | File | string,
|
||||
filename?: string,
|
||||
options?: any
|
||||
): void;
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import * as XLSX from "xlsx";
|
||||
import { getMediaTrackingResult } from "@/service/media-tracking/media-tracking";
|
||||
import { saveAs } from "file-saver";
|
||||
|
||||
type ExportParams = {
|
||||
mediaTrackingId: number;
|
||||
};
|
||||
|
||||
export async function exportMediaTrackingToExcel({
|
||||
mediaTrackingId,
|
||||
}: ExportParams) {
|
||||
let page = 0;
|
||||
let totalPages = 1;
|
||||
const allData: any[] = [];
|
||||
|
||||
while (page < totalPages) {
|
||||
const res = await getMediaTrackingResult({
|
||||
id: mediaTrackingId,
|
||||
page,
|
||||
});
|
||||
|
||||
const data = res?.data?.data;
|
||||
if (!data) break;
|
||||
|
||||
totalPages = data.totalPages;
|
||||
allData.push(...data.content);
|
||||
|
||||
page++;
|
||||
}
|
||||
|
||||
if (allData.length === 0) {
|
||||
throw new Error("Tidak ada data untuk di-export");
|
||||
}
|
||||
|
||||
function extractDomain(url?: string): string {
|
||||
if (!url) return "-";
|
||||
try {
|
||||
return new URL(url).hostname;
|
||||
} catch {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
|
||||
const rows = allData.map((item, index) => ({
|
||||
No: index + 1,
|
||||
"Media Online": extractDomain(item.link),
|
||||
"Judul Berita": item.title ?? "-",
|
||||
"Link Berita": item.link ?? "-",
|
||||
Validasi: item.validationStatus ?? "-",
|
||||
View: item.viewCount ?? 0,
|
||||
Share: item.shareCount ?? 0,
|
||||
Komentar: item.commentCount ?? 0,
|
||||
Tanggal: item.createdAt
|
||||
? new Date(item.createdAt).toLocaleString("id-ID")
|
||||
: "-",
|
||||
}));
|
||||
|
||||
const worksheet = XLSX.utils.json_to_sheet(rows);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Media Tracking");
|
||||
|
||||
const excelBuffer = XLSX.write(workbook, {
|
||||
bookType: "xlsx",
|
||||
type: "array",
|
||||
});
|
||||
|
||||
const blob = new Blob([excelBuffer], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
});
|
||||
|
||||
saveAs(blob, `media-tracking-${mediaTrackingId}.xlsx`);
|
||||
}
|
||||
Loading…
Reference in New Issue