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

851 lines
31 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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";
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,
});
// 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");
const [selectedItems, setSelectedItems] = useState<number[]>([]);
const [description, setDescription] = useState("");
const handleCheckboxChange = (index: number) => {
setSelectedItems((prev) =>
prev.includes(index) ? prev.filter((i) => i !== index) : [...prev, index],
);
};
const toggleMediaOnline = (item: string) => {
setSelectedMediaOnline((prev) =>
prev.includes(item) ? prev.filter((m) => m !== item) : [...prev, item],
);
};
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 }[],
) => {
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,
),
);
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>
)}
{/* 🧩 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
<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>
</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"
checked={selectedItems.includes(i)}
onChange={() => handleCheckboxChange(i)}
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>
{selectedItems.length > 0 && (
<div className="mt-4">
<Label className="my-2">Deskripsi</Label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Masukkan deskripsi untuk item yang dipilih..."
className="w-full p-3 border rounded-md text-sm"
rows={4}
/>
</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"
checked={selectedItems.includes(i)}
onChange={() => handleCheckboxChange(i)}
className="absolute top-2 left-2 w-4 h-4"
/>
<span className="text-xs font-medium ml-5">
{item.name}
</span>
</div>
</div>
))}
</div>
{selectedItems.length > 0 && (
<div className="mt-4">
<Label className="my-2">Deskripsi</Label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Masukkan deskripsi untuk item yang dipilih..."
className="w-full p-3 border rounded-md text-sm"
rows={4}
/>
</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"
/>
<input
type="checkbox"
checked={selectedItems.includes(i)}
onChange={() => handleCheckboxChange(i)}
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>
{selectedItems.length > 0 && (
<div className="mt-4">
<Label className="my-2">Deskripsi</Label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Masukkan deskripsi untuk item yang dipilih..."
className="w-full p-3 border rounded-md text-sm"
rows={4}
/>
</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"
checked={selectedItems.includes(i)}
onChange={() => handleCheckboxChange(i)}
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>
{selectedItems.length > 0 && (
<div className="mt-4">
<Label className="my-2">Deskripsi</Label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Masukkan deskripsi untuk item yang dipilih..."
className="w-full p-3 border rounded-md text-sm"
rows={4}
/>
</div>
)}
</div>
)}
</div>
</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>
);
}