up: dashboard admin

Deskripsi:
- akses role pada menu dashboard
- akses role pada setting
- akses role pada pelayanan surat
- akses role pada pengaduan warga
- akses role pada warga

NO Issues
This commit is contained in:
2025-11-24 16:27:35 +08:00
parent 0a3afb7b9c
commit 10db3f922e
12 changed files with 397 additions and 135 deletions

View File

@@ -16,13 +16,14 @@ import {
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconEdit } from "@tabler/icons-react"; import { IconEdit } from "@tabler/icons-react";
import type { JsonValue } from "generated/prisma/runtime/library";
import _ from "lodash"; import _ from "lodash";
import { useState } from "react"; import { useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import ModalFile from "./ModalFile"; import ModalFile from "./ModalFile";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function DesaSetting() { export default function DesaSetting({ permissions }: { permissions: JsonValue[] }) {
const [btnDisable, setBtnDisable] = useState(false); const [btnDisable, setBtnDisable] = useState(false);
const [btnLoading, setBtnLoading] = useState(false); const [btnLoading, setBtnLoading] = useState(false);
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
@@ -213,12 +214,13 @@ export default function DesaSetting() {
} }
</Table.Td> </Table.Td>
<Table.Td> <Table.Td>
<Tooltip label="Edit Setting"> <Tooltip label={permissions.includes("setting.desa.edit") ? "Edit Setting" : "Edit Setting - Anda tidak memiliki akses"}>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }} style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
onClick={() => chooseEdit({ data: v })} onClick={() => chooseEdit({ data: v })}
disabled={!permissions.includes("setting.desa.edit")}
> >
<IconEdit size={20} /> <IconEdit size={20} />
</ActionIcon> </ActionIcon>

View File

@@ -18,11 +18,12 @@ import {
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconEdit, IconEye, IconPlus, IconTrash } from "@tabler/icons-react"; import { IconEdit, IconEye, IconPlus, IconTrash } from "@tabler/icons-react";
import type { JsonValue } from "generated/prisma/runtime/library";
import { useState } from "react"; import { useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function KategoriPelayananSurat() { export default function KategoriPelayananSurat({ permissions }: { permissions: JsonValue[] }) {
const [openedDelete, { open: openDelete, close: closeDelete }] = const [openedDelete, { open: openDelete, close: closeDelete }] =
useDisclosure(false); useDisclosure(false);
const [openedDetail, { open: openDetail, close: closeDetail }] = const [openedDetail, { open: openDetail, close: closeDetail }] =
@@ -52,6 +53,7 @@ export default function KategoriPelayananSurat() {
mutate(); mutate();
}, []); }, []);
async function handleCreate() { async function handleCreate() {
try { try {
setBtnLoading(true); setBtnLoading(true);
@@ -533,15 +535,19 @@ export default function KategoriPelayananSurat() {
<Title order={4} c="gray.2"> <Title order={4} c="gray.2">
Kategori Pelayanan Surat Kategori Pelayanan Surat
</Title> </Title>
<Tooltip label="Tambah Kategori Pelayanan Surat"> {
<Button permissions.includes("setting.kategori_pelayanan.tambah") && (
variant="light" <Tooltip label="Tambah Kategori Pelayanan Surat">
leftSection={<IconPlus size={20} />} <Button
onClick={openTambah} variant="light"
> leftSection={<IconPlus size={20} />}
Tambah onClick={openTambah}
</Button> >
</Tooltip> Tambah
</Button>
</Tooltip>
)
}
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />
<Stack gap={"md"}> <Stack gap={"md"}>
@@ -572,7 +578,7 @@ export default function KategoriPelayananSurat() {
<IconEye size={20} /> <IconEye size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label="Edit Kategori"> <Tooltip label={permissions.includes("setting.kategori_pelayanan.edit") ? "Edit Kategori" : "Edit Kategori - Anda tidak memiliki akses"}>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -581,11 +587,12 @@ export default function KategoriPelayananSurat() {
setDataChoose(v); setDataChoose(v);
open(); open();
}} }}
disabled={!permissions.includes("setting.kategori_pelayanan.edit")}
> >
<IconEdit size={20} /> <IconEdit size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label="Delete Kategori"> <Tooltip label={permissions.includes("setting.kategori_pelayanan.delete") ? "Hapus Kategori" : "Hapus Kategori - Anda tidak memiliki akses"}>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -595,6 +602,7 @@ export default function KategoriPelayananSurat() {
setDataDelete(v.id); setDataDelete(v.id);
openDelete(); openDelete();
}} }}
disabled={!permissions.includes("setting.kategori_pelayanan.delete")}
> >
<IconTrash size={20} /> <IconTrash size={20} />
</ActionIcon> </ActionIcon>

