web-campaignpool/components/form/campaign-form.tsx

784 lines
28 KiB
TypeScript
Raw Normal View History

2025-11-11 02:52:38 +00:00
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Label } from "@/components/ui/label";
import { Calendar } from "@/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { CalendarIcon, Plus, Trash2 } from "lucide-react";
import { format } from "date-fns";
import { id } from "date-fns/locale";
import { Progress } from "../ui/progress";
import DialogMediaOnline from "../dialog/media-online";
import DialogMediaSosial from "../dialog/media-sosial";
import MapVideotron from "../global/maps";
import dynamic from "next/dynamic";
2025-11-11 02:52:38 +00:00
export default function FormCampaign() {
const [startDate, setStartDate] = useState<Date | undefined>(undefined);
const [endDate, setEndDate] = useState<Date | undefined>(undefined);
const [goal, setGoal] = useState("Publikasi");
const [available, setAvailable] = useState("Yes");
const [isUploadOpen, setIsUploadOpen] = useState(false);
const [media, setMedia] = useState("Media Online");
const [isDialogOpen, setIsDialogOpen] = useState(false);
const MapVideotron = dynamic(() => import("../global/maps"), {
ssr: false,
});
2025-11-11 02:52:38 +00:00
// contoh data pilihan media online (bisa diganti sesuai kebutuhan)
const mediaOnlineList = [
"Tribrata News Mabes",
"Tribrata News Polda Aceh",
"Tribrata News Polda Jawa Timur",
"Tribrata News Polda Jawa Tengah",
"Tribrata News Polda Jawa Barat",
];
const [selectedMediaOnline, setSelectedMediaOnline] = useState<string[]>([]);
const [contentType, setContentType] = useState("Meme");
const [talkshowType, setTalkshowType] = useState("Renjani Nyrah");
const [musicType, setMusicType] = useState("Sendrasena");
2025-11-11 02:52:38 +00:00
const toggleMediaOnline = (item: string) => {
setSelectedMediaOnline((prev) =>
prev.includes(item) ? prev.filter((m) => m !== item) : [...prev, item],
2025-11-11 02:52:38 +00:00
);
};
const [files, setFiles] = useState<
{ file: File; progress: number; uploaded: boolean }[]
>([]);
const [url, setUrl] = useState("");
// ✅ Upload dari file input
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFiles = e.target.files;
if (!selectedFiles) return;
const newFiles = Array.from(selectedFiles).map((f) => ({
file: f,
progress: 0,
uploaded: false,
}));
setFiles((prev) => [...prev, ...newFiles]);
simulateUpload(newFiles);
};
// ✅ Simulasi upload progress
const simulateUpload = (
fileList: { file: File; progress: number; uploaded: boolean }[],
2025-11-11 02:52:38 +00:00
) => {
fileList.forEach((fileObj) => {
let progress = 0;
const interval = setInterval(() => {
progress += 10;
setFiles((prev) =>
prev.map((f) =>
f.file === fileObj.file
? { ...f, progress, uploaded: progress >= 100 }
: f,
),
2025-11-11 02:52:38 +00:00
);
if (progress >= 100) clearInterval(interval);
}, 300);
});
};
// ✅ Upload dari URL
const handleUploadFromUrl = () => {
if (!url.trim()) return;
const fakeFile = {
file: new File([], url.split("/").pop() || "file_from_url.jpg"),
progress: 100,
uploaded: true,
};
setFiles((prev) => [...prev, fakeFile]);
setUrl("");
};
// ✅ Hapus file
const removeFile = (index: number) => {
setFiles((prev) => prev.filter((_, i) => i !== index));
};
return (
<div className="bg-white rounded-2xl shadow-sm p-6 space-y-8">
{/* Langkah 1 */}
<section className="border-b pb-6">
<h2 className="font-semibold mb-4">Langkah 1</h2>
<p className="text-sm font-medium mb-2">Pilih Durasi</p>
<div className="flex flex-wrap gap-4">
<div>
<Label className="text-sm">Dari Tanggal</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-[200px] justify-start text-left font-normal"
>
<CalendarIcon className="mr-2 h-4 w-4" />
{startDate
? format(startDate, "dd MMMM yyyy", { locale: id })
: "Pilih tanggal"}
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Calendar
mode="single"
selected={startDate}
onSelect={setStartDate}
locale={id}
/>
</PopoverContent>
</Popover>
</div>
<div>
<Label className="text-sm">Sampai Tanggal</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-[200px] justify-start text-left font-normal"
>
<CalendarIcon className="mr-2 h-4 w-4" />
{endDate
? format(endDate, "dd MMMM yyyy", { locale: id })
: "Pilih tanggal"}
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Calendar
mode="single"
selected={endDate}
onSelect={setEndDate}
locale={id}
/>
</PopoverContent>
</Popover>
</div>
</div>
</section>
<section className="border-b pb-6">
<h2 className="font-semibold mb-4">Langkah 2</h2>
<p className="text-sm font-medium mb-2">Pilih Media</p>
<RadioGroup
value={media}
onValueChange={setMedia}
className="flex flex-wrap gap-4"
>
{[
"Media Online",
"Media Sosial",
"Videotron",
"Radio Polri",
"TV Polri",
"Majalah Digital",
].map((m) => (
<div key={m} className="flex items-center space-x-2">
<RadioGroupItem value={m} id={m} />
<Label htmlFor={m}>{m}</Label>
</div>
))}
</RadioGroup>
{/* Tombol muncul sesuai media terpilih */}
{media === "Media Online" && (
<Button
variant="outline"
size="sm"
className="mt-4"
onClick={() => setIsDialogOpen(true)}
>
<Plus className="h-4 w-4 mr-2" />
Tambahkan Media Online
</Button>
)}
{media === "Media Sosial" && (
<Button
variant="outline"
size="sm"
className="mt-4"
onClick={() => setIsDialogOpen(true)}
>
<Plus className="h-4 w-4 mr-2" />
Tambahkan Media Sosial
</Button>
)}
{media === "Videotron" && (
<div className="mt-4 space-y-4">
{/* Map tampil */}
<MapVideotron />
</div>
2025-11-11 02:52:38 +00:00
)}
{/* 🧩 Komponen DialogMediaOnline dipanggil di sini */}
{media === "Media Online" && (
<DialogMediaOnline
isOpen={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
/>
)}
{media === "Media Sosial" && (
<DialogMediaSosial
isOpen={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
/>
)}
{media === "Videotron" && (
<DialogMediaSosial
isOpen={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
/>
)}
{media === "Radio Polri" && (
<div className="mt-4 space-y-3">
{[
"Pagi pukul 06:00 07:00",
"Siang pukul 12:00 13:00",
"Sore pukul 16:00 17:00",
"Malam pukul 20:00 21:00",
].map((time) => (
<div key={time} className="flex items-center space-x-2">
<input
type="checkbox"
id={time}
value={time}
className="w-4 h-4 border border-purple-600 text-purple-600 focus:ring-2 focus:ring-purple-500 rounded"
/>
<Label htmlFor={time} className="text-sm">
{time}
</Label>
</div>
))}
</div>
)}
{media === "TV Polri" && (
<div className="mt-4 space-y-3">
{[
"Pagi pukul 06:00 07:00",
"Siang pukul 12:00 13:00",
"Sore pukul 16:00 17:00",
"Malam pukul 20:00 21:00",
].map((time) => (
<div key={time} className="flex items-center space-x-2">
<input
type="checkbox"
id={time}
value={time}
className="w-4 h-4 border border-purple-600 text-purple-600 focus:ring-2 focus:ring-purple-500 rounded"
/>
<Label htmlFor={time} className="text-sm">
{time}
</Label>
</div>
))}
</div>
)}
</section>
{/* Langkah 3 */}
<section className="border-b pb-6">
<h2 className="font-semibold mb-4">Langkah 3</h2>
<p className="text-sm font-medium mb-2">Tujuan</p>
<RadioGroup value={goal} onValueChange={setGoal} className="flex gap-4">
{["Publikasi", "Sosialisasi"].map((g) => (
<div key={g} className="flex items-center space-x-2">
<RadioGroupItem value={g} id={g} />
<Label htmlFor={g}>{g}</Label>
</div>
))}
</RadioGroup>
</section>
{/* Langkah 4 */}
<section>
<h2 className="font-semibold mb-4">Langkah 4</h2>
<p className="text-sm font-medium mb-2">Materi Promote Tersedia</p>
<RadioGroup
value={available}
onValueChange={setAvailable}
className="flex gap-4 mb-4"
>
{["Yes", "Tidak"].map((a) => (
<div key={a} className="flex items-center space-x-2">
<RadioGroupItem value={a} id={a} />
<Label htmlFor={a}>{a}</Label>
</div>
))}
</RadioGroup>
{available === "Yes" ? (
// ✅ Upload
2025-11-11 02:52:38 +00:00
<div className="space-y-2">
<Label className="text-sm font-medium">Upload File</Label>
<Button
variant="outline"
size="sm"
onClick={() => setIsUploadOpen(true)}
>
<Plus className="h-4 w-4 mr-2" />
Upload File
</Button>
2025-11-11 02:52:38 +00:00
</div>
) : (
// ❌ Tidak → tampil pilihan konten
<div className="space-y-4">
{/* Pilih Jenis Konten */}
<div>
<p className="text-sm font-medium mb-2">Pilih Jenis Konten</p>
<RadioGroup
value={contentType}
onValueChange={setContentType}
className="flex flex-wrap gap-4"
>
{["Meme", "AI Influencer", "Talkshow", "Musik"].map((item) => (
<div key={item} className="flex items-center space-x-2">
<RadioGroupItem value={item} id={item} />
<Label htmlFor={item}>{item}</Label>
</div>
))}
</RadioGroup>
</div>
{/* List Konten (Dummy) */}
{/* List Konten */}
<div>
<p className="text-sm font-medium mb-2">Pilih {contentType}</p>
{/* 🔥 TAMPILAN KHUSUS MEME */}
{contentType === "Meme" && (
<div className="space-y-4">
{/* Search */}
<div className="relative">
<input
type="text"
placeholder="Cari Meme..."
className="w-full pl-10 pr-4 py-2 border rounded-full text-sm"
/>
<span className="absolute left-3 top-2.5 text-gray-400 text-sm">
🔍
</span>
</div>
{/* Grid Meme */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
{[
{
img: "/meme1.png",
title:
"Respons Cepat Lakukan Sterilisasi Ancaman Bom di Depok",
},
{
img: "/meme2.png",
title: "Upacara Penutupan Pendidikan Anggota Polri",
},
{
img: "/meme3.png",
title: "Pantau Situasi Kamtibmas Malam Natal",
},
{
img: "/meme4.png",
title: "Program SPPG Bersama Penerima Manfaat",
},
].map((item, i) => (
<div
key={i}
className="bg-white border rounded-xl overflow-hidden shadow-sm hover:shadow-md"
>
<div className="relative">
<img
src={item.img}
className="w-full h-40 object-cover"
/>
<input
type="checkbox"
className="absolute top-2 left-2 w-4 h-4"
/>
<div className="absolute bottom-2 right-2 text-white text-xs bg-black/50 px-1 rounded">
</div>
</div>
<div className="p-2 text-xs line-clamp-2">
{item.title}
</div>
</div>
))}
</div>
</div>
)}
{contentType === "AI Influencer" && (
<div className="space-y-4">
{/* Search */}
<div className="relative">
<input
type="text"
placeholder="Cari AI Influencer..."
className="w-full pl-10 pr-4 py-2 border rounded-full text-sm"
/>
<span className="absolute left-3 top-2.5 text-gray-400 text-sm">
🔍
</span>
</div>
{/* Grid AI */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
{[
{ img: "/ai1.png", name: "Revandra Jaya" },
{ img: "/ai2.png", name: "Fudhalina Adisty" },
{ img: "/ai3.png", name: "Adriyan Pratama" },
{ img: "/ai4.png", name: "Nadhya Wijaya" },
].map((item, i) => (
<div
key={i}
className="relative rounded-xl overflow-hidden shadow-sm hover:shadow-md"
>
{/* Image */}
<img
src={item.img}
className="w-full h-48 object-cover"
/>
{/* Overlay bawah */}
<div className="absolute bottom-0 left-0 right-0 bg-white/80 backdrop-blur px-3 py-2 flex items-center gap-2">
<input type="checkbox" className="w-4 h-4" />
<span className="text-xs font-medium">
{item.name}
</span>
</div>
</div>
))}
</div>
{/* Footer */}
<div className="flex justify-end items-center text-xs text-gray-500 gap-4">
<span>Rows per page: 1</span>
<span>1-1 of 1</span>
<div className="flex gap-2">
<button>{"<"}</button>
<button>{">"}</button>
</div>
</div>
</div>
)}
{contentType === "Talkshow" && (
<div className="space-y-4">
{/* Select Talkshow */}
<div>
<p className="text-sm font-medium mb-2">Pilih Talkshow</p>
<select
value={talkshowType}
onChange={(e) => setTalkshowType(e.target.value)}
className="border rounded-md px-3 py-2 text-sm"
>
<option value="Renjani Nyrah">Renjani Nyrah</option>
<option value="Agatha Bicara">Agatha Bicara</option>
</select>
</div>
{/* Konten berdasarkan pilihan */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{(talkshowType === "Renjani Nyrah"
? [
{
img: "/ts1.jpg",
title: "Renjani Nyrah - Napak Tilas di Museum",
},
{
img: "/ts2.jpg",
title: "Renjani Nyrah - Kata Kata Baru",
},
{
img: "/ts3.jpg",
title: "Renjani Nyrah - Pestapora 2025",
},
]
: [
{
img: "/agatha1.jpg",
title: "Agatha Bicara - Kerja DPR Itu Apa?",
},
{
img: "/agatha1.jpg",
title: "Agatha Bicara - Kerja DPR Itu Apa?",
},
{
img: "/agatha2.png",
title: "Agatha Bicara - Penghargaan dari Presiden",
},
]
).map((item, i) => (
<div
key={i}
className="bg-white border rounded-xl overflow-hidden shadow-sm hover:shadow-md"
>
{/* Image */}
<div className="relative">
<img
src={item.img}
className="w-full h-40 object-cover"
/>
{/* Checkbox */}
<input
type="checkbox"
className="absolute top-2 left-2 w-4 h-4"
/>
{/* Expand icon */}
<div className="absolute bottom-2 right-2 text-white text-xs bg-black/50 px-1 rounded">
</div>
</div>
{/* Title */}
<div className="p-2 text-xs line-clamp-2">
{item.title}
</div>
</div>
))}
</div>
</div>
)}
{contentType === "Musik" && (
<div className="space-y-4">
{/* Dropdown Musik */}
<div>
<p className="text-sm font-medium mb-2">Pilih Musik</p>
<select
value={musicType}
onChange={(e) => setMusicType(e.target.value)}
className="border rounded-md px-3 py-2 text-sm"
>
<option value="Sendrasena">Sendrasena</option>
<option value="Selara Luna">Selara Luna</option>
</select>
</div>
{/* Grid Musik */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
{(musicType === "Sendrasena"
? [
{
img: "/m1.jpg",
title: "Sendrasena - Track 1",
},
{
img: "/m2.jpg",
title: "Sendrasena - Track 2",
},
{
img: "/m3.jpg",
title: "Sendrasena - Track 3",
},
{
img: "/m4.jpg",
title: "Sendrasena - Track 4",
},
]
: [
{
img: "/sl1.jpg",
title: "Selara Luna - Track 1",
},
{
img: "/sl2.jpg",
title: "Selara Luna - Track 2",
},
{
img: "/sl3.jpg",
title: "Selara Luna - Track 3",
},
{
img: "/sl4.jpg",
title: "Selara Luna - Track 4",
},
]
).map((item, i) => (
<div
key={i}
className="bg-white border rounded-xl overflow-hidden shadow-sm hover:shadow-md"
>
{/* Cover */}
<div className="relative">
<img
src={item.img}
className="w-full h-40 object-cover"
/>
{/* Checkbox */}
<input
type="checkbox"
className="absolute top-2 left-2 w-4 h-4"
/>
{/* Play icon (biar beda dari talkshow 😄) */}
<div className="absolute bottom-2 right-2 text-white text-xs bg-black/50 px-2 py-1 rounded">
</div>
</div>
{/* Title */}
<div className="p-2 text-xs line-clamp-2">
{item.title}
</div>
</div>
))}
</div>
</div>
)}
</div>
2025-11-11 02:52:38 +00:00
</div>
)}
</section>
<div className="pt-6">
<Button className="w-[120px]" size="lg">
Submit
</Button>
</div>
{/* Modal Upload */}
<Dialog open={isUploadOpen} onOpenChange={setIsUploadOpen}>
<DialogContent className="max-w-lg w-[90vw] sm:w-full">
<DialogHeader>
<DialogTitle>Unggah Berkas</DialogTitle>
<p className="text-sm text-muted-foreground">
Pilih berkas dan unggah dengan aman untuk melanjutkan.
</p>
</DialogHeader>
{/* === Upload Section === */}
<div className="border-2 border-dashed rounded-lg p-6 flex flex-col items-center justify-center text-center space-y-2 w-full">
<p className="text-sm text-muted-foreground">
Seret dan jatuhkan berkas Anda
</p>
<p className="text-xs text-muted-foreground">
Format .PNG, .JPG, dan .JPEG hingga 50MB
</p>
<label htmlFor="fileInput">
<Button
variant="outline"
size="sm"
className="mt-2 cursor-pointer"
>
Pilih Berkas
</Button>
<Input
id="fileInput"
type="file"
multiple
className="hidden"
onChange={handleFileUpload}
/>
</label>
</div>
{/* === Upload via URL === */}
<div className="flex flex-col sm:flex-row items-center gap-2 mt-4 w-full">
<Input
type="url"
placeholder="Tambahkan URL berkas"
value={url}
onChange={(e) => setUrl(e.target.value)}
className="flex-1"
/>
<Button onClick={handleUploadFromUrl} className="w-full sm:w-auto">
Unggah
</Button>
</div>
{/* === Uploaded Files === */}
{files.length > 0 && (
<div className="mt-5 space-y-3 max-h-[60vh] overflow-y-auto">
<h4 className="text-sm font-semibold">Uploaded Files</h4>
<div className="space-y-2">
{files.map((f, i) => (
<div
key={i}
className="border rounded-lg p-3 flex flex-wrap sm:flex-nowrap justify-between items-start sm:items-center gap-3"
>
<div className="flex flex-col min-w-0 flex-1">
<p className="text-sm font-medium truncate max-w-[250px]">
{f.file.name}
</p>
<p className="text-xs text-muted-foreground">
{(f.file.size / (1024 * 1024)).toFixed(1)}MB {" "}
{f.uploaded ? (
<span className="text-green-600 font-medium">
Uploaded Successfully
</span>
) : (
<span className="text-blue-600 font-medium">
{f.progress}% Uploading...
</span>
)}
</p>
{!f.uploaded && (
<Progress
value={f.progress}
className="h-1 mt-1 w-full bg-gray-200"
/>
)}
</div>
<Button
variant="ghost"
size="icon"
onClick={() => removeFile(i)}
className="shrink-0"
>
<Trash2 className="h-4 w-4 text-muted-foreground" />
</Button>
</div>
))}
</div>
</div>
)}
<DialogFooter className="mt-4 flex flex-col sm:flex-row justify-end gap-2 sm:gap-4">
<Button
variant="outline"
onClick={() => setIsUploadOpen(false)}
className="w-full sm:w-auto"
>
Batal
</Button>
<Button
onClick={() => setIsUploadOpen(false)}
className="w-full sm:w-auto"
>
Lampirkan Berkas
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
}