477 lines
16 KiB
TypeScript
477 lines
16 KiB
TypeScript
"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";
|
||
|
||
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);
|
||
|
||
// 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 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" && (
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
className="mt-4"
|
||
onClick={() => setIsDialogOpen(true)}
|
||
>
|
||
<Plus className="h-4 w-4 mr-2" />
|
||
Tambahkan Videotron
|
||
</Button>
|
||
)}
|
||
|
||
{/* 🧩 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" ? (
|
||
// ✅ Jika user pilih "Yes" → tampil upload file
|
||
<div className="space-y-2">
|
||
<Label className="text-sm font-medium">Upload File</Label>
|
||
<div className="flex items-center gap-2">
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={() => setIsUploadOpen(true)}
|
||
>
|
||
<Plus className="h-4 w-4 mr-2" />
|
||
Upload File
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
// ✅ Jika user pilih "Tidak" → tampil textarea deskripsi
|
||
<div className="space-y-2">
|
||
<Label className="text-sm font-medium">Deskripsi Promote</Label>
|
||
<textarea
|
||
placeholder="Tulis deskripsi promote..."
|
||
className="w-full min-h-[100px] p-3 border rounded-md text-sm resize-none focus:outline-none focus:ring-2 focus:ring-primary"
|
||
/>
|
||
</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>
|
||
);
|
||
}
|