update form promote user,add tabel executive
continuous-integration/drone/push Build is passing
Details
|
|
@ -0,0 +1,13 @@
|
||||||
|
import DashboardNavbar from "@/components/dashboard/dashboard-navbar";
|
||||||
|
import CoordinatorTable from "@/components/table/coordinator-table";
|
||||||
|
|
||||||
|
export default function CoordinatorPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-[#f8f9fa]">
|
||||||
|
<DashboardNavbar />
|
||||||
|
<div className="p-6">
|
||||||
|
<CoordinatorTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ interface DialogCampaignDetailProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
data?: {
|
data?: {
|
||||||
|
deskripsi: string;
|
||||||
durasi: string;
|
durasi: string;
|
||||||
media: string;
|
media: string;
|
||||||
tujuan: string;
|
tujuan: string;
|
||||||
|
|
@ -46,6 +47,11 @@ export default function DialogCampaignDetail({
|
||||||
|
|
||||||
{/* Detail Info */}
|
{/* Detail Info */}
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Deskripsi</span>
|
||||||
|
<br />
|
||||||
|
<span className="font-semibold">{data.deskripsi}</span>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span className="text-gray-500">Durasi</span>
|
<span className="text-gray-500">Durasi</span>
|
||||||
<br />
|
<br />
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ import { id } from "date-fns/locale";
|
||||||
import { Progress } from "../ui/progress";
|
import { Progress } from "../ui/progress";
|
||||||
import DialogMediaOnline from "../dialog/media-online";
|
import DialogMediaOnline from "../dialog/media-online";
|
||||||
import DialogMediaSosial from "../dialog/media-sosial";
|
import DialogMediaSosial from "../dialog/media-sosial";
|
||||||
|
import MapVideotron from "../global/maps";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
export default function FormCampaign() {
|
export default function FormCampaign() {
|
||||||
const [startDate, setStartDate] = useState<Date | undefined>(undefined);
|
const [startDate, setStartDate] = useState<Date | undefined>(undefined);
|
||||||
|
|
@ -34,6 +36,10 @@ export default function FormCampaign() {
|
||||||
const [media, setMedia] = useState("Media Online");
|
const [media, setMedia] = useState("Media Online");
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
const MapVideotron = dynamic(() => import("../global/maps"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
// contoh data pilihan media online (bisa diganti sesuai kebutuhan)
|
// contoh data pilihan media online (bisa diganti sesuai kebutuhan)
|
||||||
const mediaOnlineList = [
|
const mediaOnlineList = [
|
||||||
"Tribrata News Mabes",
|
"Tribrata News Mabes",
|
||||||
|
|
@ -44,10 +50,13 @@ export default function FormCampaign() {
|
||||||
];
|
];
|
||||||
|
|
||||||
const [selectedMediaOnline, setSelectedMediaOnline] = useState<string[]>([]);
|
const [selectedMediaOnline, setSelectedMediaOnline] = useState<string[]>([]);
|
||||||
|
const [contentType, setContentType] = useState("Meme");
|
||||||
|
const [talkshowType, setTalkshowType] = useState("Renjani Nyrah");
|
||||||
|
const [musicType, setMusicType] = useState("Sendrasena");
|
||||||
|
|
||||||
const toggleMediaOnline = (item: string) => {
|
const toggleMediaOnline = (item: string) => {
|
||||||
setSelectedMediaOnline((prev) =>
|
setSelectedMediaOnline((prev) =>
|
||||||
prev.includes(item) ? prev.filter((m) => m !== item) : [...prev, item]
|
prev.includes(item) ? prev.filter((m) => m !== item) : [...prev, item],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -73,7 +82,7 @@ export default function FormCampaign() {
|
||||||
|
|
||||||
// ✅ Simulasi upload progress
|
// ✅ Simulasi upload progress
|
||||||
const simulateUpload = (
|
const simulateUpload = (
|
||||||
fileList: { file: File; progress: number; uploaded: boolean }[]
|
fileList: { file: File; progress: number; uploaded: boolean }[],
|
||||||
) => {
|
) => {
|
||||||
fileList.forEach((fileObj) => {
|
fileList.forEach((fileObj) => {
|
||||||
let progress = 0;
|
let progress = 0;
|
||||||
|
|
@ -83,8 +92,8 @@ export default function FormCampaign() {
|
||||||
prev.map((f) =>
|
prev.map((f) =>
|
||||||
f.file === fileObj.file
|
f.file === fileObj.file
|
||||||
? { ...f, progress, uploaded: progress >= 100 }
|
? { ...f, progress, uploaded: progress >= 100 }
|
||||||
: f
|
: f,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
if (progress >= 100) clearInterval(interval);
|
if (progress >= 100) clearInterval(interval);
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
@ -214,17 +223,11 @@ export default function FormCampaign() {
|
||||||
Tambahkan Media Sosial
|
Tambahkan Media Sosial
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{media === "Videotron" && (
|
{media === "Videotron" && (
|
||||||
<Button
|
<div className="mt-4 space-y-4">
|
||||||
variant="outline"
|
{/* Map tampil */}
|
||||||
size="sm"
|
<MapVideotron />
|
||||||
className="mt-4"
|
</div>
|
||||||
onClick={() => setIsDialogOpen(true)}
|
|
||||||
>
|
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
|
||||||
Tambahkan Videotron
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 🧩 Komponen DialogMediaOnline dipanggil di sini */}
|
{/* 🧩 Komponen DialogMediaOnline dipanggil di sini */}
|
||||||
|
|
@ -325,28 +328,332 @@ export default function FormCampaign() {
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
{available === "Yes" ? (
|
{available === "Yes" ? (
|
||||||
// ✅ Jika user pilih "Yes" → tampil upload file
|
// ✅ Upload
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-sm font-medium">Upload File</Label>
|
<Label className="text-sm font-medium">Upload File</Label>
|
||||||
<div className="flex items-center gap-2">
|
<Button
|
||||||
<Button
|
variant="outline"
|
||||||
variant="outline"
|
size="sm"
|
||||||
size="sm"
|
onClick={() => setIsUploadOpen(true)}
|
||||||
onClick={() => setIsUploadOpen(true)}
|
>
|
||||||
>
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
Upload File
|
||||||
Upload File
|
</Button>
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
// ✅ Jika user pilih "Tidak" → tampil textarea deskripsi
|
// ❌ Tidak → tampil pilihan konten
|
||||||
<div className="space-y-2">
|
<div className="space-y-4">
|
||||||
<Label className="text-sm font-medium">Deskripsi Promote</Label>
|
{/* Pilih Jenis Konten */}
|
||||||
<textarea
|
<div>
|
||||||
placeholder="Tulis deskripsi promote..."
|
<p className="text-sm font-medium mb-2">Pilih Jenis Konten</p>
|
||||||
className="w-full min-h-[100px] p-3 border rounded-md text-sm resize-none focus:outline-none focus:ring-2 focus:ring-primary"
|
|
||||||
/>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import { useRouter } from "next/navigation";
|
||||||
import Footer from "../landing-page/footer";
|
import Footer from "../landing-page/footer";
|
||||||
import Navbar from "../landing-page/navbar";
|
import Navbar from "../landing-page/navbar";
|
||||||
|
|
||||||
// ✅ Dummy user data
|
|
||||||
const users = [
|
const users = [
|
||||||
{
|
{
|
||||||
nrp: "1001",
|
nrp: "1001",
|
||||||
|
|
@ -26,14 +25,19 @@ const users = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nrp: "1003",
|
nrp: "1003",
|
||||||
password: "super123",
|
password: "executive123",
|
||||||
role: "supervisor",
|
role: "executive",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nrp: "1004",
|
nrp: "1004",
|
||||||
password: "approve123",
|
password: "approve123",
|
||||||
role: "approver",
|
role: "approver",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
nrp: "1005",
|
||||||
|
password: "coordinator123",
|
||||||
|
role: "kordinator",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
|
|
@ -66,12 +70,15 @@ export default function Login() {
|
||||||
case "user":
|
case "user":
|
||||||
router.push("/dashboard/user");
|
router.push("/dashboard/user");
|
||||||
break;
|
break;
|
||||||
case "supervisor":
|
case "executive":
|
||||||
router.push("/dashboard/supervisor");
|
router.push("/dashboard/supervisor");
|
||||||
break;
|
break;
|
||||||
case "approver":
|
case "approver":
|
||||||
router.push("/dashboard/approver");
|
router.push("/dashboard/approver");
|
||||||
break;
|
break;
|
||||||
|
case "kordinator":
|
||||||
|
router.push("/dashboard/coordinator");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
router.push("/");
|
router.push("/");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
export default function MapVideotron() {
|
||||||
|
return (
|
||||||
|
<div className="mt-4 h-[400px] w-full rounded-lg overflow-hidden border">
|
||||||
|
<iframe
|
||||||
|
src="https://www.google.com/maps?q=Jakarta&output=embed"
|
||||||
|
className="w-full h-full border-0"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Eye } from "lucide-react";
|
||||||
|
import DialogCampaignDetail from "../dialog/campaign-detail";
|
||||||
|
|
||||||
|
export default function CoordinatorTable() {
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
durasi: "22/08/2025 - 22/08/2026",
|
||||||
|
media: "Media Online",
|
||||||
|
tujuan: "Sosialisasi",
|
||||||
|
materi: "Tersedia",
|
||||||
|
description:
|
||||||
|
"Lorem ipsum dolor sit amet consectetur. Tempor mi scelerisque enim semper sed nibh.",
|
||||||
|
status: "Selesai",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
durasi: "22/08/2025 - 22/08/2026",
|
||||||
|
media: "Media Sosial",
|
||||||
|
tujuan: "Sosialisasi",
|
||||||
|
materi: "Tersedia",
|
||||||
|
description:
|
||||||
|
"Ultricies pellentesque ullamcorper mattis pellentesque. Amet eu ut.",
|
||||||
|
status: "Selesai",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const [selectedRow, setSelectedRow] = useState<any>(null);
|
||||||
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
const openDetail = (row: any) => {
|
||||||
|
setSelectedRow(row);
|
||||||
|
setIsDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="bg-white shadow rounded-lg p-4">
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Durasi</TableHead>
|
||||||
|
<TableHead>Media</TableHead>
|
||||||
|
<TableHead>Tujuan</TableHead>
|
||||||
|
<TableHead>Materi</TableHead>
|
||||||
|
<TableHead>Deskripsi Promote</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
|
<TableHead>Tindakan</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
|
||||||
|
<TableBody>
|
||||||
|
{data.map((row, i) => (
|
||||||
|
<TableRow key={i}>
|
||||||
|
<TableCell>{row.durasi}</TableCell>
|
||||||
|
<TableCell className="font-medium">{row.media}</TableCell>
|
||||||
|
<TableCell>{row.tujuan}</TableCell>
|
||||||
|
<TableCell>{row.materi}</TableCell>
|
||||||
|
<TableCell className="min-w-[200px] text-gray-600">
|
||||||
|
{row.description}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="bg-green-100 text-green-700 px-3 py-1 rounded-full text-xs font-medium">
|
||||||
|
{row.status}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="text-blue-600 p-0 h-auto font-medium flex items-center gap-1"
|
||||||
|
onClick={() => openDetail(row)}
|
||||||
|
>
|
||||||
|
<Eye className="h-4 w-4" />
|
||||||
|
Lihat
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pagination */}
|
||||||
|
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-center mt-4 text-sm text-gray-500 gap-2">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span>Rows per page:</span>
|
||||||
|
<select className="ml-2 border rounded px-1 py-0.5">
|
||||||
|
<option>6</option>
|
||||||
|
<option>12</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between sm:justify-end gap-2">
|
||||||
|
<span>1–1 of 1</span>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<button className="text-gray-400 hover:text-gray-600">◀</button>
|
||||||
|
<button className="text-gray-400 hover:text-gray-600">▶</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogCampaignDetail
|
||||||
|
isOpen={isDialogOpen}
|
||||||
|
onClose={() => setIsDialogOpen(false)}
|
||||||
|
data={selectedRow}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -25,12 +25,14 @@
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"lucide-react": "^0.552.0",
|
"lucide-react": "^0.552.0",
|
||||||
"next": "16.0.1",
|
"next": "16.0.1",
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-apexcharts": "^1.8.0",
|
"react-apexcharts": "^1.8.0",
|
||||||
"react-day-picker": "^9.11.1",
|
"react-day-picker": "^9.11.1",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
|
"react-leaflet": "^4.2.1",
|
||||||
"recharts": "^3.8.1",
|
"recharts": "^3.8.1",
|
||||||
"tailwind-merge": "^3.3.1"
|
"tailwind-merge": "^3.3.1"
|
||||||
},
|
},
|
||||||
|
|
@ -1936,6 +1938,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
||||||
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="
|
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-leaflet/core": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"leaflet": "^1.9.0",
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@reduxjs/toolkit": {
|
"node_modules/@reduxjs/toolkit": {
|
||||||
"version": "2.11.2",
|
"version": "2.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz",
|
||||||
|
|
@ -2196,7 +2208,7 @@
|
||||||
"version": "19.2.2",
|
"version": "19.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
|
|
@ -2205,7 +2217,7 @@
|
||||||
"version": "19.2.2",
|
"version": "19.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
|
||||||
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
|
|
@ -2431,7 +2443,7 @@
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"devOptional": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/d3-array": {
|
"node_modules/d3-array": {
|
||||||
"version": "3.2.4",
|
"version": "3.2.4",
|
||||||
|
|
@ -2847,6 +2859,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/leaflet": {
|
||||||
|
"version": "1.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
|
||||||
|
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="
|
||||||
|
},
|
||||||
"node_modules/lightningcss": {
|
"node_modules/lightningcss": {
|
||||||
"version": "1.30.2",
|
"version": "1.30.2",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
||||||
|
|
@ -3169,6 +3186,19 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-leaflet": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-leaflet/core": "^2.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"leaflet": "^1.9.0",
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-redux": {
|
"node_modules/react-redux": {
|
||||||
"version": "9.2.0",
|
"version": "9.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,14 @@
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"lucide-react": "^0.552.0",
|
"lucide-react": "^0.552.0",
|
||||||
"next": "16.0.1",
|
"next": "16.0.1",
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-apexcharts": "^1.8.0",
|
"react-apexcharts": "^1.8.0",
|
||||||
"react-day-picker": "^9.11.1",
|
"react-day-picker": "^9.11.1",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
|
"react-leaflet": "^4.2.1",
|
||||||
"recharts": "^3.8.1",
|
"recharts": "^3.8.1",
|
||||||
"tailwind-merge": "^3.3.1"
|
"tailwind-merge": "^3.3.1"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1003 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 989 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 159 KiB |