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;
|
||||
onClose: () => void;
|
||||
data?: {
|
||||
deskripsi: string;
|
||||
durasi: string;
|
||||
media: string;
|
||||
tujuan: string;
|
||||
|
|
@ -46,6 +47,11 @@ export default function DialogCampaignDetail({
|
|||
|
||||
{/* Detail Info */}
|
||||
<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>
|
||||
<span className="text-gray-500">Durasi</span>
|
||||
<br />
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ 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);
|
||||
|
|
@ -34,6 +36,10 @@ export default function FormCampaign() {
|
|||
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",
|
||||
|
|
@ -44,10 +50,13 @@ export default function FormCampaign() {
|
|||
];
|
||||
|
||||
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) => {
|
||||
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
|
||||
const simulateUpload = (
|
||||
fileList: { file: File; progress: number; uploaded: boolean }[]
|
||||
fileList: { file: File; progress: number; uploaded: boolean }[],
|
||||
) => {
|
||||
fileList.forEach((fileObj) => {
|
||||
let progress = 0;
|
||||
|
|
@ -83,8 +92,8 @@ export default function FormCampaign() {
|
|||
prev.map((f) =>
|
||||
f.file === fileObj.file
|
||||
? { ...f, progress, uploaded: progress >= 100 }
|
||||
: f
|
||||
)
|
||||
: f,
|
||||
),
|
||||
);
|
||||
if (progress >= 100) clearInterval(interval);
|
||||
}, 300);
|
||||
|
|
@ -214,17 +223,11 @@ export default function FormCampaign() {
|
|||
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>
|
||||
<div className="mt-4 space-y-4">
|
||||
{/* Map tampil */}
|
||||
<MapVideotron />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 🧩 Komponen DialogMediaOnline dipanggil di sini */}
|
||||
|
|
@ -325,10 +328,9 @@ export default function FormCampaign() {
|
|||
</RadioGroup>
|
||||
|
||||
{available === "Yes" ? (
|
||||
// ✅ Jika user pilih "Yes" → tampil upload file
|
||||
// ✅ Upload
|
||||
<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"
|
||||
|
|
@ -338,15 +340,320 @@ export default function FormCampaign() {
|
|||
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"
|
||||
// ❌ 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>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import { useRouter } from "next/navigation";
|
|||
import Footer from "../landing-page/footer";
|
||||
import Navbar from "../landing-page/navbar";
|
||||
|
||||
// ✅ Dummy user data
|
||||
const users = [
|
||||
{
|
||||
nrp: "1001",
|
||||
|
|
@ -26,14 +25,19 @@ const users = [
|
|||
},
|
||||
{
|
||||
nrp: "1003",
|
||||
password: "super123",
|
||||
role: "supervisor",
|
||||
password: "executive123",
|
||||
role: "executive",
|
||||
},
|
||||
{
|
||||
nrp: "1004",
|
||||
password: "approve123",
|
||||
role: "approver",
|
||||
},
|
||||
{
|
||||
nrp: "1005",
|
||||
password: "coordinator123",
|
||||
role: "kordinator",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Login() {
|
||||
|
|
@ -66,12 +70,15 @@ export default function Login() {
|
|||
case "user":
|
||||
router.push("/dashboard/user");
|
||||
break;
|
||||
case "supervisor":
|
||||
case "executive":
|
||||
router.push("/dashboard/supervisor");
|
||||
break;
|
||||
case "approver":
|
||||
router.push("/dashboard/approver");
|
||||
break;
|
||||
case "kordinator":
|
||||
router.push("/dashboard/coordinator");
|
||||
break;
|
||||
default:
|
||||
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",
|
||||
"date-fns": "^4.1.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"leaflet": "^1.9.4",
|
||||
"lucide-react": "^0.552.0",
|
||||
"next": "16.0.1",
|
||||
"react": "19.2.0",
|
||||
"react-apexcharts": "^1.8.0",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "19.2.0",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"recharts": "^3.8.1",
|
||||
"tailwind-merge": "^3.3.1"
|
||||
},
|
||||
|
|
@ -1936,6 +1938,16 @@
|
|||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
||||
"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": {
|
||||
"version": "2.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz",
|
||||
|
|
@ -2196,7 +2208,7 @@
|
|||
"version": "19.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
|
|
@ -2205,7 +2217,7 @@
|
|||
"version": "19.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
|
||||
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
|
|
@ -2431,7 +2443,7 @@
|
|||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
|
|
@ -2847,6 +2859,11 @@
|
|||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"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": {
|
||||
"version": "1.30.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||
|
|
|
|||
|
|
@ -25,12 +25,14 @@
|
|||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"leaflet": "^1.9.4",
|
||||
"lucide-react": "^0.552.0",
|
||||
"next": "16.0.1",
|
||||
"react": "19.2.0",
|
||||
"react-apexcharts": "^1.8.0",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "19.2.0",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"recharts": "^3.8.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 |