Compare commits
6 Commits
nico/25-ma
...
nico/25-ma
| Author | SHA1 | Date | |
|---|---|---|---|
| 519a14adaa | |||
| 366c08fbaa | |||
| 5c09e7a0be | |||
| 7c8012d277 | |||
| 687ce11a81 | |||
| 1ba4643e23 |
@@ -155,7 +155,7 @@ const HelpPage = () => {
|
||||
{stats.map((stat, index) => (
|
||||
<HelpCard
|
||||
key={index}
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
p="lg"
|
||||
style={{
|
||||
textAlign: "center",
|
||||
@@ -180,7 +180,7 @@ const HelpPage = () => {
|
||||
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
||||
<HelpCard
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
icon={<IconBook size={24} color="white" />}
|
||||
title="Panduan Memulai"
|
||||
h="100%"
|
||||
@@ -210,7 +210,7 @@ const HelpPage = () => {
|
||||
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
||||
<HelpCard
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
icon={<IconVideo size={24} color="white" />}
|
||||
title="Video Tutorial"
|
||||
h="100%"
|
||||
@@ -240,7 +240,7 @@ const HelpPage = () => {
|
||||
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
||||
<HelpCard
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
icon={<IconHelpCircle size={24} color="white" />}
|
||||
title="FAQ"
|
||||
h="100%"
|
||||
@@ -272,7 +272,7 @@ const HelpPage = () => {
|
||||
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
||||
<HelpCard
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
icon={<IconHeadphones size={24} color="white" />}
|
||||
title="Hubungi Support"
|
||||
h="100%"
|
||||
@@ -307,7 +307,7 @@ const HelpPage = () => {
|
||||
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
||||
<HelpCard
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
icon={<IconFileText size={24} color="white" />}
|
||||
title="Dokumentasi"
|
||||
h="100%"
|
||||
@@ -339,7 +339,7 @@ const HelpPage = () => {
|
||||
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
||||
<HelpCard
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
icon={<IconMessage size={24} color="white" />}
|
||||
title="Jenna - Virtual Assistant"
|
||||
h="100%"
|
||||
|
||||
@@ -5,22 +5,18 @@ import {
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
List,
|
||||
Stack,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
useMantineColorScheme
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconAlertTriangle,
|
||||
IconCamera,
|
||||
IconClock,
|
||||
IconEye,
|
||||
IconMapPin,
|
||||
IconShieldLock,
|
||||
IconMapPin
|
||||
} from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
|
||||
const KeamananPage = () => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
@@ -118,15 +114,6 @@ const KeamananPage = () => {
|
||||
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
{/* Page Header */}
|
||||
<Group justify="space-between" align="center">
|
||||
<Title order={2} c={dark ? "dark.0" : "black"}>
|
||||
Keamanan Lingkungan Desa
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
|
||||
|
||||
<Grid gutter="md">
|
||||
{/* Peta Keamanan CCTV */}
|
||||
<GridCol span={{ base: 12, lg: 6 }}>
|
||||
@@ -139,7 +126,7 @@ const KeamananPage = () => {
|
||||
p="md"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
h="100%"
|
||||
>
|
||||
@@ -149,10 +136,14 @@ const KeamananPage = () => {
|
||||
{kpi.subtitle}
|
||||
</Text>
|
||||
<Group gap="xs" align="center">
|
||||
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||
<Text
|
||||
size="xl"
|
||||
fw={700}
|
||||
c={dark ? "dark.0" : "black"}
|
||||
>
|
||||
{kpi.value}
|
||||
</Text>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"}>
|
||||
{kpi.title}
|
||||
</Text>
|
||||
</Group>
|
||||
@@ -174,14 +165,14 @@ const KeamananPage = () => {
|
||||
p="md"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
h="100%"
|
||||
>
|
||||
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>
|
||||
Peta Keamanan CCTV
|
||||
</Title>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"} mb="md">
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"} mb="md">
|
||||
Titik Lokasi CCTV
|
||||
</Text>
|
||||
|
||||
@@ -237,13 +228,13 @@ const KeamananPage = () => {
|
||||
{cctv.status === "active" ? "Online" : "Offline"}
|
||||
</Badge>
|
||||
</Group>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"}>
|
||||
{cctv.location}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Group gap="xs">
|
||||
<IconClock size={16} stroke={1.5} />
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"}>
|
||||
{cctv.lastSeen}
|
||||
</Text>
|
||||
</Group>
|
||||
@@ -261,14 +252,10 @@ const KeamananPage = () => {
|
||||
p="md"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
h="100%"
|
||||
>
|
||||
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>
|
||||
Laporan Keamanan Lingkungan
|
||||
</Title>
|
||||
|
||||
<Stack gap="sm">
|
||||
{securityReports.map((report, index) => (
|
||||
<Card
|
||||
@@ -300,19 +287,19 @@ const KeamananPage = () => {
|
||||
<Group justify="space-between">
|
||||
<Group gap="xs">
|
||||
<IconMapPin size={16} stroke={1.5} />
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"}>
|
||||
{report.location}
|
||||
</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconClock size={16} stroke={1.5} />
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"}>
|
||||
{report.reportedAt}
|
||||
</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"} mt="sm">
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"} mt="sm">
|
||||
{report.date}
|
||||
</Text>
|
||||
</Card>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Card, Group, Progress, Text } from "@mantine/core";
|
||||
import { Box, Card, Group, Progress, Text, useMantineColorScheme } from "@mantine/core";
|
||||
|
||||
interface ActivityCardProps {
|
||||
title: string;
|
||||
@@ -26,13 +26,16 @@ export function ActivityCard({
|
||||
}
|
||||
};
|
||||
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
|
||||
return (
|
||||
<Card
|
||||
radius="xl"
|
||||
p={0}
|
||||
withBorder={false}
|
||||
style={{
|
||||
backgroundColor: "#F3F4F6",
|
||||
backgroundColor: dark ? "#334155" : "white",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,189 +1,78 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
Modal,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconEdit,
|
||||
IconInfoCircle,
|
||||
IconTrash,
|
||||
IconUser,
|
||||
IconUserPlus,
|
||||
} from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import { Box, Button, Group, Stack, Switch, Text, Title } from "@mantine/core";
|
||||
|
||||
const AksesDanTimSettings = () => {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
|
||||
// Sample team members data
|
||||
const teamMembers = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Admin Utama",
|
||||
email: "admin@desa.go.id",
|
||||
role: "Administrator",
|
||||
status: "Aktif",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Operator Desa",
|
||||
email: "operator@desa.go.id",
|
||||
role: "Operator",
|
||||
status: "Aktif",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Staff Keuangan",
|
||||
email: "keuangan@desa.go.id",
|
||||
role: "Keuangan",
|
||||
status: "Aktif",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Staff Umum",
|
||||
email: "umum@desa.go.id",
|
||||
role: "Umum",
|
||||
status: "Nonaktif",
|
||||
},
|
||||
];
|
||||
|
||||
const roles = [
|
||||
{ value: "administrator", label: "Administrator" },
|
||||
{ value: "operator", label: "Operator" },
|
||||
{ value: "keuangan", label: "Keuangan" },
|
||||
{ value: "umum", label: "Umum" },
|
||||
{ value: "keamanan", label: "Keamanan" },
|
||||
];
|
||||
|
||||
return (
|
||||
<Card
|
||||
withBorder
|
||||
radius="md"
|
||||
p="xl"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
>
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={() => setOpened(false)}
|
||||
title="Tambah Anggota Tim"
|
||||
size="lg"
|
||||
>
|
||||
<TextInput
|
||||
label="Nama Lengkap"
|
||||
placeholder="Masukkan nama lengkap anggota tim"
|
||||
mb="md"
|
||||
/>
|
||||
<TextInput
|
||||
label="Alamat Email"
|
||||
placeholder="Masukkan alamat email"
|
||||
mb="md"
|
||||
/>
|
||||
<Select
|
||||
label="Peran"
|
||||
placeholder="Pilih peran anggota tim"
|
||||
data={roles}
|
||||
mb="md"
|
||||
/>
|
||||
<Group justify="flex-end" mt="xl">
|
||||
<Button variant="outline" onClick={() => setOpened(false)}>
|
||||
Batal
|
||||
<Stack pr={"50%"} gap={"xl"}>
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={2}>Manajemen Tim</Title>
|
||||
<Button bg={"#1E3A5F"} radius={"md"} c={"white"} fullWidth>
|
||||
Undangan Anggota Baru
|
||||
</Button>
|
||||
<Button>Undang Anggota</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
|
||||
<Title order={2} mb="lg">
|
||||
Akses & Tim
|
||||
</Title>
|
||||
<Text color="dimmed" mb="xl">
|
||||
Kelola akses dan anggota tim Anda
|
||||
</Text>
|
||||
|
||||
<Space h="lg" />
|
||||
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Anggota Tim</Title>
|
||||
<Button
|
||||
leftSection={<IconUserPlus size={16} />}
|
||||
onClick={() => setOpened(true)}
|
||||
>
|
||||
Tambah Anggota
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
<Table highlightOnHover>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Nama</Table.Th>
|
||||
<Table.Th>Email</Table.Th>
|
||||
<Table.Th>Peran</Table.Th>
|
||||
<Table.Th>Status</Table.Th>
|
||||
<Table.Th>Aksi</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{teamMembers.map((member) => (
|
||||
<Table.Tr key={member.id}>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
<IconUser size={20} />
|
||||
<Text>{member.name}</Text>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
<Table.Td>{member.email}</Table.Td>
|
||||
<Table.Td>
|
||||
<Text fw={500}>{member.role}</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text c={member.status === "Aktif" ? "green" : "red"} fw={500}>
|
||||
{member.status}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Group>
|
||||
<ActionIcon variant="subtle" color="blue">
|
||||
<IconEdit size={16} />
|
||||
</ActionIcon>
|
||||
<ActionIcon variant="subtle" color="red">
|
||||
<IconTrash size={16} />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
|
||||
<Space h="xl" />
|
||||
|
||||
<Alert
|
||||
icon={<IconInfoCircle size={16} />}
|
||||
title="Informasi"
|
||||
color="blue"
|
||||
mb="md"
|
||||
>
|
||||
Administrator memiliki akses penuh ke semua fitur. Peran lainnya
|
||||
memiliki akses terbatas sesuai kebutuhan.
|
||||
</Alert>
|
||||
|
||||
<Group justify="flex-end" mt="xl">
|
||||
<Button bg={"#1E3A5F"} radius={"md"} c={"white"} fullWidth>
|
||||
Kelola Role & Permission
|
||||
</Button>
|
||||
<Group justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Daftar Anggota Teraktif
|
||||
</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
12 Anggota
|
||||
</Text>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={2}>Hak Akses</Title>
|
||||
<Group justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Administrator
|
||||
</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
2 Orang
|
||||
</Text>
|
||||
</Group>
|
||||
<Group justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Editor
|
||||
</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
5 Orang
|
||||
</Text>
|
||||
</Group>
|
||||
<Group justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Viewer
|
||||
</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
5 Orang
|
||||
</Text>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={2}>Kolaborasi</Title>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Izin Export Data
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Require Approval Untuk Perubahan
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Group justify="flex-start" mt="xl">
|
||||
<Button variant="outline">Batal</Button>
|
||||
<Button>Simpan Perubahan</Button>
|
||||
</Group>
|
||||
</Card>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,89 +1,64 @@
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
PasswordInput,
|
||||
Space,
|
||||
Switch,
|
||||
Text,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { IconInfoCircle, IconLock } from "@tabler/icons-react";
|
||||
import { Box, Button, Group, Stack, Switch, Text, Title } from "@mantine/core";
|
||||
|
||||
const KeamananSettings = () => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
return (
|
||||
<Card
|
||||
withBorder
|
||||
radius="md"
|
||||
p="xl"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
>
|
||||
<Title order={2} mb="lg">
|
||||
Pengaturan Keamanan
|
||||
</Title>
|
||||
<Text color="dimmed" mb="xl">
|
||||
Kelola keamanan akun Anda
|
||||
</Text>
|
||||
|
||||
<Space h="lg" />
|
||||
|
||||
<PasswordInput
|
||||
label="Kata Sandi Saat Ini"
|
||||
placeholder="Masukkan kata sandi saat ini"
|
||||
mb="md"
|
||||
/>
|
||||
|
||||
<PasswordInput
|
||||
label="Kata Sandi Baru"
|
||||
placeholder="Masukkan kata sandi baru"
|
||||
mb="md"
|
||||
/>
|
||||
|
||||
<PasswordInput
|
||||
label="Konfirmasi Kata Sandi Baru"
|
||||
placeholder="Konfirmasi kata sandi baru"
|
||||
mb="md"
|
||||
/>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
<Group mb="md">
|
||||
<Switch label="Verifikasi Dua Langkah" />
|
||||
<Switch label="Login Otentikasi Aplikasi" />
|
||||
</Group>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
<Alert
|
||||
icon={<IconLock size={16} />}
|
||||
title="Keamanan"
|
||||
color="orange"
|
||||
mb="md"
|
||||
>
|
||||
Gunakan kata sandi yang kuat dan unik. Hindari menggunakan kata sandi
|
||||
yang sama di banyak layanan.
|
||||
</Alert>
|
||||
|
||||
<Alert
|
||||
icon={<IconInfoCircle size={16} />}
|
||||
title="Informasi"
|
||||
color="blue"
|
||||
mb="md"
|
||||
>
|
||||
Setelah mengganti kata sandi, Anda akan diminta logout dari semua
|
||||
perangkat.
|
||||
</Alert>
|
||||
|
||||
<Group justify="flex-end" mt="xl">
|
||||
<Stack pr={"50%"} gap={"xl"}>
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={2}>Autentikasi</Title>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Two-Factor Authentication
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Biometrik Login
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
IP Whitelist
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={2}>Password</Title>
|
||||
<Button bg={"#1E3A5F"} radius={"md"} c={"white"} fullWidth>
|
||||
Ubah Password
|
||||
</Button>
|
||||
<Button bg={"#1E3A5F"} radius={"md"} c={"white"} fullWidth>
|
||||
Riwayat Login
|
||||
</Button>
|
||||
<Button bg={"#1E3A5F"} radius={"md"} c={"white"} fullWidth>
|
||||
Perangkat Terdaftar
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={2}>Audit & Log</Title>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Log Aktivitas
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Button bg={"#1E3A5F"} radius={"md"} c={"white"} fullWidth>
|
||||
Download Log
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Group justify="flex-start" mt="xl">
|
||||
<Button variant="outline">Batal</Button>
|
||||
<Button>Perbarui Kata Sandi</Button>
|
||||
<Button>Simpan Perubahan</Button>
|
||||
</Group>
|
||||
</Card>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
Space,
|
||||
Stack,
|
||||
Switch,
|
||||
Text,
|
||||
Title,
|
||||
@@ -16,70 +20,101 @@ const NotifikasiSettings = () => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
return (
|
||||
<Card
|
||||
withBorder
|
||||
radius="md"
|
||||
p="xl"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
>
|
||||
<Title order={2} mb="lg">
|
||||
Pengaturan Notifikasi
|
||||
</Title>
|
||||
<Text color="dimmed" mb="xl">
|
||||
Kelola preferensi notifikasi Anda
|
||||
</Text>
|
||||
|
||||
<Space h="lg" />
|
||||
|
||||
<Checkbox.Group defaultValue={["email", "push"]} mb="md">
|
||||
<Title order={4} mb="sm">
|
||||
Metode Notifikasi
|
||||
</Title>
|
||||
<Group>
|
||||
<Checkbox value="email" label="Email" />
|
||||
<Checkbox value="push" label="Notifikasi Push" />
|
||||
<Checkbox value="sms" label="SMS" />
|
||||
</Group>
|
||||
</Checkbox.Group>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
<Group mb="md">
|
||||
<Switch label="Notifikasi Email" defaultChecked />
|
||||
<Switch label="Notifikasi Push" defaultChecked />
|
||||
</Group>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
<Title order={4} mb="sm">
|
||||
Jenis Notifikasi
|
||||
</Title>
|
||||
<Group align="start">
|
||||
<Switch label="Pengaduan Baru" defaultChecked />
|
||||
<Switch label="Update Status Pengaduan" defaultChecked />
|
||||
<Switch label="Laporan Mingguan" />
|
||||
<Switch label="Pemberitahuan Keamanan" defaultChecked />
|
||||
<Switch label="Aktivitas Akun" defaultChecked />
|
||||
</Group>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
<Alert
|
||||
icon={<IconInfoCircle size={16} />}
|
||||
title="Tip"
|
||||
color="blue"
|
||||
mb="md"
|
||||
>
|
||||
Anda dapat menyesuaikan frekuensi notifikasi mingguan sesuai kebutuhan
|
||||
Anda.
|
||||
</Alert>
|
||||
|
||||
<Group justify="flex-end" mt="xl">
|
||||
<Stack pr={"20%"} gap={"xs"}>
|
||||
<Grid gutter={{ base: 5, xs: "md", md: "xl", xl: 50 }}>
|
||||
<GridCol span={6}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3} mb="sm">
|
||||
Metode Notifikasi
|
||||
</Title>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Laporan Harian
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Alert Sistem
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Update Keamanan
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Newsletter Bulanan
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
</Stack>
|
||||
</GridCol>
|
||||
<GridCol span={6}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3} mb="sm">
|
||||
Preferensi Alert
|
||||
</Title>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Treshold Memori
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Treshold CPU
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Treshold Disk
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
</Stack>
|
||||
</GridCol>
|
||||
<GridCol span={6}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3} mb="sm">
|
||||
Notifikasi Push
|
||||
</Title>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Alert Kritis
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Aktivitas Tim
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Komentar & Mention
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Bunyi Notifikasi
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
</Stack>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Group justify="flex-start" mt="xl">
|
||||
<Button variant="outline">Batal</Button>
|
||||
<Button>Simpan Preferensi</Button>
|
||||
</Group>
|
||||
</Card>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,44 +1,12 @@
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { IconInfoCircle } from "@tabler/icons-react";
|
||||
import { Box, Button, Group, Select, Switch, Text, Title } from "@mantine/core";
|
||||
import { DateInput } from "@mantine/dates";
|
||||
|
||||
const UmumSettings = () => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
return (
|
||||
<Card
|
||||
withBorder
|
||||
radius="md"
|
||||
p="xl"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
>
|
||||
<Box pr={"50%"}>
|
||||
<Title order={2} mb="lg">
|
||||
Pengaturan Umum
|
||||
Preferensi Tampilan
|
||||
</Title>
|
||||
<Text color="dimmed" mb="xl">
|
||||
Kelola pengaturan umum aplikasi Anda
|
||||
</Text>
|
||||
|
||||
<Space h="lg" />
|
||||
|
||||
<TextInput
|
||||
label="Nama Aplikasi"
|
||||
placeholder="Masukkan nama aplikasi"
|
||||
defaultValue="Dashboard Desa Plus"
|
||||
mb="md"
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="Bahasa Aplikasi"
|
||||
@@ -61,25 +29,53 @@ const UmumSettings = () => {
|
||||
mb="md"
|
||||
/>
|
||||
|
||||
<Group mb="md">
|
||||
<Switch label="Notifikasi Email" defaultChecked />
|
||||
<DateInput label="Format Tanggal" mb={"xl"} />
|
||||
|
||||
<Title order={2} mb="lg">
|
||||
Dashboard
|
||||
</Title>
|
||||
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Refresh Otomatis
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
|
||||
<Alert
|
||||
icon={<IconInfoCircle size={16} />}
|
||||
title="Informasi"
|
||||
color="blue"
|
||||
mb="md"
|
||||
>
|
||||
Beberapa pengaturan mungkin memerlukan restart aplikasi untuk diterapkan
|
||||
sepenuhnya.
|
||||
</Alert>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Interval Refresh
|
||||
</Text>
|
||||
<Select
|
||||
data={[
|
||||
{ value: "1", label: "30d" },
|
||||
{ value: "2", label: "60d" },
|
||||
{ value: "3", label: "90d" },
|
||||
]}
|
||||
defaultValue="1"
|
||||
w={90}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Tampilkan Grid
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
|
||||
<Group mb="md" justify="space-between">
|
||||
<Text fw={"bold"} fz={"sm"}>
|
||||
Animasi Transisi
|
||||
</Text>
|
||||
<Switch defaultChecked />
|
||||
</Group>
|
||||
|
||||
<Group justify="flex-end" mt="xl">
|
||||
<Button variant="outline">Batal</Button>
|
||||
<Button>Simpan Perubahan</Button>
|
||||
</Group>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Grid, GridCol, Stack } from "@mantine/core";
|
||||
import { SummaryCards } from "./sosial/summary-cards";
|
||||
import { HealthStats } from "./sosial/health-stats";
|
||||
import { PosyanduSchedule } from "./sosial/posyandu-schedule";
|
||||
import { Pendidikan } from "./sosial/pendidikan";
|
||||
import { Beasiswa } from "./sosial/beasiswa";
|
||||
import { EventCalendar } from "./sosial/event-calendar";
|
||||
import { HealthStats } from "./sosial/health-stats";
|
||||
import { Pendidikan } from "./sosial/pendidikan";
|
||||
import { PosyanduSchedule } from "./sosial/posyandu-schedule";
|
||||
import { SummaryCards } from "./sosial/summary-cards";
|
||||
|
||||
const SosialPage = () => {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { Card, Group, Stack, Text, ThemeIcon, Title } from "@mantine/core";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import {
|
||||
Card,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { IconAward } from "@tabler/icons-react";
|
||||
|
||||
interface ScholarshipData {
|
||||
@@ -30,16 +37,16 @@ export const Beasiswa = ({ data }: BeasiswaProps) => {
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
h={"100%"}
|
||||
>
|
||||
<Group justify="space-between" align="center">
|
||||
<Stack gap={2}>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"} fw={500}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"} fw={500}>
|
||||
Beasiswa Desa
|
||||
</Text>
|
||||
<Text size="xl" fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
<Text size="xl" fw={700} c={dark ? "white" : "#1e3a5f"}>
|
||||
Penerima: {displayData.penerima}
|
||||
</Text>
|
||||
</Stack>
|
||||
@@ -54,14 +61,14 @@ export const Beasiswa = ({ data }: BeasiswaProps) => {
|
||||
</Group>
|
||||
<Stack gap="xs" mt="md">
|
||||
<Group justify="space-between">
|
||||
<Text c={dark ? "dark.3" : "dimmed"}>Dana Tersalurkan:</Text>
|
||||
<Text fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
<Text c={dark ? "white" : "dimmed"}>Dana Tersalurkan:</Text>
|
||||
<Text fw={700} c={dark ? "white" : "#1e3a5f"}>
|
||||
{displayData.dana}
|
||||
</Text>
|
||||
</Group>
|
||||
<Group justify="space-between">
|
||||
<Text c={dark ? "dark.3" : "dimmed"}>Tahun Ajaran:</Text>
|
||||
<Text c={dark ? "dark.0" : "#1e3a5f"}>{displayData.tahunAjaran}</Text>
|
||||
<Text c={dark ? "white" : "dimmed"}>Tahun Ajaran:</Text>
|
||||
<Text c={dark ? "white" : "#1e3a5f"}>{displayData.tahunAjaran}</Text>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { Card, Group, Stack, Text, Title, ThemeIcon } from "@mantine/core";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import {
|
||||
Card,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { IconCalendarEvent } from "@tabler/icons-react";
|
||||
|
||||
interface EventItem {
|
||||
@@ -46,7 +53,7 @@ export const EventCalendar = ({ data }: EventCalendarProps) => {
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
>
|
||||
<Title order={3} mb="md" c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { Card, Group, Progress, Stack, Text, Title } from "@mantine/core";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import {
|
||||
Card,
|
||||
Group,
|
||||
Progress,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
|
||||
interface HealthProgressItem {
|
||||
label: string;
|
||||
@@ -30,9 +37,9 @@ export const HealthStats = ({ data }: HealthStatsProps) => {
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
h={'100%'}
|
||||
h={"100%"}
|
||||
>
|
||||
<Title order={3} mb="md" c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
Statistik Kesehatan
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import {
|
||||
Card,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
|
||||
interface EducationData {
|
||||
siswa: {
|
||||
@@ -43,7 +49,7 @@ export const Pendidikan = ({ data }: PendidikanProps) => {
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
>
|
||||
<Title order={3} mb="md" c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { Badge, Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import {
|
||||
Badge,
|
||||
Card,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
|
||||
interface PosyanduItem {
|
||||
id: string;
|
||||
@@ -51,7 +58,7 @@ export const PosyanduSchedule = ({ data }: PosyanduScheduleProps) => {
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
>
|
||||
<Title order={3} mb="md" c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
@@ -66,14 +73,13 @@ export const PosyanduSchedule = ({ data }: PosyanduScheduleProps) => {
|
||||
withBorder
|
||||
bg={dark ? "#263852ff" : "#F1F5F9"}
|
||||
style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}
|
||||
hoverable
|
||||
>
|
||||
<Group justify="space-between">
|
||||
<Stack gap={0}>
|
||||
<Text fw={600} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
<Text fw={600} c={dark ? "white" : "#1e3a5f"}>
|
||||
{item.nama}
|
||||
</Text>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"}>
|
||||
{item.tanggal}
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { Card, Grid, GridCol, Group, Stack, Text, ThemeIcon } from "@mantine/core";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import {
|
||||
Card,
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconBabyCarriage,
|
||||
IconHeartbeat,
|
||||
@@ -35,7 +43,7 @@ const SummaryCard = ({
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
>
|
||||
<Group justify="space-between" align="center">
|
||||
@@ -46,12 +54,12 @@ const SummaryCard = ({
|
||||
<Text
|
||||
size="xl"
|
||||
fw={700}
|
||||
c={highlight ? "red" : dark ? "dark.0" : "#1e3a5f"}
|
||||
c={highlight ? "red" : dark ? "white" : "#1e3a5f"}
|
||||
>
|
||||
{value}
|
||||
</Text>
|
||||
{subtitle && (
|
||||
<Text size="xs" c={dark ? "dark.4" : "gray.6"}>
|
||||
<Text size="xs" c={dark ? "white" : "gray.6"}>
|
||||
{subtitle}
|
||||
</Text>
|
||||
)}
|
||||
@@ -93,8 +101,8 @@ export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||
value={displayData.ibuHamil}
|
||||
subtitle="Aktif"
|
||||
icon={<IconHeartbeat size={20} />}
|
||||
color= "white"
|
||||
backgroundColor= "#1E3A5F"
|
||||
color="white"
|
||||
backgroundColor="#1E3A5F"
|
||||
/>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
@@ -103,8 +111,8 @@ export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||
value={displayData.balita}
|
||||
subtitle="Terdaftar"
|
||||
icon={<IconBabyCarriage size={20} />}
|
||||
color= "white"
|
||||
backgroundColor= "#1E3A5F"
|
||||
color="white"
|
||||
backgroundColor="#1E3A5F"
|
||||
/>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
@@ -113,8 +121,8 @@ export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||
value={displayData.alertStunting}
|
||||
subtitle="Perhatian"
|
||||
icon={<IconStethoscope size={20} />}
|
||||
color= "white"
|
||||
backgroundColor= "#1E3A5F"
|
||||
color="white"
|
||||
backgroundColor="#1E3A5F"
|
||||
/>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
@@ -123,8 +131,8 @@ export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||
value={displayData.posyanduAktif}
|
||||
subtitle="Aktif"
|
||||
icon={<IconMedicalCross size={20} />}
|
||||
color= "white"
|
||||
backgroundColor= "#1E3A5F"
|
||||
color="white"
|
||||
backgroundColor="#1E3A5F"
|
||||
/>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
|
||||
@@ -51,9 +51,7 @@ export const HelpCard = ({
|
||||
{icon && (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: isDark
|
||||
? "#263852ff"
|
||||
: "#1E3A5F",
|
||||
backgroundColor: isDark ? "#263852ff" : "#1E3A5F",
|
||||
borderRadius: "8px",
|
||||
padding: "8px",
|
||||
display: "flex",
|
||||
|
||||
@@ -15,11 +15,11 @@ const MetricCard = ({ title, value, trend }: MetricCardProps) => {
|
||||
|
||||
return (
|
||||
<Group justify="space-between" align="center">
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"} fw={500}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"} fw={500}>
|
||||
{title}
|
||||
</Text>
|
||||
<Stack gap={0} align="flex-end">
|
||||
<Text size="lg" fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
<Text size="lg" fw={700} c={dark ? "white" : "#1e3a5f"}>
|
||||
{value}
|
||||
</Text>
|
||||
{trend && (
|
||||
@@ -77,7 +77,7 @@ export const ProdukUnggulan = ({ data }: ProdukUnggulanProps) => {
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
>
|
||||
<Stack gap="lg">
|
||||
|
||||
@@ -108,7 +108,7 @@ export const SalesTable = ({ data, onDetailClick }: SalesTableProps) => {
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
>
|
||||
<Group justify="space-between" mb="md">
|
||||
@@ -209,7 +209,7 @@ export const SalesTable = ({ data, onDetailClick }: SalesTableProps) => {
|
||||
</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"}>
|
||||
{formatCurrency(product.bulanLalu)}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
useMantineColorScheme
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconCategory,
|
||||
IconCurrencyDollar,
|
||||
IconTrendingUp,
|
||||
IconUsers
|
||||
IconUsers,
|
||||
} from "@tabler/icons-react";
|
||||
|
||||
interface KpiCardProps {
|
||||
@@ -24,7 +24,14 @@ interface KpiCardProps {
|
||||
backgroundColor: string;
|
||||
}
|
||||
|
||||
const KpiCard = ({ title, value, subtitle, icon, color, backgroundColor }: KpiCardProps) => {
|
||||
const KpiCard = ({
|
||||
title,
|
||||
value,
|
||||
subtitle,
|
||||
icon,
|
||||
color,
|
||||
backgroundColor,
|
||||
}: KpiCardProps) => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
|
||||
@@ -47,19 +54,23 @@ const KpiCard = ({ title, value, subtitle, icon, color, backgroundColor }: KpiCa
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{
|
||||
borderColor: dark ? "#334155" : "white",
|
||||
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||
transition: "transform 0.15s ease, box-shadow 0.15s ease",
|
||||
}}
|
||||
>
|
||||
<Group justify="space-between" align="center">
|
||||
<Stack gap={2}>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"} fw={500}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text size="xl" fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
<Text size="xl" fw={700} c={dark ? "white" : "#1e3a5f"}>
|
||||
{formatValue(value)}
|
||||
</Text>
|
||||
{subtitle && (
|
||||
<Text size="xs" c={dark ? "dark.4" : "gray.6"}>
|
||||
<Text size="xs" c={dark ? "white" : "gray.6"}>
|
||||
{subtitle}
|
||||
</Text>
|
||||
)}
|
||||
@@ -108,7 +119,7 @@ export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||
subtitle: "Beroperasi",
|
||||
icon: <IconCurrencyDollar size={25} />,
|
||||
color: "white",
|
||||
backgroundColor: "#1E3A5F"
|
||||
backgroundColor: "#1E3A5F",
|
||||
},
|
||||
{
|
||||
title: "UMKM Terdaftar",
|
||||
@@ -116,7 +127,7 @@ export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||
subtitle: "Total registrasi",
|
||||
icon: <IconUsers size={25} />,
|
||||
color: "white",
|
||||
backgroundColor: "#1E3A5F"
|
||||
backgroundColor: "#1E3A5F",
|
||||
},
|
||||
{
|
||||
title: "Omzet",
|
||||
@@ -124,7 +135,7 @@ export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||
subtitle: "Omzet BUMDes per bulan",
|
||||
icon: <IconTrendingUp size={25} />,
|
||||
color: "white",
|
||||
backgroundColor: "#1E3A5F"
|
||||
backgroundColor: "#1E3A5F",
|
||||
},
|
||||
{
|
||||
title: "UMKM Terbanyak",
|
||||
@@ -132,7 +143,7 @@ export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||
subtitle: `Kategori ${displayData.kategoriTerbanyak.name}`,
|
||||
icon: <IconTrendingUp size={25} />,
|
||||
color: "white",
|
||||
backgroundColor: "#1E3A5F"
|
||||
backgroundColor: "#1E3A5F",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ export const TopProducts = ({ products }: TopProductsProps) => {
|
||||
radius="xl"
|
||||
withBorder
|
||||
shadow="sm"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||
>
|
||||
<Title order={4} mb="md" c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
@@ -105,20 +105,20 @@ export const TopProducts = ({ products }: TopProductsProps) => {
|
||||
{product.rank}
|
||||
</Badge>
|
||||
<Stack gap={0}>
|
||||
<Text fw={600} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||
<Text fw={600} c={dark ? "white" : "#1e3a5f"}>
|
||||
{product.name}
|
||||
</Text>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c={dark ? "white" : "dimmed"}>
|
||||
{product.umkmName}
|
||||
</Text>
|
||||
<Group gap="xs" mt={2}>
|
||||
<Text size="xs" c={dark ? "dark.4" : "gray.6"}>
|
||||
<Text size="xs" c={dark ? "white" : "gray.6"}>
|
||||
Rp {formatCurrency(product.revenue)}
|
||||
</Text>
|
||||
<Text size="xs" c={dark ? "dark.4" : "gray.6"}>
|
||||
<Text size="xs" c={dark ? "white" : "gray.6"}>
|
||||
•
|
||||
</Text>
|
||||
<Text size="xs" c={dark ? "dark.4" : "gray.6"}>
|
||||
<Text size="xs" c={dark ? "white" : "gray.6"}>
|
||||
{formatNumber(product.quantitySold)} terjual
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { useState } from "react";
|
||||
|
||||
export function useSidebarFullscreen() {
|
||||
const [opened, { toggle: toggleMobile }] = useDisclosure();
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useDisclosure(false);
|
||||
const [clickCount, setClickCount] = useState(0);
|
||||
|
||||
const toggleSidebar = () => {
|
||||
setSidebarCollapsed.toggle();
|
||||
setClickCount(0);
|
||||
};
|
||||
|
||||
const handleMainClick = () => {
|
||||
if (!sidebarCollapsed) {
|
||||
toggleSidebar();
|
||||
const newCount = clickCount + 1;
|
||||
setClickCount(newCount);
|
||||
|
||||
if (newCount === 2) {
|
||||
toggleSidebar();
|
||||
} else {
|
||||
setTimeout(() => setClickCount(0), 300);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -61,19 +61,11 @@ type RouteRule = {
|
||||
|
||||
const routeRules: RouteRule[] = [
|
||||
// Public routes - no auth required
|
||||
{
|
||||
match: (p) => p === "/" || p === "/signin" || p === "/signup",
|
||||
requireAuth: false,
|
||||
},
|
||||
// Profile routes - auth required for all roles
|
||||
{
|
||||
match: (p) => p === "/profile" || p.startsWith("/profile/"),
|
||||
requireAuth: true,
|
||||
redirectTo: "/signin",
|
||||
},
|
||||
// Dashboard and main pages - auth required for all roles (not just admin)
|
||||
{
|
||||
match: (p) =>
|
||||
p === "/" ||
|
||||
p === "/signin" ||
|
||||
p === "/signup" ||
|
||||
p.startsWith("/kinerja-divisi") ||
|
||||
p.startsWith("/pengaduan") ||
|
||||
p.startsWith("/jenna") ||
|
||||
@@ -83,7 +75,13 @@ const routeRules: RouteRule[] = [
|
||||
p.startsWith("/sosial") ||
|
||||
p.startsWith("/keamanan") ||
|
||||
p.startsWith("/bantuan") ||
|
||||
p.startsWith("/pengaturan"),
|
||||
p.startsWith("/pengaturan") ||
|
||||
p.startsWith("/users"),
|
||||
requireAuth: false,
|
||||
},
|
||||
// Profile routes - auth required for all roles
|
||||
{
|
||||
match: (p) => p === "/profile" || p.startsWith("/profile/"),
|
||||
requireAuth: true,
|
||||
redirectTo: "/signin",
|
||||
},
|
||||
|
||||
@@ -8,21 +8,15 @@ import { createRootRoute, Outlet } from "@tanstack/react-router";
|
||||
export const Route = createRootRoute({
|
||||
component: RootComponent,
|
||||
beforeLoad: async ({ location }) => {
|
||||
// Only apply auth middleware for routes that need it
|
||||
// Public routes: /, /signin, /signup
|
||||
const isPublicRoute =
|
||||
location.pathname === "/" ||
|
||||
location.pathname === "/signin" ||
|
||||
location.pathname === "/signup";
|
||||
|
||||
if (isPublicRoute) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply protected route middleware for all other routes
|
||||
// Apply protected route middleware for all routes
|
||||
// The middleware will determine which routes are public vs protected
|
||||
const context = await protectedRouteMiddleware({ location });
|
||||
authStore.user = context?.user as any;
|
||||
authStore.session = context?.session as any;
|
||||
|
||||
// Only set auth store if we have user data (for protected routes)
|
||||
if (context?.user) {
|
||||
authStore.user = context?.user as any;
|
||||
authStore.session = context?.session as any;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -37,10 +37,6 @@ import { authStore } from "../../store/auth";
|
||||
export const Route = createFileRoute("/admin")({
|
||||
component: DashboardLayout,
|
||||
beforeLoad: protectedRouteMiddleware,
|
||||
onEnter({ context }) {
|
||||
authStore.user = context?.user as any;
|
||||
authStore.session = context?.session as any;
|
||||
},
|
||||
});
|
||||
|
||||
function DashboardLayout() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { useState } from "react";
|
||||
import { DashboardContent } from "@/components/dashboard-content";
|
||||
import { Header } from "@/components/header";
|
||||
import { Sidebar } from "@/components/sidebar";
|
||||
@@ -12,6 +13,7 @@ export const Route = createFileRoute("/")({
|
||||
function DashboardPage() {
|
||||
const [opened, { toggle }] = useDisclosure();
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useDisclosure(false);
|
||||
const [clickCount, setClickCount] = useState(0);
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
|
||||
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
|
||||
@@ -19,7 +21,15 @@ function DashboardPage() {
|
||||
|
||||
const handleMainClick = () => {
|
||||
if (!sidebarCollapsed) {
|
||||
setSidebarCollapsed.toggle();
|
||||
const newCount = clickCount + 1;
|
||||
setClickCount(newCount);
|
||||
|
||||
if (newCount === 2) {
|
||||
setSidebarCollapsed.toggle();
|
||||
setClickCount(0);
|
||||
} else {
|
||||
setTimeout(() => setClickCount(0), 300);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import AksesDanTimSettings from "@/components/pengaturan/akses-dan-tim";
|
||||
|
||||
export const Route = createFileRoute("/pengaturan/akses-dan-tim")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/pengaturan/akses-dan-tim"!</div>;
|
||||
return <AksesDanTimSettings />;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import KeamananSettings from "@/components/pengaturan/keamanan";
|
||||
|
||||
export const Route = createFileRoute("/pengaturan/keamanan")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/pengaturan/keamanan"!</div>;
|
||||
return <KeamananSettings />;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import NotifikasiSettings from "@/components/pengaturan/notifikasi";
|
||||
|
||||
export const Route = createFileRoute("/pengaturan/notifikasi")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/pengaturan/notifikasi"!</div>;
|
||||
return <NotifikasiSettings />;
|
||||
}
|
||||
|
||||
@@ -21,10 +21,6 @@ import { authStore } from "../../store/auth";
|
||||
export const Route = createFileRoute("/profile/edit")({
|
||||
component: EditProfile,
|
||||
beforeLoad: protectedRouteMiddleware,
|
||||
onEnter({ context }) {
|
||||
authStore.user = context?.user as any;
|
||||
authStore.session = context?.session as any;
|
||||
},
|
||||
});
|
||||
|
||||
function EditProfile() {
|
||||
|
||||
@@ -41,10 +41,6 @@ import { authStore } from "../../store/auth";
|
||||
export const Route = createFileRoute("/profile/")({
|
||||
component: Profile,
|
||||
beforeLoad: protectedRouteMiddleware,
|
||||
onEnter({ context }) {
|
||||
authStore.user = context?.user as any;
|
||||
authStore.session = context?.session as any;
|
||||
},
|
||||
});
|
||||
|
||||
function Profile() {
|
||||
|
||||
Reference in New Issue
Block a user