pull main
This commit is contained in:
commit
6688345b64
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
|
@ -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",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue