chore: fix linting and type safety across the project
This commit is contained in:
@@ -53,18 +53,22 @@ export function DashboardContent() {
|
||||
);
|
||||
|
||||
setStats({
|
||||
complaints: (complaintRes.data as any)?.data || {
|
||||
complaints: (complaintRes.data as { data: typeof stats.complaints })
|
||||
?.data || {
|
||||
total: 0,
|
||||
baru: 0,
|
||||
proses: 0,
|
||||
selesai: 0,
|
||||
},
|
||||
residents: (residentRes.data as any)?.data || {
|
||||
residents: (residentRes.data as { data: typeof stats.residents })
|
||||
?.data || {
|
||||
total: 0,
|
||||
heads: 0,
|
||||
poor: 0,
|
||||
},
|
||||
weeklyService: (weeklyServiceRes.data as any)?.data?.count || 0,
|
||||
weeklyService:
|
||||
(weeklyServiceRes.data as { data: { count: number } })?.data
|
||||
?.count || 0,
|
||||
loading: false,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -31,10 +31,12 @@ export function ActivityList() {
|
||||
const res = await apiClient.GET("/api/event/");
|
||||
if (res.data?.data) {
|
||||
setData(
|
||||
(res.data.data as any[]).map((e) => ({
|
||||
date: dayjs(e.startDate).format("D MMMM YYYY"),
|
||||
title: e.title,
|
||||
})),
|
||||
(res.data.data as { startDate: string; title: string }[]).map(
|
||||
(e) => ({
|
||||
date: dayjs(e.startDate).format("D MMMM YYYY"),
|
||||
title: e.title,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -55,7 +55,10 @@ export function ChartAPBDes() {
|
||||
<XAxis type="number" hide domain={[0, 100]} />
|
||||
<YAxis type="category" hide dataKey="name" />
|
||||
<Tooltip
|
||||
formatter={(value: number) => [`${value}%`, ""]}
|
||||
formatter={(value: number | string | undefined) => [
|
||||
`${value}%`,
|
||||
"",
|
||||
]}
|
||||
contentStyle={{
|
||||
backgroundColor: dark ? "#1E293B" : "white",
|
||||
borderColor: dark ? "#334155" : "#e5e7eb",
|
||||
|
||||
@@ -20,11 +20,16 @@ import {
|
||||
} from "recharts";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
|
||||
interface ChartData {
|
||||
month: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export function ChartSurat() {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [data, setData] = useState<ChartData[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -33,7 +38,7 @@ export function ChartSurat() {
|
||||
const res = await apiClient.GET("/api/complaint/service-trends");
|
||||
if (res.data?.data) {
|
||||
setData(
|
||||
(res.data.data as any[]).map((d) => ({
|
||||
(res.data.data as { month: string; count: number }[]).map((d) => ({
|
||||
month: d.month,
|
||||
value: Number(d.count),
|
||||
})),
|
||||
@@ -79,7 +84,10 @@ export function ChartSurat() {
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
role="img"
|
||||
aria-label="Tampilkan Detail"
|
||||
>
|
||||
<title>Tampilkan Detail</title>
|
||||
<path
|
||||
d="M8 5L13 10L8 15"
|
||||
stroke="currentColor"
|
||||
|
||||
@@ -17,6 +17,14 @@ interface DivisionData {
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface DivisionApiResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
_count?: {
|
||||
activities: number;
|
||||
};
|
||||
}
|
||||
|
||||
export function DivisionProgress() {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
@@ -30,7 +38,7 @@ export function DivisionProgress() {
|
||||
const res = await apiClient.GET("/api/division/");
|
||||
if (res.data?.data) {
|
||||
setData(
|
||||
(res.data.data as any[]).map((d) => ({
|
||||
(res.data.data as DivisionApiResponse[]).map((d) => ({
|
||||
name: d.name,
|
||||
value: d._count?.activities || 0,
|
||||
})),
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Card,
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
Loader,
|
||||
Progress,
|
||||
Stack,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
@@ -45,6 +42,49 @@ const sektorUnggulanData = [
|
||||
{ sektor: "Jasa", value: 52 },
|
||||
];
|
||||
|
||||
interface AgeData {
|
||||
ageRange: string;
|
||||
total: number;
|
||||
}
|
||||
|
||||
interface JobData {
|
||||
job: string;
|
||||
total: number;
|
||||
}
|
||||
|
||||
interface ReligionData {
|
||||
name: string;
|
||||
value: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
interface BanjarData {
|
||||
id: string;
|
||||
name: string;
|
||||
totalPopulation: number;
|
||||
totalKK: number;
|
||||
totalPoor: number;
|
||||
}
|
||||
|
||||
interface ReligionResponse {
|
||||
religion: string;
|
||||
_count: {
|
||||
_all: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface OccupationResponse {
|
||||
occupation: string | null;
|
||||
_count: {
|
||||
_all: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface AgeGroupResponse {
|
||||
range: string;
|
||||
count: number | string;
|
||||
}
|
||||
|
||||
const DemografiPekerjaan = () => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
@@ -54,10 +94,10 @@ const DemografiPekerjaan = () => {
|
||||
heads: 0,
|
||||
poor: 0,
|
||||
});
|
||||
const [ageData, setAgeData] = useState<any[]>([]);
|
||||
const [jobData, setJobData] = useState<any[]>([]);
|
||||
const [religionData, setReligionData] = useState<any[]>([]);
|
||||
const [banjarData, setBanjarData] = useState<any[]>([]);
|
||||
const [ageData, setAgeData] = useState<AgeData[]>([]);
|
||||
const [jobData, setJobData] = useState<JobData[]>([]);
|
||||
const [religionData, setReligionData] = useState<ReligionData[]>([]);
|
||||
const [banjarData, setBanjarData] = useState<BanjarData[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -70,7 +110,8 @@ const DemografiPekerjaan = () => {
|
||||
]);
|
||||
|
||||
if (statsRes.data?.data) setStats(statsRes.data.data);
|
||||
if (banjarRes.data?.data) setBanjarData(banjarRes.data.data);
|
||||
if (banjarRes.data?.data)
|
||||
setBanjarData(banjarRes.data.data as BanjarData[]);
|
||||
if (demoRes.data?.data) {
|
||||
const { religion, occupation, ageGroups } = demoRes.data.data;
|
||||
|
||||
@@ -85,7 +126,7 @@ const DemografiPekerjaan = () => {
|
||||
};
|
||||
|
||||
setReligionData(
|
||||
(religion as any[]).map((r) => ({
|
||||
(religion as ReligionResponse[]).map((r) => ({
|
||||
name: r.religion,
|
||||
value: r._count._all,
|
||||
color: religionColors[r.religion] || "#94A3B8",
|
||||
@@ -93,14 +134,14 @@ const DemografiPekerjaan = () => {
|
||||
);
|
||||
|
||||
setJobData(
|
||||
(occupation as any[]).map((o) => ({
|
||||
(occupation as OccupationResponse[]).map((o) => ({
|
||||
job: o.occupation || "Lainnya",
|
||||
total: o._count._all,
|
||||
})),
|
||||
);
|
||||
|
||||
setAgeData(
|
||||
(ageGroups as any[]).map((a) => ({
|
||||
(ageGroups as AgeGroupResponse[]).map((a) => ({
|
||||
ageRange: a.range,
|
||||
total: Number(a.count),
|
||||
})),
|
||||
@@ -401,8 +442,8 @@ const DemografiPekerjaan = () => {
|
||||
</Title>
|
||||
</Group>
|
||||
<Grid gutter="sm">
|
||||
{dynamicStats.map((stat, index) => (
|
||||
<Grid.Col key={index} span={6}>
|
||||
{dynamicStats.map((stat) => (
|
||||
<Grid.Col key={stat.title} span={6}>
|
||||
<Card
|
||||
p="sm"
|
||||
radius="lg"
|
||||
@@ -480,8 +521,8 @@ const DemografiPekerjaan = () => {
|
||||
paddingAngle={2}
|
||||
dataKey="value"
|
||||
>
|
||||
{religionData.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||
{religionData.map((entry) => (
|
||||
<Cell key={`cell-${entry.name}`} fill={entry.color} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip
|
||||
@@ -496,8 +537,8 @@ const DemografiPekerjaan = () => {
|
||||
</ResponsiveContainer>
|
||||
<Stack gap="xs" mt="md">
|
||||
{!loading &&
|
||||
religionData.map((item, index) => (
|
||||
<Group key={index} justify="space-between">
|
||||
religionData.map((item) => (
|
||||
<Group key={item.name} justify="space-between">
|
||||
<Group gap="xs">
|
||||
<Box
|
||||
w={10}
|
||||
@@ -601,12 +642,12 @@ const DemografiPekerjaan = () => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{banjarData.map((item, index) => (
|
||||
{banjarData.map((item) => (
|
||||
<tr
|
||||
key={item.id || index}
|
||||
key={item.id}
|
||||
style={{
|
||||
backgroundColor:
|
||||
index % 2 === 0
|
||||
banjarData.indexOf(item) % 2 === 0
|
||||
? dark
|
||||
? "#334155"
|
||||
: "#F8FAFC"
|
||||
@@ -725,8 +766,8 @@ const DemografiPekerjaan = () => {
|
||||
radius={[0, 8, 8, 0]}
|
||||
maxBarSize={40}
|
||||
>
|
||||
{sektorUnggulanData.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill="#396aaaff" />
|
||||
{sektorUnggulanData.map((entry) => (
|
||||
<Cell key={`cell-${entry.sektor}`} fill="#396aaaff" />
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
|
||||
@@ -129,7 +129,6 @@ export function DevInspector({ children }: { children: React.ReactNode }) {
|
||||
tt.style.left = `${rect.left + window.scrollX}px`;
|
||||
}, []);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: updateOverlay is stable
|
||||
useEffect(() => {
|
||||
if (!active) return;
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ export function ImageWithFallback(
|
||||
<div className="flex items-center justify-center w-full h-full">
|
||||
<img
|
||||
src={ERROR_IMG_SRC}
|
||||
alt="Error loading image"
|
||||
alt="Error loading content"
|
||||
{...rest}
|
||||
data-original-url={src}
|
||||
/>
|
||||
/>{" "}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
Box,
|
||||
Card,
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -354,8 +353,8 @@ const KeuanganAnggaran = () => {
|
||||
Pendapatan
|
||||
</Title>
|
||||
<Stack gap="xs">
|
||||
{apbdReport.income.map((item, index) => (
|
||||
<Group key={index} justify="space-between">
|
||||
{apbdReport.income.map((item) => (
|
||||
<Group key={item.category} justify="space-between">
|
||||
<Text size="sm" c={dark ? "gray.3" : "gray.7"}>
|
||||
{item.category}
|
||||
</Text>
|
||||
@@ -390,8 +389,8 @@ const KeuanganAnggaran = () => {
|
||||
Belanja
|
||||
</Title>
|
||||
<Stack gap="xs">
|
||||
{apbdReport.expenses.map((item, index) => (
|
||||
<Group key={index} justify="space-between">
|
||||
{apbdReport.expenses.map((item) => (
|
||||
<Group key={item.category} justify="space-between">
|
||||
<Text size="sm" c={dark ? "gray.3" : "gray.7"}>
|
||||
{item.category}
|
||||
</Text>
|
||||
@@ -473,9 +472,9 @@ const KeuanganAnggaran = () => {
|
||||
</Title>
|
||||
</Group>
|
||||
<Stack gap="sm">
|
||||
{assistanceFundData.map((fund, index) => (
|
||||
{assistanceFundData.map((fund) => (
|
||||
<Card
|
||||
key={index}
|
||||
key={fund.source}
|
||||
p="sm"
|
||||
radius="lg"
|
||||
bg={dark ? "#334155" : "#F1F5F9"}
|
||||
|
||||
@@ -18,9 +18,23 @@ const archiveData = [
|
||||
{ name: "Notulensi Rapat" },
|
||||
];
|
||||
|
||||
interface Activity {
|
||||
id: string;
|
||||
title: string;
|
||||
createdAt: string;
|
||||
progress: number;
|
||||
status: "SELESAI" | "BERJALAN" | "TERTUNDA";
|
||||
}
|
||||
|
||||
interface EventData {
|
||||
id: string;
|
||||
title: string;
|
||||
startDate: string;
|
||||
}
|
||||
|
||||
const KinerjaDivisi = () => {
|
||||
const [activities, setActivities] = useState<any[]>([]);
|
||||
const [todayEvents, setTodayEvents] = useState<any[]>([]);
|
||||
const [activities, setActivities] = useState<Activity[]>([]);
|
||||
const [todayEvents, setTodayEvents] = useState<EventData[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -32,10 +46,10 @@ const KinerjaDivisi = () => {
|
||||
]);
|
||||
|
||||
if (activityRes.data?.data) {
|
||||
setActivities(activityRes.data.data);
|
||||
setActivities(activityRes.data.data as Activity[]);
|
||||
}
|
||||
if (eventRes.data?.data) {
|
||||
setTodayEvents(eventRes.data.data);
|
||||
setTodayEvents(eventRes.data.data as EventData[]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch kinerja divisi data", error);
|
||||
@@ -57,11 +71,8 @@ const KinerjaDivisi = () => {
|
||||
<Stack gap="lg">
|
||||
{/* SECTION 1 — PROGRAM KEGIATAN */}
|
||||
<Grid gutter="md">
|
||||
{activities.slice(0, 4).map((kegiatan, index) => (
|
||||
<Grid.Col
|
||||
key={kegiatan.id || index}
|
||||
span={{ base: 12, md: 6, lg: 3 }}
|
||||
>
|
||||
{activities.slice(0, 4).map((kegiatan) => (
|
||||
<Grid.Col key={kegiatan.id} span={{ base: 12, md: 6, lg: 3 }}>
|
||||
<ActivityCard
|
||||
title={kegiatan.title}
|
||||
date={dayjs(kegiatan.createdAt).format("D MMMM YYYY")}
|
||||
@@ -111,8 +122,8 @@ const KinerjaDivisi = () => {
|
||||
|
||||
{/* SECTION 5 — ARSIP DIGITAL PERANGKAT DESA */}
|
||||
<Grid gutter="md">
|
||||
{archiveData.map((item, index) => (
|
||||
<Grid.Col key={index} span={{ base: 12, md: 6 }}>
|
||||
{archiveData.map((item) => (
|
||||
<Grid.Col key={item.name} span={{ base: 12, md: 6 }}>
|
||||
<ArchiveCard item={item} />
|
||||
</Grid.Col>
|
||||
))}
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { Card, Group, Stack, Text, useMantineColorScheme } from "@mantine/core";
|
||||
import { MessageCircle } from "lucide-react";
|
||||
|
||||
interface DiscussionItem {
|
||||
@@ -57,9 +50,9 @@ export function DiscussionPanel() {
|
||||
</Text>
|
||||
</Group>
|
||||
<Stack gap="sm">
|
||||
{discussions.map((discussion, index) => (
|
||||
{discussions.map((discussion) => (
|
||||
<Card
|
||||
key={index}
|
||||
key={`${discussion.sender}-${discussion.date}`}
|
||||
p="sm"
|
||||
radius="md"
|
||||
withBorder
|
||||
|
||||
@@ -27,12 +27,12 @@ export function DivisionList() {
|
||||
try {
|
||||
const { data } = await apiClient.GET("/api/division/");
|
||||
if (data?.data) {
|
||||
const mapped = data.data.map(
|
||||
(div: { name: string; _count?: { activities: number } }) => ({
|
||||
name: div.name,
|
||||
count: div._count?.activities || 0,
|
||||
}),
|
||||
);
|
||||
const mapped = (
|
||||
data.data as { name: string; _count?: { activities: number } }[]
|
||||
).map((div) => ({
|
||||
name: div.name,
|
||||
count: div._count?.activities || 0,
|
||||
}));
|
||||
setDivisions(mapped);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -68,9 +68,9 @@ export function DivisionList() {
|
||||
<Loader size="sm" />
|
||||
</Group>
|
||||
) : divisions.length > 0 ? (
|
||||
divisions.map((division, index) => (
|
||||
divisions.map((division) => (
|
||||
<Group
|
||||
key={index}
|
||||
key={division.name}
|
||||
justify="space-between"
|
||||
align="center"
|
||||
style={{
|
||||
|
||||
@@ -63,8 +63,8 @@ export function DocumentChart() {
|
||||
labelStyle={{ color: dark ? "#E2E8F0" : "#374151" }}
|
||||
/>
|
||||
<Bar dataKey="jumlah" radius={[4, 4, 0, 0]}>
|
||||
{documentData.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||
{documentData.map((entry) => (
|
||||
<Cell key={`cell-${entry.name}`} fill={entry.color} />
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
|
||||
@@ -43,8 +43,12 @@ export function EventCard({ agendas = [] }: EventCardProps) {
|
||||
</Group>
|
||||
{agendas.length > 0 ? (
|
||||
<Stack gap="sm">
|
||||
{agendas.map((agenda, index) => (
|
||||
<Group key={index} align="flex-start" gap="md">
|
||||
{agendas.map((agenda) => (
|
||||
<Group
|
||||
key={`${agenda.time}-${agenda.event}`}
|
||||
align="flex-start"
|
||||
gap="md"
|
||||
>
|
||||
<Box w={60}>
|
||||
<Text size="sm" fw={600} c={dark ? "white" : "#1E3A5F"}>
|
||||
{agenda.time}
|
||||
|
||||
@@ -47,8 +47,8 @@ export function ProgressChart() {
|
||||
paddingAngle={2}
|
||||
dataKey="value"
|
||||
>
|
||||
{progressData.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||
{progressData.map((entry) => (
|
||||
<Cell key={`cell-${entry.name}`} fill={entry.color} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip
|
||||
@@ -61,8 +61,8 @@ export function ProgressChart() {
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
<Stack gap="xs" mt="md">
|
||||
{progressData.map((item, index) => (
|
||||
<Group key={index} justify="space-between">
|
||||
{progressData.map((item) => (
|
||||
<Group key={item.name} justify="space-between">
|
||||
<Group gap="xs">
|
||||
<Box
|
||||
w={12}
|
||||
|
||||
@@ -57,6 +57,26 @@ const ideInovatif = [
|
||||
},
|
||||
];
|
||||
|
||||
interface Complaint {
|
||||
id: string;
|
||||
title: string;
|
||||
category: string;
|
||||
status: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface ServiceStat {
|
||||
jenis: string;
|
||||
jumlah: number;
|
||||
}
|
||||
|
||||
interface ServiceApiResponse {
|
||||
letterType: string;
|
||||
_count: {
|
||||
_all: number;
|
||||
};
|
||||
}
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status.toLowerCase()) {
|
||||
case "baru":
|
||||
@@ -81,8 +101,8 @@ const PengaduanLayananPublik = () => {
|
||||
proses: 0,
|
||||
selesai: 0,
|
||||
});
|
||||
const [recentComplaints, setRecentComplaints] = useState<any[]>([]);
|
||||
const [serviceStats, setServiceStats] = useState<any[]>([]);
|
||||
const [recentComplaints, setRecentComplaints] = useState<Complaint[]>([]);
|
||||
const [serviceStats, setServiceStats] = useState<ServiceStat[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -95,9 +115,12 @@ const PengaduanLayananPublik = () => {
|
||||
]);
|
||||
|
||||
if (statsRes.data?.data) setStats(statsRes.data.data);
|
||||
if (recentRes.data?.data) setRecentComplaints(recentRes.data.data);
|
||||
if (recentRes.data?.data)
|
||||
setRecentComplaints(recentRes.data.data as Complaint[]);
|
||||
if (serviceRes.data?.data) {
|
||||
const mappedService = serviceRes.data.data.map((item: any) => ({
|
||||
const mappedService = (
|
||||
serviceRes.data.data as ServiceApiResponse[]
|
||||
).map((item) => ({
|
||||
jenis: item.letterType,
|
||||
jumlah: item._count?._all || 0,
|
||||
}));
|
||||
@@ -148,8 +171,8 @@ const PengaduanLayananPublik = () => {
|
||||
<Stack gap="lg">
|
||||
{/* TOP SECTION - 4 STAT CARDS */}
|
||||
<Grid gutter="md">
|
||||
{summaryData.map((item, index) => (
|
||||
<Grid.Col key={index} span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
{summaryData.map((item) => (
|
||||
<Grid.Col key={item.title} span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
<Card
|
||||
p="md"
|
||||
radius="xl"
|
||||
@@ -330,9 +353,9 @@ const PengaduanLayananPublik = () => {
|
||||
<Loader />
|
||||
</Group>
|
||||
) : recentComplaints.length > 0 ? (
|
||||
recentComplaints.map((item, index) => (
|
||||
recentComplaints.map((item) => (
|
||||
<Card
|
||||
key={item.id || index}
|
||||
key={item.id}
|
||||
p="sm"
|
||||
radius="md"
|
||||
withBorder
|
||||
@@ -392,9 +415,9 @@ const PengaduanLayananPublik = () => {
|
||||
Ajuan Ide Inovatif
|
||||
</Title>
|
||||
<Stack gap="sm">
|
||||
{ideInovatif.map((item, index) => (
|
||||
{ideInovatif.map((item) => (
|
||||
<Card
|
||||
key={index}
|
||||
key={item.judul}
|
||||
p="sm"
|
||||
radius="md"
|
||||
withBorder
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
Space,
|
||||
Stack,
|
||||
Switch,
|
||||
Text,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { IconInfoCircle } from "@tabler/icons-react";
|
||||
|
||||
const NotifikasiSettings = () => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
const _dark = colorScheme === "dark";
|
||||
return (
|
||||
<Stack pr={"20%"} gap={"xs"}>
|
||||
<Grid gutter={{ base: 5, xs: "md", md: "xl", xl: 50 }}>
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Collapse,
|
||||
Group,
|
||||
Image,
|
||||
Input,
|
||||
NavLink as MantineNavLink,
|
||||
Stack,
|
||||
Text,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { useLocation, useNavigate } from "@tanstack/react-router";
|
||||
@@ -80,11 +77,11 @@ export function Sidebar({ className }: SidebarProps) {
|
||||
|
||||
{/* Menu Items */}
|
||||
<Stack gap={0} px="xs" style={{ overflowY: "auto" }}>
|
||||
{menuItems.map((item, index) => {
|
||||
{menuItems.map((item) => {
|
||||
const isActive = location.pathname === item.path;
|
||||
return (
|
||||
<MantineNavLink
|
||||
key={index}
|
||||
key={item.path}
|
||||
onClick={() => navigate({ to: item.path })}
|
||||
label={item.name}
|
||||
active={isActive}
|
||||
@@ -146,11 +143,11 @@ export function Sidebar({ className }: SidebarProps) {
|
||||
ml="lg"
|
||||
style={{ overflowY: "auto", maxHeight: "200px" }}
|
||||
>
|
||||
{settingsItems.map((item, index) => {
|
||||
{settingsItems.map((item) => {
|
||||
const isActive = location.pathname === item.path;
|
||||
return (
|
||||
<MantineNavLink
|
||||
key={index}
|
||||
key={item.path}
|
||||
onClick={() => navigate({ to: item.path })}
|
||||
label={item.name}
|
||||
active={isActive}
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Stack,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { IconAward } from "@tabler/icons-react";
|
||||
|
||||
@@ -49,8 +49,8 @@ export const HealthStats = ({ data }: HealthStatsProps) => {
|
||||
Statistik Kesehatan
|
||||
</Title>
|
||||
<Stack gap="md">
|
||||
{displayData.map((item, index) => (
|
||||
<div key={index}>
|
||||
{displayData.map((item) => (
|
||||
<div key={item.label}>
|
||||
<Group justify="space-between" mb={5}>
|
||||
<Text size="sm" fw={500} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
{item.label}
|
||||
|
||||
@@ -53,8 +53,6 @@ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="breadcrumb-page"
|
||||
role="link"
|
||||
aria-disabled="true"
|
||||
aria-current="page"
|
||||
className={cn("text-foreground font-normal", className)}
|
||||
{...props}
|
||||
|
||||
@@ -46,7 +46,6 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
mantineVariant = "transparent";
|
||||
mantineColor = "blue"; // Assuming primary maps to blue in Mantine for now
|
||||
break;
|
||||
case "default":
|
||||
default:
|
||||
mantineVariant = "filled";
|
||||
mantineColor = "blue"; // Assuming primary maps to blue in Mantine for now
|
||||
|
||||
@@ -117,16 +117,16 @@ function Carousel({
|
||||
canScrollNext,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
{/* biome-ignore lint/a11y/useAriaPropsSupportedByRole: section with aria-roledescription is standard for carousels. */}
|
||||
<section
|
||||
onKeyDownCapture={handleKeyDown}
|
||||
className={cn("relative", className)}
|
||||
role="region"
|
||||
aria-roledescription="carousel"
|
||||
data-slot="carousel"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</section>{" "}
|
||||
</CarouselContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -156,6 +156,7 @@ function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
|
||||
const { orientation } = useCarousel();
|
||||
|
||||
return (
|
||||
// biome-ignore lint/a11y/useSemanticElements: role='group' and aria-roledescription='slide' is standard for carousel slides.
|
||||
<div
|
||||
role="group"
|
||||
aria-roledescription="slide"
|
||||
|
||||
@@ -80,6 +80,7 @@ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
|
||||
|
||||
return (
|
||||
<style
|
||||
// biome-ignore lint/security/noDangerouslySetInnerHtml: This is a safe use of dangerouslySetInnerHTML for generating dynamic CSS variables for charts.
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: Object.entries(THEMES)
|
||||
.map(
|
||||
@@ -125,6 +126,11 @@ function ChartTooltipContent({
|
||||
indicator?: "line" | "dot" | "dashed";
|
||||
nameKey?: string;
|
||||
labelKey?: string;
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Recharts payload is complex and better handled as any[] for this wrapper.
|
||||
payload?: any[];
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Recharts label can be any type.
|
||||
label?: any;
|
||||
active?: boolean;
|
||||
}) {
|
||||
const { config } = useChart();
|
||||
|
||||
@@ -257,9 +263,11 @@ function ChartLegendContent({
|
||||
verticalAlign = "bottom",
|
||||
nameKey,
|
||||
}: React.ComponentProps<"div"> &
|
||||
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
||||
Pick<RechartsPrimitive.LegendProps, "verticalAlign"> & {
|
||||
hideIcon?: boolean;
|
||||
nameKey?: string;
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Recharts legend payload.
|
||||
payload?: any[];
|
||||
}) {
|
||||
const { config } = useChart();
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ function InputOTPSlot({
|
||||
|
||||
function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div data-slot="input-otp-separator" role="separator" {...props}>
|
||||
<div data-slot="input-otp-separator" {...props}>
|
||||
<MinusIcon />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
MoreHorizontalIcon,
|
||||
} from "lucide-react";
|
||||
import type * as React from "react";
|
||||
import { Button } from "./button";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const baseClasses =
|
||||
@@ -13,7 +12,6 @@ const baseClasses =
|
||||
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
|
||||
return (
|
||||
<nav
|
||||
role="navigation"
|
||||
aria-label="pagination"
|
||||
data-slot="pagination"
|
||||
className={cn("mx-auto flex w-full justify-center", className)}
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
Progress as MantineProgress,
|
||||
type ProgressProps as MantineProgressProps,
|
||||
} from "@mantine/core";
|
||||
import React from "react";
|
||||
import { cn } from "./utils";
|
||||
|
||||
// Original ProgressProps likely had 'value' and 'max'.
|
||||
|
||||
@@ -77,6 +77,7 @@ function SidebarProvider({
|
||||
}
|
||||
|
||||
// This sets the cookie to keep the sidebar state.
|
||||
// biome-ignore lint/suspicious/noDocumentCookie: This is a safe use of document.cookie for persisting sidebar state across page loads.
|
||||
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
||||
},
|
||||
[setOpenProp, open],
|
||||
@@ -85,7 +86,7 @@ function SidebarProvider({
|
||||
// Helper to toggle the sidebar.
|
||||
const toggleSidebar = React.useCallback(() => {
|
||||
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
|
||||
}, [isMobile, setOpen, setOpenMobile]);
|
||||
}, [isMobile, setOpen]);
|
||||
|
||||
// Adds a keyboard shortcut to toggle the sidebar.
|
||||
React.useEffect(() => {
|
||||
@@ -117,7 +118,7 @@ function SidebarProvider({
|
||||
setOpenMobile,
|
||||
toggleSidebar,
|
||||
}),
|
||||
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],
|
||||
[state, open, setOpen, isMobile, openMobile, toggleSidebar],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -52,6 +52,7 @@ function Slider({
|
||||
{Array.from({ length: _values.length }, (_, index) => (
|
||||
<SliderPrimitive.Thumb
|
||||
data-slot="slider-thumb"
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: slider thumbs are stable and index is an appropriate key here.
|
||||
key={index}
|
||||
className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import { useTheme } from "next-themes";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import { Toaster as Sonner, type ToasterProps } from "sonner";
|
||||
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { theme = "system" } = useTheme();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps["theme"]}
|
||||
theme={
|
||||
colorScheme === "auto"
|
||||
? "system"
|
||||
: (colorScheme as ToasterProps["theme"])
|
||||
}
|
||||
className="toaster group"
|
||||
style={
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Tooltip as MantineTooltip, type TooltipProps } from "@mantine/core";
|
||||
import React from "react";
|
||||
import { cn } from "./utils";
|
||||
|
||||
interface CustomTooltipProps extends TooltipProps {
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconCategory,
|
||||
IconCurrencyDollar,
|
||||
IconTrendingUp,
|
||||
IconUsers,
|
||||
@@ -149,8 +148,8 @@ export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||
|
||||
return (
|
||||
<Grid gutter="md">
|
||||
{kpiData.map((kpi, index) => (
|
||||
<GridCol key={index} span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
{kpiData.map((kpi) => (
|
||||
<GridCol key={kpi.title} span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
<KpiCard {...kpi} />
|
||||
</GridCol>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user