View File

@@ -15,11 +15,12 @@ import {
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react"; import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react";
import type { JsonValue } from "generated/prisma/runtime/library";
import { useState } from "react"; import { useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function KategoriPengaduan() { export default function KategoriPengaduan({ permissions }: { permissions: JsonValue[] }) {
const [openedDelete, { open: openDelete, close: closeDelete }] = const [openedDelete, { open: openDelete, close: closeDelete }] =
useDisclosure(false); useDisclosure(false);
const [btnDisable, setBtnDisable] = useState(true); const [btnDisable, setBtnDisable] = useState(true);
@@ -293,15 +294,19 @@ export default function KategoriPengaduan() {
<Title order={4} c="gray.2"> <Title order={4} c="gray.2">
Kategori Pengaduan Kategori Pengaduan
</Title> </Title>
<Tooltip label="Tambah Kategori Pengaduan"> {
<Button permissions.includes("setting.kategori_pengaduan.tambah") && (
variant="light" <Tooltip label="Tambah Kategori Pengaduan">
leftSection={<IconPlus size={20} />} <Button
onClick={openTambah} variant="light"
> leftSection={<IconPlus size={20} />}
Tambah onClick={openTambah}
</Button> >
</Tooltip> Tambah
</Button>
</Tooltip>
)
}
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />
<Stack gap={"md"}> <Stack gap={"md"}>
@@ -318,17 +323,18 @@ export default function KategoriPengaduan() {
<Table.Td>{v.name}</Table.Td> <Table.Td>{v.name}</Table.Td>
<Table.Td> <Table.Td>
<Group> <Group>
<Tooltip label="Edit Kategori"> <Tooltip label={permissions.includes("setting.kategori_pengaduan.edit") ? "Edit Kategori" : "Edit Kategori - Anda tidak memiliki akses"}>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }} style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
onClick={() => chooseEdit({ data: v })} onClick={() => chooseEdit({ data: v })}
disabled={!permissions.includes("setting.kategori_pengaduan.edit")}
> >
<IconEdit size={20} /> <IconEdit size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label="Delete Kategori"> <Tooltip label={permissions.includes("setting.kategori_pengaduan.delete") ? "Hapus Kategori" : "Hapus Kategori - Anda tidak memiliki akses"}>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -338,6 +344,7 @@ export default function KategoriPengaduan() {
setDataDelete(v.id); setDataDelete(v.id);
openDelete(); openDelete();
}} }}
disabled={!permissions.includes("setting.kategori_pengaduan.delete")}
> >
<IconTrash size={20} /> <IconTrash size={20} />
</ActionIcon> </ActionIcon>

View File

@@ -9,10 +9,11 @@ import {
Stack, Stack,
Title, Title,
} from "@mantine/core"; } from "@mantine/core";
import type { JsonValue } from "generated/prisma/runtime/library";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function ProfileUser() { export default function ProfileUser({ permissions }: { permissions: JsonValue[] }) {
const [opened, setOpened] = useState(false); const [opened, setOpened] = useState(false);
const [openedPassword, setOpenedPassword] = useState(false); const [openedPassword, setOpenedPassword] = useState(false);
const [pwdBaru, setPwdBaru] = useState(""); const [pwdBaru, setPwdBaru] = useState("");
@@ -126,12 +127,21 @@ export default function ProfileUser() {
Profile Pengguna Profile Pengguna
</Title> </Title>
<Group gap="md"> <Group gap="md">
<Button variant="light" onClick={() => setOpened(true)}> {
Edit permissions.includes("setting.profile.edit") && (
</Button> <Button variant="light" onClick={() => setOpened(true)}>
<Button variant="light" onClick={() => setOpenedPassword(true)}> Edit
Ubah Password </Button>
</Button> )
}
{
permissions.includes("setting.profile.password") && (
<Button variant="light" onClick={() => setOpenedPassword(true)}>
Ubah Password
</Button>
)
}
</Group> </Group>
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />

View File

@@ -16,12 +16,13 @@ import {
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react"; import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react";
import type { JsonValue } from "generated/prisma/runtime/library";
import { useState } from "react"; import { useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
import PermissionRole from "./PermissionRole"; import PermissionRole from "./PermissionRole";
export default function UserRoleSetting() { export default function UserRoleSetting({ permissions }: { permissions: JsonValue[] }) {
const [btnDisable, setBtnDisable] = useState(true); const [btnDisable, setBtnDisable] = useState(true);
const [btnLoading, setBtnLoading] = useState(false); const [btnLoading, setBtnLoading] = useState(false);
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
@@ -391,15 +392,19 @@ export default function UserRoleSetting() {
<Title order={4} c="gray.2"> <Title order={4} c="gray.2">
Daftar Role Daftar Role
</Title> </Title>
<Tooltip label="Tambah Role"> {
<Button permissions.includes('setting.user_role.tambah') && (
variant="light" <Tooltip label="Tambah Role">
leftSection={<IconPlus size={20} />} <Button
onClick={openTambah} variant="light"
> leftSection={<IconPlus size={20} />}
Tambah onClick={openTambah}
</Button> >
</Tooltip> Tambah
</Button>
</Tooltip>
)
}
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />
<Stack gap={"md"}> <Stack gap={"md"}>
@@ -421,17 +426,18 @@ export default function UserRoleSetting() {
</Table.Td> </Table.Td>
<Table.Td> <Table.Td>
<Group> <Group>
<Tooltip label="Edit User"> <Tooltip label={permissions.includes('setting.user_role.edit') ? "Edit Role" : "Edit Role - Anda tidak memiliki akses"}>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }} style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
onClick={() => chooseEdit({ data: v })} onClick={() => chooseEdit({ data: v })}
disabled={!permissions.includes('setting.user_role.edit')}
> >
<IconEdit size={20} /> <IconEdit size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label="Delete User"> <Tooltip label={permissions.includes('setting.user_role.delete') ? "Delete Role" : "Delete Role - Anda tidak memiliki akses"}>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -441,6 +447,7 @@ export default function UserRoleSetting() {
setDataDelete(v.id); setDataDelete(v.id);
openDelete(); openDelete();
}} }}
disabled={!permissions.includes('setting.user_role.delete')}
> >
<IconTrash size={20} /> <IconTrash size={20} />
</ActionIcon> </ActionIcon>

View File

@@ -16,11 +16,12 @@ import {
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react"; import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react";
import type { JsonValue } from "generated/prisma/runtime/library";
import { useState } from "react"; import { useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function UserSetting() { export default function UserSetting({ permissions }: { permissions: JsonValue[] }) {
const [btnDisable, setBtnDisable] = useState(true); const [btnDisable, setBtnDisable] = useState(true);
const [btnLoading, setBtnLoading] = useState(false); const [btnLoading, setBtnLoading] = useState(false);
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
@@ -390,15 +391,20 @@ export default function UserSetting() {
<Title order={4} c="gray.2"> <Title order={4} c="gray.2">
Daftar User Daftar User
</Title> </Title>
<Tooltip label="Tambah User"> {
<Button permissions.includes('setting.user.tambah') && (
variant="light" <Tooltip label="Tambah User">
leftSection={<IconPlus size={20} />} <Button
onClick={openTambah} variant="light"
> leftSection={<IconPlus size={20} />}
Tambah onClick={openTambah}
</Button> >
</Tooltip> Tambah
</Button>
</Tooltip>
)
}
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />
<Stack gap={"md"}> <Stack gap={"md"}>
@@ -422,17 +428,18 @@ export default function UserSetting() {
<Table.Td>{v.roleId}</Table.Td> <Table.Td>{v.roleId}</Table.Td>
<Table.Td> <Table.Td>
<Group> <Group>
<Tooltip label="Edit User"> <Tooltip label={permissions.includes('setting.user.edit') ? "Edit User" : "Edit User - Anda tidak memiliki akses"}>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }} style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
onClick={() => chooseEdit({ data: v })} onClick={() => chooseEdit({ data: v })}
disabled={!permissions.includes('setting.user.edit')}
> >
<IconEdit size={20} /> <IconEdit size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label="Delete User"> <Tooltip label={permissions.includes('setting.user.delete') ? "Delete User" : "Delete User - Anda tidak memiliki akses"}>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -442,6 +449,7 @@ export default function UserSetting() {
setDataDelete(v.id); setDataDelete(v.id);
openDelete(); openDelete();
}} }}
disabled={!permissions.includes('setting.user.delete')}
> >
<IconTrash size={20} /> <IconTrash size={20} />
</ActionIcon> </ActionIcon>

View File

@@ -27,8 +27,16 @@
"label": "Antrian", "label": "Antrian",
"default": true, "default": true,
"children": [ "children": [
{ "key": "pengaduan.antrian.tolak", "label": "Menolak", "default": true }, {
{ "key": "pengaduan.antrian.terima", "label": "Menerima", "default": true } "key": "pengaduan.antrian.tolak",
"label": "Menolak",
"default": true
},
{
"key": "pengaduan.antrian.terima",
"label": "Menerima",
"default": true
}
] ]
}, },
{ {
@@ -36,7 +44,11 @@
"label": "Diterima", "label": "Diterima",
"default": true, "default": true,
"children": [ "children": [
{ "key": "pengaduan.diterima.dikerjakan", "label": "Dikerjakan", "default": true } {
"key": "pengaduan.diterima.dikerjakan",
"label": "Dikerjakan",
"default": true
}
] ]
}, },
{ {
@@ -44,7 +56,11 @@
"label": "Dikerjakan", "label": "Dikerjakan",
"default": true, "default": true,
"children": [ "children": [
{ "key": "pengaduan.dikerjakan.selesai", "label": "Diselesaikan", "default": true } {
"key": "pengaduan.dikerjakan.selesai",
"label": "Diselesaikan",
"default": true
}
] ]
} }
] ]
@@ -64,8 +80,16 @@
"label": "Antrian", "label": "Antrian",
"default": true, "default": true,
"children": [ "children": [
{ "key": "pelayanan.antrian.tolak", "label": "Menolak", "default": true }, {
{ "key": "pelayanan.antrian.terima", "label": "Menerima", "default": true } "key": "pelayanan.antrian.tolak",
"label": "Menolak",
"default": true
},
{
"key": "pelayanan.antrian.terima",
"label": "Menerima",
"default": true
}
] ]
}, },
{ {
@@ -73,8 +97,16 @@
"label": "Diterima", "label": "Diterima",
"default": true, "default": true,
"children": [ "children": [
{ "key": "pelayanan.diterima.tolak", "label": "Menolak", "default": true }, {
{ "key": "pelayanan.diterima.setujui", "label": "Menyetujui", "default": true } "key": "pelayanan.diterima.tolak",
"label": "Menolak",
"default": true
},
{
"key": "pelayanan.diterima.setujui",
"label": "Menyetujui",
"default": true
}
] ]
} }
] ]
@@ -101,9 +133,21 @@
"label": "Profile", "label": "Profile",
"default": true, "default": true,
"children": [ "children": [
{ "key": "setting.profile.view", "label": "View", "default": true }, {
{ "key": "setting.profile.edit", "label": "Edit", "default": true }, "key": "setting.profile.view",
{ "key": "setting.profile.password", "label": "Ubah Password", "default": true } "label": "View",
"default": true
},
{
"key": "setting.profile.edit",
"label": "Edit",
"default": true
},
{
"key": "setting.profile.password",
"label": "Ubah Password",
"default": true
}
] ]
}, },
{ {
@@ -111,10 +155,26 @@
"label": "User", "label": "User",
"default": true, "default": true,
"children": [ "children": [
{ "key": "setting.user.view", "label": "View List", "default": true }, {
{ "key": "setting.user.tambah", "label": "Tambah", "default": true }, "key": "setting.user.view",
{ "key": "setting.user.edit", "label": "Edit", "default": true }, "label": "View List",
{ "key": "setting.user.delete", "label": "Delete", "default": true } "default": true
},
{
"key": "setting.user.tambah",
"label": "Tambah",
"default": true
},
{
"key": "setting.user.edit",
"label": "Edit",
"default": true
},
{
"key": "setting.user.delete",
"label": "Delete",
"default": true
}
] ]
}, },
{ {
@@ -122,10 +182,26 @@
"label": "User Role", "label": "User Role",
"default": true, "default": true,
"children": [ "children": [
{ "key": "setting.user_role.view", "label": "View List", "default": true }, {
{ "key": "setting.user_role.tambah", "label": "Tambah", "default": true }, "key": "setting.user_role.view",
{ "key": "setting.user_role.edit", "label": "Edit", "default": true }, "label": "View List",
{ "key": "setting.user_role.delete", "label": "Delete", "default": true } "default": true
},
{
"key": "setting.user_role.tambah",
"label": "Tambah",
"default": true
},
{
"key": "setting.user_role.edit",
"label": "Edit",
"default": true
},
{
"key": "setting.user_role.delete",
"label": "Delete",
"default": true
}
] ]
}, },
{ {
@@ -133,10 +209,26 @@
"label": "Kategori Pengaduan", "label": "Kategori Pengaduan",
"default": true, "default": true,
"children": [ "children": [
{ "key": "setting.kategori_pengaduan.view", "label": "View List", "default": true }, {
{ "key": "setting.kategori_pengaduan.tambah", "label": "Tambah", "default": true }, "key": "setting.kategori_pengaduan.view",
{ "key": "setting.kategori_pengaduan.edit", "label": "Edit", "default": true }, "label": "View List",
{ "key": "setting.kategori_pengaduan.delete", "label": "Delete", "default": true } "default": true
},
{
"key": "setting.kategori_pengaduan.tambah",
"label": "Tambah",
"default": true
},
{
"key": "setting.kategori_pengaduan.edit",
"label": "Edit",
"default": true
},
{
"key": "setting.kategori_pengaduan.delete",
"label": "Delete",
"default": true
}
] ]
}, },
{ {
@@ -144,11 +236,31 @@
"label": "Kategori Pelayanan Surat", "label": "Kategori Pelayanan Surat",
"default": true, "default": true,
"children": [ "children": [
{ "key": "setting.kategori_pelayanan.view", "label": "View List", "default": true }, {
{ "key": "setting.kategori_pelayanan.detail", "label": "View Detail", "default": true }, "key": "setting.kategori_pelayanan.view",
{ "key": "setting.kategori_pelayanan.tambah", "label": "Tambah", "default": true }, "label": "View List",
{ "key": "setting.kategori_pelayanan.edit", "label": "Edit", "default": true }, "default": true
{ "key": "setting.kategori_pelayanan.delete", "label": "Delete", "default": true } },
{
"key": "setting.kategori_pelayanan.detail",
"label": "View Detail",
"default": true
},
{
"key": "setting.kategori_pelayanan.tambah",
"label": "Tambah",
"default": true
},
{
"key": "setting.kategori_pelayanan.edit",
"label": "Edit",
"default": true
},
{
"key": "setting.kategori_pelayanan.delete",
"label": "Delete",
"default": true
}
] ]
}, },
{ {
@@ -156,8 +268,16 @@
"label": "Desa", "label": "Desa",
"default": true, "default": true,
"children": [ "children": [
{ "key": "setting.desa.view", "label": "View List", "default": true }, {
{ "key": "setting.desa.edit", "label": "Edit", "default": true } "key": "setting.desa.view",
"label": "View List",
"default": true
},
{
"key": "setting.desa.edit",
"label": "Edit",
"default": true
}
] ]
} }
] ]
@@ -166,13 +286,25 @@
"key": "api_key", "key": "api_key",
"label": "API Key", "label": "API Key",
"default": true, "default": true,
"children": [] "children": [
{
"key": "api_key.view",
"label": "View List",
"default": true
}
]
}, },
{ {
"key": "credential", "key": "credential",
"label": "Credential", "label": "Credential",
"default": true, "default": true,
"children": [] "children": [
{
"key": "credential.viewØ",
"label": "View List",
"default": true
}
]
} }
] ]
} }

