pull main

This commit is contained in:
Sabda Yagra 2025-09-12 14:48:11 +07:00
commit 6688345b64
7 changed files with 1208 additions and 473 deletions

View File

@ -0,0 +1,64 @@
"use server";
import { getListContent } from "@/service/landing/landing";
import { NextResponse } from "next/server";
export async function GET() {
try {
const baseUrl = "https://mediahub.polri.go.id/in";
const response = await getListContent({
page: 1,
limit: "100",
search: "",
});
const articles = response?.data?.data || [];
const urls = articles
.map((article: any) => {
const type =
article.fileTypeId == 1
? "image"
: article.fileTypeId == 2
? "video"
: article.fileTypeId == 3
? "document"
: "audio";
const slug = article.slug
? encodeURIComponent(article.slug)
: article.id;
const lastmod = article.updatedAt
? new Date(article.updatedAt).toISOString()
: new Date().toISOString();
return `
<url>
<loc>${baseUrl}/${type}/detail/${article.id}-${slug}</loc>
<lastmod>${lastmod}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>`;
})
.join("\n");
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>${baseUrl}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
${urls}
</urlset>`;
return new NextResponse(sitemap, {
headers: {
"Content-Type": "application/xml",
},
});
} catch (error: any) {
console.error("Sitemap error:", error);
return new NextResponse("Sitemap generation failed", { status: 500 });
}
}

View File

@ -1,40 +0,0 @@
"use server";
import { getListContent } from "@/service/landing/landing";
import { NextResponse } from "next/server";
export async function GET() {
const baseUrl = "https://mediahub.polri.go.id/in";
const response = await getListContent({ page: 1, limit: "100", search: "" });
const articles = response?.data?.data || [];
const urls = articles
.map(
(article: any) => `
<url>
<loc>${baseUrl}/${article.fileTypeId == 1 ? "image" : article.fileTypeId == 2 ? "video" : article.fileTypeId == 3 ? "document" : "audio"}/detail/${article.id}-${article.slug}</loc>
<lastmod>${new Date(article.updatedAt).toISOString()}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>`
)
.join("");
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>${baseUrl}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
${urls}
</urlset>`;
return new NextResponse(sitemap, {
headers: {
"Content-Type": "application/xml",
},
});
}

View File

@ -762,19 +762,26 @@ export default function FormAudioUpdate() {
const getPlacement = () => {
const temp = [];
for (let i = 0; i < filePlacements?.length; i++) {
if (filePlacements[i]?.length !== 0) {
const now = filePlacements[i];
let nowArr = now?.join(",")?.replaceAll("nasional", "mabes");
nowArr = nowArr?.replaceAll("wilayah", "polda");
nowArr = nowArr?.replaceAll("semua", "all");
const file = files[i] as any;
if (file.id && filePlacements[file.id] && filePlacements[file.id].length > 0) {
const now = filePlacements[file.id];
const normalizedNow = now.map((item): PlacementType => {
const value = String(item);
if (value === "nasional") return "mabes";
if (value === "wilayah") return "polda";
if (value === "semua") return "all";
return item as PlacementType;
});
const uniqueNow = Array.from(new Set(normalizedNow));
const nowArr = uniqueNow.join(",");
// Dapatkan checked levels untuk file ini
const currentFileCheckedLevels = fileCheckedLevels[i]
? Array.from(fileCheckedLevels[i])
const currentFileCheckedLevels = fileCheckedLevels[file.id]
? Array.from(fileCheckedLevels[file.id])
: [];
const data = {
mediaFileId: files[i]?.id,
mediaFileId: file.id,
placements: nowArr,
customLocationPlacements: currentFileCheckedLevels.join(","),
};
@ -859,12 +866,20 @@ export default function FormAudioUpdate() {
}
temp[index] = now;
} else {
// Handle mapping dari UI key ke backend value
let placementToAdd = placement;
if (placement === "nasional") {
placementToAdd = "mabes";
} else if (placement === "semua") {
placementToAdd = "all";
}
const now = temp[index] || [];
if (!now.includes(placement)) {
now.push(placement);
if (!now.includes(placementToAdd)) {
now.push(placementToAdd);
}
// Auto-checklist "all" jika nasional, wilayah, dan international ter-checklist
const requiredItems = ["nasional", "wilayah", "international"];
const requiredItems = ["mabes", "wilayah", "international"];
const hasAllRequired = requiredItems.every(item => now.includes(item));
if (hasAllRequired && !now.includes("all")) {
now.push("all");
@ -921,14 +936,22 @@ export default function FormAudioUpdate() {
const now = temp[index]?.filter((a) => a !== "satker");
temp[index] = now;
} else {
const now = temp[index]?.filter((a) => a !== placement);
// Handle mapping dari UI key ke backend value
let placementToRemove = placement;
if (placement === "nasional") {
placementToRemove = "mabes";
} else if (placement === "semua") {
placementToRemove = "all";
}
const now = temp[index]?.filter((a) => a !== placementToRemove);
temp[index] = now;
}
// Hapus "all" jika tidak semua item ter-checklist
const currentNow = temp[index] || [];
if (currentNow.includes("all")) {
const requiredItems = ["nasional", "wilayah", "international"];
const requiredItems = ["mabes", "wilayah", "international"];
const hasAllRequired = requiredItems.every(item => currentNow.includes(item));
if (!hasAllRequired) {
const newData = currentNow.filter((b) => b !== "all");
@ -1108,9 +1131,11 @@ export default function FormAudioUpdate() {
}
if (placements.includes("polda")) {
selection.polda = true;
selection.wilayah = true; // Auto-check wilayah when polda is present
}
if (placements.includes("satker")) {
selection.satker = true;
selection.wilayah = true; // Auto-check wilayah when satker is present
}
if (placements.includes("international")) {
selection.international = true;
@ -1608,56 +1633,352 @@ export default function FormAudioUpdate() {
</Label>
<div className="grid gap-4">
{files.map((file: any, index: any) => (
<div
key={file.id} // Gunakan ID file sebagai key
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
<div className="flex gap-3 items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewBox="0 0 20 20"
<div
key={file.id}
className="flex items-center border p-2 rounded-md"
>
<path
fill="currentColor"
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
/>
</svg>{" "}
<div>
<div className="text-sm text-card-foreground">
{file.fileName || file.name}
<div className="flex flex-wrap gap-3 items-center ">
<div className="flex-grow">
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
width="80"
height="80"
viewBox="0 0 20 20"
>
<path
fill="currentColor"
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
/>
</svg>{" "}
<p className="font-medium">{file.fileName}</p>
</div>
<a
href={file.url}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 text-sm"
>
{t("view-file", {
defaultValue: "View File",
})}
</a>
</div>
<div className="text-xs font-light text-muted-foreground">
{Math.round(file.size / 100) / 10 > 1000 ? (
<>
{(
Math.round(file.size / 100) / 10000
).toFixed(1)}
<div className="bg-white rounded-md p-4 border">
{/* Checkbox Tingkat Utama */}
<div className="space-y-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ key: "semua", label: "Semua" },
{
key: "nasional",
label: "Nasional",
},
{ key: "wilayah", label: "Wilayah" },
{
key: "international",
label: "Internasional",
},
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
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"
>
{item.label}
</Label>
</div>
))}
</div>
{/* Detail Wilayah */}
{fileUnitSelections[index]?.wilayah && isDetailOfRegionShowed && (
<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: "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"
>
{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"
>
<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 SATKER
</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>
{/* Tombol expand hanya untuk SATKER POLRI */}
{polda.name === "SATKER POLRI" && 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>
{/* Sub-items hanya untuk SATKER POLRI */}
{polda.name === "SATKER POLRI" && 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
</>
) : (
<>
{(Math.round(file.size / 100) / 10).toFixed(
1
)}
<Icon
icon="material-symbols:check-all"
width={
12
}
height={
12
}
className="mr-1"
/>
Pilih
Semua
</>
)}
{" kb"}
</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>
)}
<Button
size="icon"
color="destructive"
variant="outline"
className="border-none rounded-full"
onClick={() => handleDeleteFile(file.id)} // Kirim ID spesifik
>
<Icon icon="tabler:x" className="h-5 w-5" />
</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>
</div>
))}
))}
</div>
</div>
)}

View File

@ -489,8 +489,8 @@ export default function FormImageUpdate() {
});
}
} else {
// Update salah satu saja
currentSelection[key] = value;
// Update salah satu saja
currentSelection[key] = value;
}
// Cek apakah semua selain "semua" sudah dicentang
@ -889,9 +889,11 @@ export default function FormImageUpdate() {
}
if (placements.includes("polda")) {
selection.polda = true;
selection.wilayah = true; // Auto-check wilayah when polda is present
}
if (placements.includes("satker")) {
selection.satker = true;
selection.wilayah = true; // Auto-check wilayah when satker is present
}
if (placements.includes("international")) {
selection.international = true;
@ -937,19 +939,26 @@ export default function FormImageUpdate() {
const getPlacement = () => {
const temp = [];
for (let i = 0; i < filePlacements?.length; i++) {
if (filePlacements[i]?.length !== 0) {
const now = filePlacements[i];
let nowArr = now?.join(",")?.replaceAll("nasional", "mabes");
nowArr = nowArr?.replaceAll("wilayah", "polda");
nowArr = nowArr?.replaceAll("semua", "all");
const file = files[i] as any;
if (file.id && filePlacements[file.id] && filePlacements[file.id].length > 0) {
const now = filePlacements[file.id];
const normalizedNow = now.map((item): PlacementType => {
const value = String(item);
if (value === "nasional") return "mabes";
if (value === "wilayah") return "polda";
if (value === "semua") return "all";
return item as PlacementType;
});
const uniqueNow = Array.from(new Set(normalizedNow));
const nowArr = uniqueNow.join(",");
// Dapatkan checked levels untuk file ini
const currentFileCheckedLevels = fileCheckedLevels[i]
? Array.from(fileCheckedLevels[i])
const currentFileCheckedLevels = fileCheckedLevels[file.id]
? Array.from(fileCheckedLevels[file.id])
: [];
const data = {
mediaFileId: files[i]?.id,
mediaFileId: file.id,
placements: nowArr,
customLocationPlacements: currentFileCheckedLevels.join(","),
};
@ -1548,6 +1557,25 @@ export default function FormImageUpdate() {
const now = temp[index]?.filter((a) => a !== placement);
console.log("now", now);
temp[index] = now;
if (placement === "wilayah") {
// Ketika wilayah di-uncheck, hapus wilayah, polda, dan satker
const now = temp[index]?.filter((a) =>
a !== "wilayah" && a !== "polda" && a !== "satker"
);
temp[index] = now;
} else if (placement === "polda") {
// Ketika polda di-uncheck, hapus polda dari filePlacements
const now = temp[index]?.filter((a) => a !== "polda");
temp[index] = now;
} else if (placement === "satker") {
// Ketika satker di-uncheck, hapus satker dari filePlacements
const now = temp[index]?.filter((a) => a !== "satker");
temp[index] = now;
} else {
const now = temp[index]?.filter((a) => a !== placement);
temp[index] = now;
}
// Hapus "all" jika tidak semua item ter-checklist
if (now.includes("all")) {
const nonSatkerItems = now.filter(

View File

@ -804,25 +804,31 @@ export default function FormTeksUpdate() {
};
const getPlacement = () => {
const temp = [];
for (let i = 0; i < filePlacements?.length; i++) {
if (filePlacements[i]?.length !== 0) {
const now = filePlacements[i];
let nowArr = now?.join(",")?.replaceAll("nasional", "mabes");
nowArr = nowArr?.replaceAll("wilayah", "polda");
nowArr = nowArr?.replaceAll("semua", "all");
const temp: Array<{ mediaFileId: number | string; placements: string; customLocationPlacements: string }> = [];
for (let i = 0; i < files.length; i++) {
const file = files[i] as any;
const now = filePlacements[i];
if (file?.id && Array.isArray(now) && now.length > 0) {
const normalizedNow = now.map((item): PlacementType => {
const value = String(item);
if (value === "nasional") return "mabes";
if (value === "wilayah") return "polda";
if (value === "semua") return "all";
return item as PlacementType;
});
const uniqueNow = Array.from(new Set(normalizedNow));
const nowArr = uniqueNow.join(",");
// Dapatkan checked levels untuk file ini
// Dapatkan checked levels untuk file ini (berbasis index)
const currentFileCheckedLevels = fileCheckedLevels[i]
? Array.from(fileCheckedLevels[i])
: [];
const data = {
mediaFileId: files[i]?.id,
temp.push({
mediaFileId: file.id,
placements: nowArr,
customLocationPlacements: currentFileCheckedLevels.join(","),
};
temp.push(data);
});
}
}
return temp;
@ -903,12 +909,20 @@ export default function FormTeksUpdate() {
}
temp[index] = now;
} else {
// Handle mapping dari UI key ke backend value
let placementToAdd = placement;
if (placement === "nasional") {
placementToAdd = "mabes";
} else if (placement === "semua") {
placementToAdd = "all";
}
const now = temp[index] || [];
if (!now.includes(placement)) {
now.push(placement);
if (!now.includes(placementToAdd)) {
now.push(placementToAdd);
}
// Auto-checklist "all" jika nasional, wilayah, dan international ter-checklist
const requiredItems = ["nasional", "wilayah", "international"];
const requiredItems = ["mabes", "wilayah", "international"];
const hasAllRequired = requiredItems.every(item => now.includes(item));
if (hasAllRequired && !now.includes("all")) {
now.push("all");
@ -965,14 +979,22 @@ export default function FormTeksUpdate() {
const now = temp[index]?.filter((a) => a !== "satker");
temp[index] = now;
} else {
const now = temp[index]?.filter((a) => a !== placement);
// Handle mapping dari UI key ke backend value
let placementToRemove = placement;
if (placement === "nasional") {
placementToRemove = "mabes";
} else if (placement === "semua") {
placementToRemove = "all";
}
const now = temp[index]?.filter((a) => a !== placementToRemove);
temp[index] = now;
}
// Hapus "all" jika tidak semua item ter-checklist
const currentNow = temp[index] || [];
if (currentNow.includes("all")) {
const requiredItems = ["nasional", "wilayah", "international"];
const requiredItems = ["mabes", "wilayah", "international"];
const hasAllRequired = requiredItems.every(item => currentNow.includes(item));
if (!hasAllRequired) {
const newData = currentNow.filter((b) => b !== "all");
@ -1084,11 +1106,10 @@ export default function FormTeksUpdate() {
}));
setFiles(formattedFiles);
// Inisialisasi filePlacements dari detail
// Inisialisasi filePlacements dari detail (biarkan format backend, normalisasi dilakukan saat submit)
const initialFilePlacements: string[][] = details.files.map((file: any) => {
if (file.placements) {
const placements = file.placements.split(",").map((p: string) => p.trim());
return placements;
return file.placements.split(",").map((p: string) => p.trim());
}
return [];
});
@ -1122,8 +1143,7 @@ export default function FormTeksUpdate() {
if (file.placements) {
const placements = file.placements.split(",").map((p: string) => p.trim());
// Map dari format backend ke checkbox
if (placements.includes("all")) {
selection.semua = true;
selection.nasional = true;
@ -1132,23 +1152,21 @@ export default function FormTeksUpdate() {
selection.polda = true;
selection.satker = true;
} else {
if (placements.includes("mabes")) {
selection.nasional = true;
}
if (placements.includes("wilayah")) {
if (placements.includes("mabes")) selection.nasional = true;
if (placements.includes("international")) selection.international = true;
if (placements.includes("polda")) selection.polda = true;
if (placements.includes("satker")) selection.satker = true;
// Wilayah aktif jika ada "wilayah" ATAU ada polda/satker
if (
placements.includes("wilayah") ||
placements.includes("polda") ||
placements.includes("satker")
) {
selection.wilayah = true;
}
if (placements.includes("polda")) {
selection.polda = true;
}
if (placements.includes("satker")) {
selection.satker = true;
}
if (placements.includes("international")) {
selection.international = true;
}
}
}
return selection;
});
setFileUnitSelections(initialFileUnitSelections);
@ -1240,6 +1258,16 @@ export default function FormTeksUpdate() {
setIsStartUpload(true);
setProgressList(progressInfoArr);
// Update file placements terlebih dahulu (sebelum upload)
const placementData = getPlacement();
if (placementData.length > 0) {
const responseFilePlacements = await updateFilePlacements(placementData);
if (responseFilePlacements?.error) {
error(responseFilePlacements?.message);
return false;
}
}
close();
// showProgress();
files.map(async (item: any, index: number) => {
@ -1251,12 +1279,6 @@ export default function FormTeksUpdate() {
);
});
// Update file placements
const placementData = getPlacement();
if (placementData.length > 0) {
await updateFilePlacements(placementData);
}
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
@ -1594,13 +1616,13 @@ export default function FormTeksUpdate() {
</Fragment>
) : null}
{files.length > 0 && (
<div className="mt-4 space-y-2">
<Label className="text-lg font-semibold">
<div className="mt-4">
<Label className="text-md font-semibold">
{" "}
{t("file-media", { defaultValue: "File Media" })}
</Label>
<div className="grid gap-4">
{files.map((file: any) => (
{files.map((file: any, index: any) => (
<div
key={file.id}
className="flex items-center border p-2 rounded-md"
@ -1624,193 +1646,314 @@ export default function FormTeksUpdate() {
})}
</a>
</div>
<div>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={fileUnitSelections[files.indexOf(file)]?.semua || false}
onChange={(e) =>
handleFileUnitChange(
files.indexOf(file),
"semua",
e.target.checked
)
}
className="form-checkbox"
/>
<span>
{t("all", { defaultValue: "All" })}
</span>
<div className="bg-white rounded-md p-4 border">
{/* Checkbox Tingkat Utama */}
<div className="space-y-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ key: "semua", label: "Semua" },
{
key: "nasional",
label: "Nasional",
},
{ key: "wilayah", label: "Wilayah" },
{
key: "international",
label: "Internasional",
},
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
// id={`${item.key}-${index}`}
checked={
fileUnitSelections[files.indexOf(file)]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
files.indexOf(file),
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
files.indexOf(file),
item.key as keyof typeof unitSelection,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={fileUnitSelections[files.indexOf(file)]?.nasional || false}
onChange={(e) =>
handleFileUnitChange(
files.indexOf(file),
"nasional",
e.target.checked
)
}
className="form-checkbox"
/>
<span>Nasional</span>
))}
</div>
{/* Detail Wilayah */}
{fileUnitSelections[files.indexOf(file)]?.wilayah && isDetailOfRegionShowed && (
<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: "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[files.indexOf(file)]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
files.indexOf(file),
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
files.indexOf(file),
item.key as keyof typeof unitSelection,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={fileUnitSelections[files.indexOf(file)]?.wilayah || false}
onChange={(e) =>
handleFileUnitChange(
files.indexOf(file),
"wilayah",
e.target.checked
)
}
className="form-checkbox"
/>
<span>Wilayah</span>
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={fileUnitSelections[files.indexOf(file)]?.international || false}
onChange={(e) =>
handleFileUnitChange(
files.indexOf(file),
"international",
e.target.checked
)
}
className="form-checkbox"
/>
<span>Internasional</span>
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={fileUnitSelections[files.indexOf(file)]?.polda || false}
onChange={(e) =>
handleFileUnitChange(
files.indexOf(file),
"polda",
e.target.checked
)
}
className="form-checkbox"
/>
<span>POLDA</span>
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={fileUnitSelections[files.indexOf(file)]?.satker || false}
onChange={(e) =>
handleFileUnitChange(
files.indexOf(file),
"satker",
e.target.checked
)
}
className="form-checkbox"
/>
<span>SATKER</span>
</Label>
</div>
<div>
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" size="sm">
Detail Wilayah
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Daftar Wilayah</DialogTitle>
</DialogHeader>
<div className="max-h-96 overflow-y-auto">
{isLoading ? (
<div className="flex justify-center items-center h-32">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
</div>
) : (
<div className="space-y-2">
{listDest.map((polda: any) => (
<div key={polda.id} className="space-y-1">
<div className="flex items-center space-x-2">
<input
type="checkbox"
checked={fileCheckedLevels[files.indexOf(file)]?.has(Number(polda.id)) || false}
onChange={(e) =>
handleFileCheckboxChangePlacement(
files.indexOf(file),
Number(polda.id)
)
}
className="form-checkbox"
))}
{/* 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}
/>
<span className="font-medium">
{polda.name}
</span>
{polda.name === "SATKER POLRI" && polda.subDestination && (
<Button
variant="outline"
size="sm"
onClick={() => toggleExpand(Number(polda.id))}
>
{expandedPolda[Number(polda.id)] ? "Tutup" : "Buka"}
</Button>
{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 SATKER
</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[
files.indexOf(file)
]?.has(
Number(
polda.id
)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
files.indexOf(file),
Number(polda.id)
)
}
/>
<span className="font-semibold text-gray-900 text-sm">
{polda.name}
</span>
</Label>
{/* Tombol expand hanya untuk SATKER POLRI */}
{polda.name === "SATKER POLRI" && polda.subDestination && (
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleExpand(
Number(polda.id)
);
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
>
<Icon
icon={
expandedPolda[Number(polda.id)]
? "mdi:chevron-up"
: "mdi:chevron-down"
}
width={16}
height={16}
/>
</button>
)}
</div>
{/* Sub-items hanya untuk SATKER POLRI */}
{polda.name === "SATKER POLRI" && polda.subDestination &&
expandedPolda[Number(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[
files.indexOf(file)
]?.has(
Number(
sub.id
)
)
);
return (
<Button
size="sm"
variant="outline"
className="text-xs h-6 px-2"
onClick={() =>
handleSelectAllSubItems(
files.indexOf(file),
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>
);
})()}
</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[
files.indexOf(file)
]?.has(
Number(
sub.id
)
) ||
false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
files.indexOf(file),
Number(sub.id)
)
}
/>
<span className="text-gray-700">
{
sub.name
}
</span>
</Label>
)
)}
</div>
</div>
)}
</div>
)
)}
</div>
{polda.name === "SATKER POLRI" && polda.subDestination && expandedPolda[Number(polda.id)] && (
<div className="ml-6 space-y-1">
<div className="flex items-center space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => handleSelectAllSubItems(files.indexOf(file), polda)}
>
{polda.subDestination.every((sub: any) =>
fileCheckedLevels[files.indexOf(file)]?.has(Number(sub.id))
) ? "Batal Pilih Semua" : "Pilih Semua"}
</Button>
</div>
{polda.subDestination.map((sub: any) => (
<div key={sub.id} className="flex items-center space-x-2 ml-4">
<input
type="checkbox"
checked={fileCheckedLevels[files.indexOf(file)]?.has(Number(sub.id)) || false}
onChange={(e) =>
handleFileCheckboxChangePlacement(
files.indexOf(file),
Number(sub.id)
)
}
className="form-checkbox"
/>
<span className="text-sm">{sub.name}</span>
</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>
</DialogContent>
</Dialog>
)}
</div>
</div>
</div>
</div>

View File

@ -25,7 +25,7 @@ import {
} from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { Dialog, DialogClose, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { register } from "module";
import { Switch } from "@/components/ui/switch";
@ -93,6 +93,7 @@ type Detail = {
};
interface FileWithPreview extends File {
id: string;
preview: string;
}
@ -146,6 +147,9 @@ export default function FormVideoUpdate() {
type VideoSchema = z.infer<typeof videoSchema>;
let progressInfo: any = [];
let counterUpdateProgress = 0;
const isDetailOfRegionShowed = false;
const [progressList, setProgressList] = useState<any>([]);
let uploadPersen = 0;
const [isStartUpload, setIsStartUpload] = useState(false);
@ -764,19 +768,36 @@ export default function FormVideoUpdate() {
});
};
const getPlacement = (fileId?: string): string => {
if (fileId && filePlacements[fileId]) {
const placements = filePlacements[fileId];
let nowArr = placements.join(",");
nowArr = nowArr?.replaceAll("all", "all");
nowArr = nowArr?.replaceAll("mabes", "mabes");
nowArr = nowArr?.replaceAll("wilayah", "polda");
nowArr = nowArr?.replaceAll("polda", "polda");
nowArr = nowArr?.replaceAll("satker", "satker");
nowArr = nowArr?.replaceAll("international", "international");
return nowArr;
const getPlacement = () => {
const temp = [];
for (let i = 0; i < files.length; i++) {
const file = files[i] as any;
if (file.id && filePlacements[file.id] && filePlacements[file.id].length > 0) {
const now = filePlacements[file.id];
const normalizedNow = now.map((item): PlacementType => {
const value = String(item);
if (value === "nasional") return "mabes";
if (value === "wilayah") return "polda";
if (value === "semua") return "all";
return item as PlacementType;
});
const uniqueNow = Array.from(new Set(normalizedNow));
const nowArr = uniqueNow.join(",");
// Dapatkan checked levels untuk file ini
const currentFileCheckedLevels = fileCheckedLevels[file.id]
? Array.from(fileCheckedLevels[file.id])
: [];
const data = {
mediaFileId: file.id,
placements: nowArr,
customLocationPlacements: currentFileCheckedLevels.join(","),
};
temp.push(data);
}
}
return "";
return temp;
};
const setupPlacement = (fileId: string, placement: string, isChecked: boolean) => {
@ -926,12 +947,10 @@ export default function FormVideoUpdate() {
}
// Update file placements
if (files.length > 0) {
files.forEach((file: any) => {
if (file.id) {
updateFilePlacements(getPlacement(file.id));
}
});
const responseFilePlacements = await updateFilePlacements(getPlacement());
if (responseFilePlacements?.error) {
error(responseFilePlacements?.message);
return false;
}
if (selectedFiles.length > 0) {
@ -1317,13 +1336,13 @@ export default function FormVideoUpdate() {
</Fragment>
) : null}
{files.length > 0 && (
<div className="mt-4 space-y-2">
<Label className="text-lg font-semibold">
<div className="mt-4">
<Label className="text-md font-semibold">
{" "}
{t("file-media", { defaultValue: "File Media" })}
</Label>
<div className="grid gap-4">
{files.map((file: any) => (
{files.map((file: any, index: any) => (
<div
key={file.id}
className="flex items-center border p-2 rounded-md"
@ -1347,136 +1366,319 @@ export default function FormVideoUpdate() {
})}
</a>
</div>
<div className="flex flex-wrap gap-3 items-center">
<div>
<Label className="flex items-center space-x-2">
<Checkbox
checked={fileUnitSelections[file.id]?.semua || false}
onCheckedChange={(checked) =>
handleFileUnitChange(file.id, "semua", checked as boolean)
}
/>
<span>
{t("all", { defaultValue: "All" })}
</span>
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<Checkbox
checked={fileUnitSelections[file.id]?.nasional || false}
onCheckedChange={(checked) =>
handleFileUnitChange(file.id, "nasional", checked as boolean)
}
/>
<span>Nasional</span>
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<Checkbox
checked={fileUnitSelections[file.id]?.wilayah || false}
onCheckedChange={(checked) =>
handleFileUnitChange(file.id, "wilayah", checked as boolean)
}
/>
<span>Wilayah</span>
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<Checkbox
checked={fileUnitSelections[file.id]?.international || false}
onCheckedChange={(checked) =>
handleFileUnitChange(file.id, "international", checked as boolean)
}
/>
<span>Internasional</span>
</Label>
</div>
<div>
<Dialog>
<DialogTrigger asChild>
<Button
type="button"
variant="outline"
size="sm"
onClick={() => setTempFile(file)}
<div className="bg-white rounded-md p-4 border">
{/* Checkbox Tingkat Utama */}
<div className="space-y-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ key: "semua", label: "Semua" },
{
key: "nasional",
label: "Nasional",
},
{ key: "wilayah", label: "Wilayah" },
{
key: "international",
label: "Internasional",
},
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
>
Detail Wilayah
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Daftar Wilayah POLDA dan SATKER</DialogTitle>
</DialogHeader>
<div className="max-h-96 overflow-y-auto">
{listDest?.filter((item) => item.levelNumber === 2)
.map((poldaItem) => (
<div key={poldaItem.id} className="mb-4">
<div className="flex items-center space-x-2 mb-2">
<Checkbox
checked={fileCheckedLevels[file.id]?.has(poldaItem.id) || false}
onCheckedChange={(checked) =>
handleFileCheckboxChangePlacement(file.id, poldaItem, checked as boolean)
}
<Checkbox
// id={`${item.key}-${index}`}
checked={
fileUnitSelections[file.id]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
file.id,
item.key,
value as boolean
);
setupPlacement(
file.id,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
</div>
{/* Detail Wilayah */}
{fileUnitSelections[file.id]?.wilayah && isDetailOfRegionShowed && (
<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: "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[file.id]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
file.id,
item.key,
value as boolean
);
setupPlacement(
file.id,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{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"
>
<Icon
icon="material-symbols:tune"
width={16}
height={16}
/>
<Label className="font-medium">
{poldaItem.name}
</Label>
{poldaItem.name === "SATKER POLRI" && poldaItem.subDestination.length > 0 && (
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => toggleExpand(poldaItem.id)}
>
{expandedPolda.has(poldaItem.id) ? "Tutup" : "Buka"}
</Button>
{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 SATKER
</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[
file.id
]?.has(
Number(
polda.id
)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
file.id,
polda,
!fileCheckedLevels[file.id]?.has(Number(polda.id))
)
}
/>
<span className="font-semibold text-gray-900 text-sm">
{polda.name}
</span>
</Label>
{/* Tombol expand hanya untuk SATKER POLRI */}
{polda.name === "SATKER POLRI" && 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.has(
polda.id
)
? "mdi:chevron-up"
: "mdi:chevron-down"
}
width={16}
height={16}
/>
</button>
)}
</div>
{/* Sub-items hanya untuk SATKER POLRI */}
{polda.name === "SATKER POLRI" && polda.subDestination &&
expandedPolda.has(
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[
file.id
]?.has(
Number(
sub.id
)
)
);
return (
<Button
size="sm"
variant="outline"
className="text-xs h-6 px-2"
onClick={() =>
handleSelectAllSubItems(
file.id,
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>
);
})()}
</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[
file.id
]?.has(
Number(
sub.id
)
) ||
false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
file.id,
polda,
!fileCheckedLevels[file.id]?.has(Number(sub.id))
)
}
/>
<span className="text-gray-700">
{
sub.name
}
</span>
</Label>
)
)}
</div>
</div>
)}
</div>
)
)}
</div>
{poldaItem.name === "SATKER POLRI" && expandedPolda.has(poldaItem.id) && (
<div className="ml-6 space-y-2">
<div className="flex items-center space-x-2">
<Button
type="button"
variant="outline"
size="sm"
onClick={() => handleSelectAllSubItems(file.id, poldaItem)}
>
Pilih Semua
</Button>
</div>
{poldaItem.subDestination.map((satkerItem) => (
<div key={satkerItem.id} className="flex items-center space-x-2 ml-4">
<Checkbox
checked={fileCheckedLevels[file.id]?.has(satkerItem.id) || false}
onCheckedChange={(checked) => {
setFileCheckedLevels(prev => {
const currentFileLevels = prev[file.id] || new Set<number>();
const newFileLevels = new Set(currentFileLevels);
if (checked) {
newFileLevels.add(satkerItem.id);
} else {
newFileLevels.delete(satkerItem.id);
}
return { ...prev, [file.id]: newFileLevels };
});
updateMainCheckboxFromModal(file.id);
}}
/>
<Label className="text-sm">
{satkerItem.name}
</Label>
</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>
</DialogContent>
</Dialog>
</div>
)}
</div>
</div>
</div>

View File

@ -7,17 +7,34 @@ type VideoPlayerProps = {
const VideoPlayer: React.FC<VideoPlayerProps> = ({ url }) => {
return (
// <ReactPlayer
// className="react-player"
// width="100%"
// height="100%"
// playing
// pip
// controls
// config={{
// file: {
// attributes: {
// controlsList: "nodownload",
// },
// },
// }}
// url={url}
// />
<ReactPlayer
className="react-player"
width="100%"
height="100%"
playing
pip
controls
playing
muted
pip={false} //
config={{
file: {
attributes: {
controlsList: "nodownload",
controlsList: "nodownload",
},
},
}}
@ -26,4 +43,4 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ url }) => {
);
};
export default VideoPlayer;
export default VideoPlayer;