View File

@@ -35,6 +35,7 @@ import {
IconUsersGroup, IconUsersGroup,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import type { User } from "generated/prisma"; import type { User } from "generated/prisma";
import type { JsonValue } from "generated/prisma/runtime/library";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { Outlet, useLocation, useNavigate } from "react-router-dom";
@@ -212,36 +213,54 @@ function HostView() {
function NavigationDashboard() { function NavigationDashboard() {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const [permissions, setPermissions] = useState<JsonValue[]>([]);
useEffect(() => {
async function fetchPermissions() {
const { data } = await apiFetch.api.user.find.get();
if (Array.isArray(data?.permissions)) {
setPermissions(data.permissions);
} else {
setPermissions([]);
}
}
fetchPermissions();
}, []);
const isActive = (path: keyof typeof clientRoute) => const isActive = (path: keyof typeof clientRoute) =>
location.pathname.startsWith(clientRoute[path]); location.pathname.startsWith(clientRoute[path]);
const navItems = [ const navItems = [
{ {
key: "dashboard",
path: "/scr/dashboard/dashboard-home", path: "/scr/dashboard/dashboard-home",
icon: <IconDashboard size={20} />, icon: <IconDashboard size={20} />,
label: "Dashboard Overview", label: "Dashboard Overview",
description: "Quick summary and insights", description: "Quick summary and insights",
}, },
{ {
key: "pengaduan",
path: "/scr/dashboard/pengaduan/list", path: "/scr/dashboard/pengaduan/list",
icon: <IconMessageReport size={20} />, icon: <IconMessageReport size={20} />,
label: "Pengaduan Warga", label: "Pengaduan Warga",
description: "Manage pengaduan warga", description: "Manage pengaduan warga",
}, },
{ {
key: "pelayanan",
path: "/scr/dashboard/pelayanan-surat/list-pelayanan", path: "/scr/dashboard/pelayanan-surat/list-pelayanan",
icon: <IconFileCertificate size={20} />, icon: <IconFileCertificate size={20} />,
label: "Pelayanan Surat", label: "Pelayanan Surat",
description: "Manage pelayanan surat", description: "Manage pelayanan surat",
}, },
{ {
key: "warga",
path: "/scr/dashboard/warga/list-warga", path: "/scr/dashboard/warga/list-warga",
icon: <IconUsersGroup size={20} />, icon: <IconUsersGroup size={20} />,
label: "Warga", label: "Warga",
description: "Manage warga", description: "Manage warga",
}, },
{ {
key: "setting",
path: "/scr/dashboard/setting/detail-setting", path: "/scr/dashboard/setting/detail-setting",
icon: <IconSettings size={20} />, icon: <IconSettings size={20} />,
label: "Setting", label: "Setting",
@@ -249,12 +268,14 @@ function NavigationDashboard() {
"Manage setting (category pengaduan dan pelayanan surat, desa, etc)", "Manage setting (category pengaduan dan pelayanan surat, desa, etc)",
}, },
{ {
key: "api_key",
path: "/scr/dashboard/apikey/apikey", path: "/scr/dashboard/apikey/apikey",
icon: <IconKey size={20} />, icon: <IconKey size={20} />,
label: "API Key Manager", label: "API Key Manager",
description: "Create and manage API keys", description: "Create and manage API keys",
}, },
{ {
key: "credential",
path: "/scr/dashboard/credential/credential", path: "/scr/dashboard/credential/credential",
icon: <IconLock size={20} />, icon: <IconLock size={20} />,
label: "Credentials", label: "Credentials",
@@ -264,7 +285,7 @@ function NavigationDashboard() {
return ( return (
<Stack gap="xs" p="sm"> <Stack gap="xs" p="sm">
{navItems.map((item) => ( {navItems.filter((item) => permissions.includes(item.key)).map((item) => (
<NavLink <NavLink
key={item.path} key={item.path}
active={isActive(item.path as keyof typeof clientRoute)} active={isActive(item.path as keyof typeof clientRoute)}

View File

@@ -31,6 +31,7 @@ import {
IconUser IconUser
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import type { User } from "generated/prisma"; import type { User } from "generated/prisma";
import type { JsonValue } from "generated/prisma/runtime/library";
import _ from "lodash"; import _ from "lodash";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
@@ -76,11 +77,17 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
const [host, setHost] = useState<User | null>(null); const [host, setHost] = useState<User | null>(null);
const [noSurat, setNoSurat] = useState(""); const [noSurat, setNoSurat] = useState("");
const [openedPreview, setOpenedPreview] = useState(false); const [openedPreview, setOpenedPreview] = useState(false);
const [permissions, setPermissions] = useState<JsonValue[]>([]);
useEffect(() => { useEffect(() => {
async function fetchHost() { async function fetchHost() {
const { data } = await apiFetch.api.user.find.get(); const { data } = await apiFetch.api.user.find.get();
setHost(data?.user ?? null); setHost(data?.user ?? null);
if (data?.permissions && Array.isArray(data.permissions)) {
const onlySetting = data.permissions.filter((p: any) => p.startsWith("pelayanan"));
setPermissions(onlySetting);
}
} }
fetchHost(); fetchHost();
}, []); }, []);
@@ -276,6 +283,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
data?.status === "antrian" ? ( data?.status === "antrian" ? (
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
disabled={!permissions.includes("pelayanan.antrian.tolak")}
variant="light" variant="light"
onClick={() => { onClick={() => {
setCatModal("tolak"); setCatModal("tolak");
@@ -285,6 +293,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
Tolak Tolak
</Button> </Button>
<Button <Button
disabled={!permissions.includes("pelayanan.antrian.terima")}
variant="filled" variant="filled"
onClick={() => { onClick={() => {
setCatModal("terima"); setCatModal("terima");
@@ -297,6 +306,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
) : data?.status === "diterima" ? ( ) : data?.status === "diterima" ? (
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
disabled={!permissions.includes("pelayanan.diterima.tolak")}
variant="light" variant="light"
onClick={() => { onClick={() => {
setCatModal("tolak"); setCatModal("tolak");
@@ -306,6 +316,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
Tolak Tolak
</Button> </Button>
<Button <Button
disabled={!permissions.includes("pelayanan.diterima.setujui")}
variant="filled" variant="filled"
onClick={() => { onClick={() => {
setCatModal("terima"); setCatModal("terima");

View File

@@ -31,6 +31,7 @@ import {
IconUser, IconUser,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import type { User } from "generated/prisma"; import type { User } from "generated/prisma";
import type { JsonValue } from "generated/prisma/runtime/library";
import _ from "lodash"; import _ from "lodash";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
@@ -77,11 +78,17 @@ function DetailDataPengaduan({ data, onAction }: { data: any, onAction: () => vo
useDisclosure(false); useDisclosure(false);
const [keterangan, setKeterangan] = useState(""); const [keterangan, setKeterangan] = useState("");
const [host, setHost] = useState<User | null>(null); const [host, setHost] = useState<User | null>(null);
const [permissions, setPermissions] = useState<JsonValue[]>([]);
useEffect(() => { useEffect(() => {
async function fetchHost() { async function fetchHost() {
const { data } = await apiFetch.api.user.find.get(); const { data } = await apiFetch.api.user.find.get();
setHost(data?.user ?? null); setHost(data?.user ?? null);
if (data?.permissions && Array.isArray(data.permissions)) {
const onlySetting = data.permissions.filter((p: any) => p.startsWith("pengaduan"));
setPermissions(onlySetting);
}
} }
fetchHost(); fetchHost();
}, []); }, []);
@@ -294,6 +301,7 @@ function DetailDataPengaduan({ data, onAction }: { data: any, onAction: () => vo
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
variant="light" variant="light"
disabled={!permissions.includes("pengaduan.antrian.tolak")}
onClick={() => { onClick={() => {
setCatModal("tolak"); setCatModal("tolak");
open(); open();
@@ -303,6 +311,7 @@ function DetailDataPengaduan({ data, onAction }: { data: any, onAction: () => vo
</Button> </Button>
<Button <Button
variant="filled" variant="filled"
disabled={!permissions.includes("pengaduan.antrian.terima")}
onClick={() => { onClick={() => {
setCatModal("terima"); setCatModal("terima");
open(); open();
@@ -315,6 +324,7 @@ function DetailDataPengaduan({ data, onAction }: { data: any, onAction: () => vo
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
variant="filled" variant="filled"
disabled={!permissions.includes("pengaduan.diterima.dikerjakan")}
onClick={() => { onClick={() => {
setCatModal("terima"); setCatModal("terima");
open(); open();
@@ -327,6 +337,7 @@ function DetailDataPengaduan({ data, onAction }: { data: any, onAction: () => vo
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
variant="filled" variant="filled"
disabled={!permissions.includes("pengaduan.dikerjakan.selesai")}
onClick={() => { onClick={() => {
setCatModal("terima"); setCatModal("terima");
open(); open();

View File

@@ -4,6 +4,7 @@ import KategoriPengaduan from "@/components/KategoriPengaduan";
import ProfileUser from "@/components/ProfileUser"; import ProfileUser from "@/components/ProfileUser";
import UserRoleSetting from "@/components/UserRoleSetting"; import UserRoleSetting from "@/components/UserRoleSetting";
import UserSetting from "@/components/UserSetting"; import UserSetting from "@/components/UserSetting";
import apiFetch from "@/lib/apiFetch";
import { import {
Card, Card,
Container, Container,
@@ -18,12 +19,75 @@ import {
IconUserScreen, IconUserScreen,
IconUsersGroup IconUsersGroup
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import type { JsonValue } from "generated/prisma/runtime/library";
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
export default function DetailSettingPage() { export default function DetailSettingPage() {
const { search } = useLocation(); const { search } = useLocation();
const query = new URLSearchParams(search); const query = new URLSearchParams(search);
const type = query.get("type"); const type = query.get("type");
const [permissions, setPermissions] = useState<JsonValue[]>([]);
useEffect(() => {
async function fetchPermissions() {
const { data } = await apiFetch.api.user.find.get();
if (Array.isArray(data?.permissions)) {
const onlySetting = data.permissions.filter((p: any) => p.startsWith("setting"));
setPermissions(onlySetting);
} else {
setPermissions([]);
}
}
fetchPermissions();
}, []);
const navItems = [
{
key: "setting.profile",
path: "profile",
icon: <IconUserCog size={20} />,
label: "Profile",
description: "Manage profile settings",
},
{
key: "setting.user",
path: "user",
icon: <IconUsersGroup size={20} />,
label: "User",
description: "Manage user accounts",
},
{
key: "setting.user_role",
path: "role",
icon: <IconUserScreen size={20} />,
label: "Role",
description: "Manage user roles",
},
{
key: "setting.kategori_pengaduan",
path: "cat-pengaduan",
icon: <IconCategory2 size={20} />,
label: "Kategori Pengaduan",
description: "Manage complaint categories",
},
{
key: "setting.kategori_pelayanan",
path: "cat-pelayanan",
icon: <IconMailSpark size={20} />,
label: "Kategori Pelayanan Surat",
description: "Manage letter service categories",
},
{
key: "setting.desa",
path: "desa",
icon: <IconBuildingBank size={20} />,
label: "Desa",
description: "Manage desa information",
}
];
return ( return (
<Container size="xl" py="xl" w={"100%"}> <Container size="xl" py="xl" w={"100%"}>
@@ -40,42 +104,17 @@ export default function DetailSettingPage() {
boxShadow: "0 0 20px rgba(0,255,200,0.08)", boxShadow: "0 0 20px rgba(0,255,200,0.08)",
}} }}
> >
<NavLink {
href={`?type=profile`} navItems.filter((item) => permissions.includes(item.key)).map((item) => (
label="Profile" <NavLink
leftSection={<IconUserCog size={16} stroke={1.5} />} key={item.key}
active={type === "profile" || !type} href={'?type=' + item.path}
/> label={item.label}
<NavLink leftSection={item.icon}
href={`?type=user`} active={type === item.path || (!type && item.path === 'profile')}
label="User" />
leftSection={<IconUsersGroup size={16} stroke={1.5} />} ))
active={type === "user"} }
/>
<NavLink
href={`?type=role`}
label="Role"
leftSection={<IconUserScreen size={16} stroke={1.5} />}
active={type === "role"}
/>
<NavLink
href={`?type=cat-pengaduan`}
label="Kategori Pengaduan"
leftSection={<IconCategory2 size={16} stroke={1.5} />}
active={type === "cat-pengaduan"}
/>
<NavLink
href={`?type=cat-pelayanan`}
label="Kategori Pelayanan Surat"
leftSection={<IconMailSpark size={16} stroke={1.5} />}
active={type === "cat-pelayanan"}
/>
<NavLink
href={`?type=desa`}
label="Desa"
leftSection={<IconBuildingBank size={16} stroke={1.5} />}
active={type === "desa"}
/>
</Card> </Card>
</Grid.Col> </Grid.Col>
<Grid.Col span={9}> <Grid.Col span={9}>
@@ -91,17 +130,17 @@ export default function DetailSettingPage() {
}} }}
> >
{type === "cat-pengaduan" ? ( {type === "cat-pengaduan" ? (
<KategoriPengaduan /> <KategoriPengaduan permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.kategori_pengaduan"))} />
) : type === "cat-pelayanan" ? ( ) : type === "cat-pelayanan" ? (
<KategoriPelayananSurat /> <KategoriPelayananSurat permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.kategori_pelayanan"))} />
) : type === "desa" ? ( ) : type === "desa" ? (
<DesaSetting /> <DesaSetting permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.desa"))} />
) : type === "user" ? ( ) : type === "user" ? (
<UserSetting /> <UserSetting permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.user."))} />
) : type === "role" ? ( ) : type === "role" ? (
<UserRoleSetting /> <UserRoleSetting permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.user_role"))} />
) : ( ) : (
<ProfileUser /> <ProfileUser permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.profile"))} />
)} )}
</Card> </Card>
</Grid.Col> </Grid.Col>

View File

@@ -6,10 +6,16 @@ const UserRoute = new Elysia({
prefix: "user", prefix: "user",
tags: ["user"], tags: ["user"],
}) })
.get('/find', (ctx) => { .get('/find', async (ctx) => {
const { user } = ctx as any const { user } = ctx as any
const permissions = await prisma.role.findFirst({
where: { id: user?.roleId },
select: { permissions: true }
});
return { return {
user: user as User user: user as User,
permissions: permissions?.permissions || []
} }
}, { }, {
detail: { detail: {