Nico 20 Nov 25
Dibagian layout admin sudah disesuaikan dengan rolenya : supadmin, admin desa, admin kesehatan, admin pendidikan Fix API User & Role Admin
This commit is contained in:
@@ -1,23 +1,26 @@
|
||||
[
|
||||
{
|
||||
"id": "role-1",
|
||||
"name": "ADMIN DESA",
|
||||
"description": "Administrator Desa",
|
||||
"permissions": ["manage_users", "manage_content", "view_reports"],
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "role-2",
|
||||
"name": "ADMIN KESEHATAN",
|
||||
"description": "Administrator Bidang Kesehatan",
|
||||
"permissions": ["manage_health_data", "view_reports"],
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "role-3",
|
||||
"name": "ADMIN SEKOLAH",
|
||||
"description": "Administrator Sekolah",
|
||||
"permissions": ["manage_school_data", "view_reports"],
|
||||
"isActive": true
|
||||
}
|
||||
]
|
||||
{
|
||||
"id": "0",
|
||||
"name": "SUPER ADMIN",
|
||||
"description": "Administrator",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"name": "ADMIN DESA",
|
||||
"description": "Administrator Desa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "ADMIN KESEHATAN",
|
||||
"description": "Administrator Bidang Kesehatan",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "ADMIN PENDIDIKAN",
|
||||
"description": "Administrator Bidang Pendidikan",
|
||||
"isActive": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "user-1",
|
||||
"nama": "Admin Desa",
|
||||
"nomor": "089647037426",
|
||||
"roleId": "role-1",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "user-2",
|
||||
"nama": "Admin Kesehatan",
|
||||
"nomor": "082339004198",
|
||||
"roleId": "role-2",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "user-3",
|
||||
"nama": "Admin Sekolah",
|
||||
"nomor": "085237157222",
|
||||
"roleId": "role-3",
|
||||
"isActive": true
|
||||
}
|
||||
]
|
||||
@@ -2181,7 +2181,6 @@ model Role {
|
||||
id String @id @default(cuid())
|
||||
name String @unique // ADMIN_DESA, ADMIN_KESEHATAN, ADMIN_SEKOLAH
|
||||
description String?
|
||||
permissions Json // Menyimpan permission dalam format JSON
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -2200,17 +2199,6 @@ model KodeOtp {
|
||||
otp Int
|
||||
}
|
||||
|
||||
// Tabel untuk menyimpan permission
|
||||
model Permission {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
description String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("permissions")
|
||||
}
|
||||
|
||||
model UserSession {
|
||||
id String @id @default(cuid())
|
||||
token String
|
||||
|
||||
@@ -54,14 +54,13 @@ import tujuanProgram2 from "./data/pendidikan/pendidikan-non-formal/tujuan-progr
|
||||
import programUnggulan from "./data/pendidikan/program-pendidikan-anak/program-unggulan.json";
|
||||
import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json";
|
||||
import roles from "./data/user/roles.json";
|
||||
import users from "./data/user/users.json";
|
||||
import fileStorage from "./data/file-storage.json";
|
||||
import jenjangPendidikan from "./data/pendidikan/info-sekolah/jenjang-pendidikan.json";
|
||||
import seedAssets from "./seed_assets";
|
||||
import { safeSeedUnique } from "./safeseedUnique";
|
||||
|
||||
(async () => {
|
||||
// =========== USER & ROLE ===========
|
||||
// =========== ROLE ===========
|
||||
// In your seed.ts
|
||||
// =========== ROLES ===========
|
||||
console.log("🔄 Seeding roles...");
|
||||
@@ -69,35 +68,12 @@ import { safeSeedUnique } from "./safeseedUnique";
|
||||
await safeSeedUnique("role", { id: r.id }, {
|
||||
name: r.name,
|
||||
description: r.description,
|
||||
permissions: r.permissions,
|
||||
isActive: r.isActive,
|
||||
});
|
||||
}
|
||||
|
||||
console.log("✅ Roles seeded");
|
||||
|
||||
// =========== USERS ===========
|
||||
console.log("🔄 Seeding users...");
|
||||
for (const u of users) {
|
||||
// First verify the role exists
|
||||
const roleExists = await prisma.role.findUnique({
|
||||
where: { id: u.roleId },
|
||||
});
|
||||
|
||||
if (!roleExists) {
|
||||
console.error(`❌ Role with id ${u.roleId} not found for user ${u.nama}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await safeSeedUnique("user", { id: u.id }, {
|
||||
username: u.nama,
|
||||
nomor: u.nomor,
|
||||
roleId: u.roleId,
|
||||
isActive: u.isActive,
|
||||
});
|
||||
}
|
||||
console.log("✅ Users seeded");
|
||||
|
||||
// =========== FILE STORAGE ===========
|
||||
console.log("🔄 Seeding file storage...");
|
||||
for (const f of fileStorage) {
|
||||
|
||||
@@ -90,27 +90,35 @@ const userState = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
updateActive: {
|
||||
update: {
|
||||
loading: false,
|
||||
async submit(id: string, isActive: boolean) {
|
||||
|
||||
async submit(payload: { id: string; isActive?: boolean; roleId?: string }) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await fetch(`/api/user/updt`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ id, isActive }),
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (res.status === 200 && data.success) {
|
||||
toast.success(data.message);
|
||||
userState.findMany.load(userState.findMany.page, 10, userState.findMany.search);
|
||||
|
||||
// refresh list
|
||||
userState.findMany.load(
|
||||
userState.findMany.page,
|
||||
10,
|
||||
userState.findMany.search
|
||||
);
|
||||
} else {
|
||||
toast.error(data.message || "Gagal update status user");
|
||||
toast.error(data.message || "Gagal update user");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error("Gagal update status user");
|
||||
toast.error("Gagal update user");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
@@ -120,12 +128,10 @@ const userState = proxy({
|
||||
|
||||
const templateRole = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
permissions: z.array(z.string()).min(1, "Permission harus diisi"),
|
||||
});
|
||||
|
||||
const defaultRole = {
|
||||
name: "",
|
||||
permissions: [] as string[],
|
||||
};
|
||||
|
||||
const roleState = proxy({
|
||||
@@ -237,7 +243,7 @@ const roleState = proxy({
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/role/${id}`, {
|
||||
method: "GET",
|
||||
@@ -245,31 +251,25 @@ const roleState = proxy({
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
|
||||
// langsung set melalui root state, bukan this
|
||||
roleState.update.id = data.id;
|
||||
roleState.update.form = {
|
||||
name: data.name,
|
||||
permissions: data.permissions,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
|
||||
return data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading role:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
toast.error("Gagal memuat data");
|
||||
}
|
||||
},
|
||||
},
|
||||
async update() {
|
||||
const cek = templateRole.safeParse(roleState.update.form);
|
||||
if (!cek.success) {
|
||||
@@ -290,7 +290,6 @@ const roleState = proxy({
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
permissions: this.form.permissions,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Box, Button, Loader, Paper, PinInput, Stack, Text, Title } from '@manti
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { authStore } from '@/store/authStore';
|
||||
|
||||
export default function Validasi() {
|
||||
const router = useRouter();
|
||||
@@ -53,10 +54,17 @@ export default function Validasi() {
|
||||
setLoading(true);
|
||||
const verifyResult = await apiFetchVerifyOtp({ nomor, otp, kodeId });
|
||||
|
||||
if (verifyResult.success) {
|
||||
if (verifyResult.success && verifyResult.user) {
|
||||
// ✅ SET USER KE STORE
|
||||
authStore.setUser({
|
||||
id: verifyResult.user.id,
|
||||
name: verifyResult.user.name,
|
||||
roleId: Number(verifyResult.user.roleId),
|
||||
});
|
||||
|
||||
cleanupStorage();
|
||||
router.push('/admin/landing-page/profil/program-inovasi');
|
||||
return; // ✅ HENTIKAN eksekusi di sini
|
||||
return;
|
||||
}
|
||||
|
||||
// Hanya coba registrasi jika akun tidak ditemukan
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Loader, Group, MultiSelect, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Group, Loader, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
@@ -9,6 +10,7 @@ import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import user from '../../../_state/user/user-state';
|
||||
|
||||
|
||||
function EditRole() {
|
||||
const stateRole = useProxy(user.roleState);
|
||||
const router = useRouter();
|
||||
@@ -17,46 +19,37 @@ function EditRole() {
|
||||
// Controlled local state
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
permissions: [] as string[],
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
permissions: [] as string[],
|
||||
});
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Load role data
|
||||
const loadRole = useCallback(async (id: string) => {
|
||||
try {
|
||||
const data = await stateRole.update.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
permissions: data.permissions || [],
|
||||
});
|
||||
setOriginalData({
|
||||
name: data.name || '',
|
||||
permissions: data.permissions || [],
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading role:', error);
|
||||
toast.error(error instanceof Error ? error.message : 'Gagal mengambil data role');
|
||||
const data = await stateRole.update.load(id);
|
||||
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name ?? '',
|
||||
});
|
||||
|
||||
setOriginalData({
|
||||
name: data.name ?? '',
|
||||
});
|
||||
}
|
||||
}, [stateRole.update]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
stateRole.findMany.load(); // Load permissions/options
|
||||
const id = params?.id as string;
|
||||
if (id) loadRole(id);
|
||||
}, [params?.id, loadRole, stateRole.findMany]);
|
||||
stateRole.findMany.load(); // load permission
|
||||
if (params?.id) loadRole(params.id as string);
|
||||
}, [params?.id]);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
permissions: originalData.permissions,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
@@ -66,10 +59,6 @@ function EditRole() {
|
||||
toast.error('Nama role tidak boleh kosong');
|
||||
return;
|
||||
}
|
||||
if (!formData.permissions.length) {
|
||||
toast.error('Pilih minimal satu permission');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
@@ -77,7 +66,6 @@ function EditRole() {
|
||||
stateRole.update.form = {
|
||||
...stateRole.update.form,
|
||||
name: formData.name,
|
||||
permissions: formData.permissions,
|
||||
};
|
||||
await stateRole.update.update();
|
||||
toast.success('Role berhasil diperbarui!');
|
||||
@@ -116,24 +104,7 @@ function EditRole() {
|
||||
label={<Text fw="bold" fz="sm">Nama Role</Text>}
|
||||
placeholder="Masukkan nama role"
|
||||
/>
|
||||
<MultiSelect
|
||||
value={formData.permissions}
|
||||
onChange={(val) => setFormData({ ...formData, permissions: val })}
|
||||
label={<Text fw="bold" fz="sm">Permission</Text>}
|
||||
placeholder="Pilih permission"
|
||||
data={
|
||||
stateRole.findMany.data?.map((v) => ({
|
||||
value: v.id,
|
||||
label: v.name,
|
||||
})) || []
|
||||
}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
error={!formData.permissions.length ? 'Pilih minimal satu permission' : undefined}
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
@@ -5,9 +5,8 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
MultiSelect,
|
||||
Paper,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
Title
|
||||
@@ -15,9 +14,9 @@ import {
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import user from '../../../_state/user/user-state';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
|
||||
export default function CreateRole() {
|
||||
@@ -31,8 +30,7 @@ export default function CreateRole() {
|
||||
|
||||
const resetForm = () => {
|
||||
stateRole.create.form = {
|
||||
name: '',
|
||||
permissions: [],
|
||||
name: ''
|
||||
};
|
||||
};
|
||||
|
||||
@@ -80,28 +78,6 @@ export default function CreateRole() {
|
||||
required
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<MultiSelect
|
||||
label="Permission"
|
||||
placeholder="Pilih permission"
|
||||
data={
|
||||
Array.from(
|
||||
new Set(
|
||||
stateRole.findMany.data
|
||||
?.map((item) => item.permissions)
|
||||
.flat()
|
||||
)
|
||||
)
|
||||
.filter((p): p is string => typeof p === 'string')
|
||||
.map((p) => ({ label: p, value: p }))
|
||||
}
|
||||
value={stateRole.create.form.permissions}
|
||||
onChange={(value) => (stateRole.create.form.permissions = value)}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core';
|
||||
import { Box, Button, Center, Group, Pagination, Paper, Select, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconCheck, IconSearch, IconX } from '@tabler/icons-react';
|
||||
import { useState } from 'react';
|
||||
@@ -28,6 +28,7 @@ function User() {
|
||||
|
||||
function ListUser({ search }: { search: string }) {
|
||||
const stateUser = useProxy(user.userState)
|
||||
const stateRole = useProxy(user.roleState)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
|
||||
@@ -51,6 +52,7 @@ function ListUser({ search }: { search: string }) {
|
||||
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateRole.findMany.load()
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
@@ -92,11 +94,31 @@ function ListUser({ search }: { search: string }) {
|
||||
{item.nomor}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '20%', }}>
|
||||
<Text truncate fz="sm" c="dimmed">
|
||||
{item.role.name}
|
||||
</Text>
|
||||
<TableTd style={{ width: '20%' }}>
|
||||
<Select
|
||||
placeholder="Pilih role"
|
||||
data={stateRole.findMany.data.map((r) => ({
|
||||
label: r.name,
|
||||
value: r.id,
|
||||
}))}
|
||||
value={item.roleId} // ⬅ role milik user ini
|
||||
onChange={async (val) => {
|
||||
if (!val) return;
|
||||
|
||||
await stateUser.update.submit({
|
||||
id: item.id,
|
||||
roleId: val, // ⬅ kirim roleId
|
||||
});
|
||||
|
||||
// reload data supaya UI up-to-date
|
||||
stateUser.findMany.load(page, 10, search);
|
||||
}}
|
||||
searchable
|
||||
clearable={false} // role harus ada
|
||||
nothingFoundMessage="Role tidak ditemukan"
|
||||
/>
|
||||
</TableTd>
|
||||
|
||||
<TableTd style={{ width: '15%' }}>
|
||||
<Tooltip
|
||||
label={item.isActive ? "Nonaktifkan user" : "Aktifkan user"}
|
||||
@@ -106,8 +128,12 @@ function ListUser({ search }: { search: string }) {
|
||||
variant="light"
|
||||
color={item.isActive ? "green" : "red"}
|
||||
onClick={async () => {
|
||||
await stateUser.updateActive.submit(item.id, !item.isActive)
|
||||
stateUser.findMany.load(page, 10, search)
|
||||
await stateUser.update.submit({
|
||||
id: item.id,
|
||||
isActive: !item.isActive, // toggle
|
||||
});
|
||||
|
||||
stateUser.findMany.load(page, 10, search);
|
||||
}}
|
||||
>
|
||||
{item.isActive ? <IconCheck size={20} /> : <IconX size={20} />}
|
||||
|
||||
@@ -84,7 +84,6 @@ export const navBar = [
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
id: "Desa",
|
||||
name: "Desa",
|
||||
@@ -387,12 +386,377 @@ export const navBar = [
|
||||
{
|
||||
id: "User",
|
||||
name: "User",
|
||||
path: "/admin/user/user"
|
||||
path: "/admin/user&role/user"
|
||||
},
|
||||
{
|
||||
id: "Role",
|
||||
name: "Role",
|
||||
path: "/admin/role/role"
|
||||
path: "/admin/user&role/role"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export const role1 = [
|
||||
{
|
||||
id: "Landing Page",
|
||||
name: "Landing Page",
|
||||
path: "",
|
||||
children: [
|
||||
{
|
||||
id: "Landing_Page_1",
|
||||
name: "Profil",
|
||||
path: "/admin/landing-page/profil/program-inovasi"
|
||||
},
|
||||
{
|
||||
id: "Landing_Page_2",
|
||||
name: "Desa Anti Korupsi",
|
||||
path: "/admin/landing-page/desa-anti-korupsi/list-desa-anti-korupsi"
|
||||
},
|
||||
{
|
||||
id: "Landing_Page_3",
|
||||
name: "Indeks Kepuasan Masyarakat",
|
||||
path: "/admin/landing-page/indeks-kepuasan-masyarakat/grafik-kepuasan-masyarakat"
|
||||
},
|
||||
{
|
||||
id: "Landing_Page_4",
|
||||
name: "SDGs",
|
||||
path: "/admin/landing-page/SDGs"
|
||||
},
|
||||
{
|
||||
id: "Landing_Page_5",
|
||||
name: "APBDes",
|
||||
path: "/admin/landing-page/apbdes"
|
||||
},
|
||||
{
|
||||
id: "Landing_Page_6",
|
||||
name: "Prestasi Desa",
|
||||
path: "/admin/landing-page/prestasi-desa/list-prestasi-desa"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "PPID",
|
||||
name: "PPID",
|
||||
path: "",
|
||||
children: [
|
||||
{
|
||||
id: "PPID_1",
|
||||
name: "Profil PPID",
|
||||
path: "/admin/ppid/profil-ppid"
|
||||
},
|
||||
{
|
||||
id: "PPID_2",
|
||||
name: "Struktur PPID",
|
||||
path: "/admin/ppid/struktur-ppid/pegawai"
|
||||
},
|
||||
{
|
||||
id: "PPID_3",
|
||||
name: "Visi Misi PPID",
|
||||
path: "/admin/ppid/visi-misi-ppid"
|
||||
},
|
||||
{
|
||||
id: "PPID_4",
|
||||
name: "Dasar Hukum",
|
||||
path: "/admin/ppid/dasar-hukum"
|
||||
},
|
||||
{
|
||||
id: "PPID_5",
|
||||
name: "Permohonan Informasi Publik",
|
||||
path: "/admin/ppid/permohonan-informasi-publik"
|
||||
},
|
||||
{
|
||||
id: "PPID_6",
|
||||
name: "Permohonan Keberatan Informasi Publik",
|
||||
path: "/admin/ppid/permohonan-keberatan-informasi-publik"
|
||||
},
|
||||
{
|
||||
id: "PPID_7",
|
||||
name: "Daftar Informasi Publik",
|
||||
path: "/admin/ppid/daftar-informasi-publik"
|
||||
},
|
||||
{
|
||||
id: "PPID_8",
|
||||
name: "Indeks Kepuasan Masyarakat",
|
||||
path: "/admin/ppid/indeks-kepuasan-masyarakat/grafik-kepuasan-masyarakat"
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "Desa",
|
||||
name: "Desa",
|
||||
path: "",
|
||||
children: [
|
||||
{
|
||||
id: "Desa_1",
|
||||
name: "Profile",
|
||||
path: "/admin/desa/profile/profile-desa"
|
||||
},
|
||||
{
|
||||
id: "Desa_2",
|
||||
name: "Potensi",
|
||||
path: "/admin/desa/potensi/list-potensi"
|
||||
},
|
||||
{
|
||||
id: "Desa_3",
|
||||
name: "Berita",
|
||||
path: "/admin/desa/berita/list-berita"
|
||||
},
|
||||
{
|
||||
id: "Desa_4",
|
||||
name: "Pengumuman",
|
||||
path: "/admin/desa/pengumuman/list-pengumuman"
|
||||
},
|
||||
{
|
||||
id: "Desa_5",
|
||||
name: "Gallery",
|
||||
path: "/admin/desa/gallery/foto"
|
||||
},
|
||||
{
|
||||
id: "Desa_6",
|
||||
name: "Layanan",
|
||||
path: "/admin/desa/layanan/pelayanan_surat_keterangan"
|
||||
},
|
||||
{
|
||||
id: "Desa_7",
|
||||
name: "Penghargaan",
|
||||
path: "/admin/desa/penghargaan"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "Keamanan",
|
||||
name: "Keamanan",
|
||||
path: "",
|
||||
children: [
|
||||
{
|
||||
id: "Keamanan_1",
|
||||
name: "Keamanan Lingkungan (Pecalang/Patwal)",
|
||||
path: "/admin/keamanan/keamanan-lingkungan-pecalang-patwal"
|
||||
},
|
||||
{
|
||||
id: "Keamanan_2",
|
||||
name: "Polsek Terdekat",
|
||||
path: "/admin/keamanan/polsek-terdekat"
|
||||
},
|
||||
{
|
||||
id: "Keamanan_3",
|
||||
name: "Kontak Darurat",
|
||||
path: "/admin/keamanan/kontak-darurat/kontak-darurat-keamanan"
|
||||
},
|
||||
{
|
||||
id: "Keamanan_4",
|
||||
name: "Pencegahan Kriminalitas",
|
||||
path: "/admin/keamanan/pencegahan-kriminalitas"
|
||||
},
|
||||
{
|
||||
id: "Keamanan_5",
|
||||
name: "Laporan Publik",
|
||||
path: "/admin/keamanan/laporan-publik"
|
||||
},
|
||||
{
|
||||
id: "Keamanan_6",
|
||||
name: "Tips Keamanan",
|
||||
path: "/admin/keamanan/tips-keamanan"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "Ekonomi",
|
||||
name: "Ekonomi",
|
||||
path: "",
|
||||
children: [
|
||||
{
|
||||
id: "Ekonomi_1",
|
||||
name: "Pasar Desa",
|
||||
path: "/admin/ekonomi/pasar-desa/produk-pasar-desa"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_2",
|
||||
name: "Lowongan Kerja Lokal",
|
||||
path: "/admin/ekonomi/lowongan-kerja-lokal"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_3",
|
||||
name: "Struktur Organisasi Dan Sk Pengurus Bumdesa",
|
||||
path: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_4",
|
||||
name: "PADesa (Pendapatan Asli Desa)",
|
||||
path: "/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_5",
|
||||
name: "Jumlah Pengangguran",
|
||||
path: "/admin/ekonomi/jumlah-pengangguran"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_6",
|
||||
name: "Jumlah penduduk usia kerja yang menganggur",
|
||||
path: "/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_7",
|
||||
name: "Jumlah Penduduk Miskin",
|
||||
path: "/admin/ekonomi/jumlah-penduduk-miskin"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_8",
|
||||
name: "Program Kemiskinan",
|
||||
path: "/admin/ekonomi/program-kemiskinan"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_9",
|
||||
name: "Sektor Unggulan Desa",
|
||||
path: "/admin/ekonomi/sektor-unggulan-desa"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_10",
|
||||
name: "Demografi Pekerjaan",
|
||||
path: "/admin/ekonomi/demografi-pekerjaan"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "Inovasi",
|
||||
name: "Inovasi",
|
||||
path: "",
|
||||
children: [
|
||||
{
|
||||
id: "Inovasi_1",
|
||||
name: "Desa Digital/Smart Village",
|
||||
path: "/admin/inovasi/desa-digital-smart-village"
|
||||
},
|
||||
{
|
||||
id: "Inovasi_2",
|
||||
name: "Layanan Online Desa",
|
||||
path: "/admin/inovasi/layanan-online-desa/administrasi-online"
|
||||
},
|
||||
{
|
||||
id: "Inovasi_3",
|
||||
name: "Program Kreatif Desa",
|
||||
path: "/admin/inovasi/program-kreatif-desa"
|
||||
},
|
||||
{
|
||||
id: "Inovasi_4",
|
||||
name: "Kolaborasi Inovasi",
|
||||
path: "/admin/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi"
|
||||
},
|
||||
{
|
||||
id: "Inovasi_5",
|
||||
name: "Info Teknologi Tepat Guna",
|
||||
path: "/admin/inovasi/info-teknologi-tepat-guna"
|
||||
},
|
||||
{
|
||||
id: "Inovasi_6",
|
||||
name: "Ajukan Ide Inovatif",
|
||||
path: "/admin/inovasi/ajukan-ide-inovatif"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "Lingkungan",
|
||||
name: "Lingkungan",
|
||||
path: "",
|
||||
children: [
|
||||
{
|
||||
id: "Lingkungan_1",
|
||||
name: "Pengelolaan Sampah (Bank Sampah)",
|
||||
path: "/admin/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah"
|
||||
},
|
||||
{
|
||||
id: "Lingkungan_2",
|
||||
name: "Program Penghijauan",
|
||||
path: "/admin/lingkungan/program-penghijauan"
|
||||
},
|
||||
{
|
||||
id: "Lingkungan_3",
|
||||
name: "Data Lingkungan Desa",
|
||||
path: "/admin/lingkungan/data-lingkungan-desa"
|
||||
},
|
||||
{
|
||||
id: "Lingkungan_4",
|
||||
name: "Gotong Royong",
|
||||
path: "/admin/lingkungan/gotong-royong/kegiatan-desa"
|
||||
},
|
||||
{
|
||||
id: "Lingkungan_5",
|
||||
name: "Edukasi Lingkungan",
|
||||
path: "/admin/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan"
|
||||
},
|
||||
{
|
||||
id: "Lingkungan_6",
|
||||
name: "Konservasi Adat Bali",
|
||||
path: "/admin/lingkungan/konservasi-adat-bali/filosofi-tri-hita-karana"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export const role2 = [
|
||||
{
|
||||
id: "Kesehatan",
|
||||
name: "Kesehatan",
|
||||
path: "",
|
||||
children: [
|
||||
{
|
||||
id: "Kesehatan_1",
|
||||
name: "Posyandu",
|
||||
path: "/admin/kesehatan/posyandu"
|
||||
},
|
||||
{
|
||||
id: "Kesehatan_2",
|
||||
name: "Data Kesehatan Warga",
|
||||
path: "/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian"
|
||||
},
|
||||
{
|
||||
id: "Kesehatan_3",
|
||||
name: "Puskesmas",
|
||||
path: "/admin/kesehatan/puskesmas"
|
||||
},
|
||||
{
|
||||
id: "Kesehatan_4",
|
||||
name: "Program Kesehatan",
|
||||
path: "/admin/kesehatan/program-kesehatan"
|
||||
},
|
||||
{
|
||||
id: "Kesehatan_5",
|
||||
name: "Penanganan Darurat",
|
||||
path: "/admin/kesehatan/penanganan-darurat"
|
||||
},
|
||||
{
|
||||
id: "Kesehatan_6",
|
||||
name: "Kontak Darurat",
|
||||
path: "/admin/kesehatan/kontak-darurat"
|
||||
},
|
||||
{
|
||||
id: "Kesehatan_7",
|
||||
name: "Info Wabah/Penyakit",
|
||||
path: "/admin/kesehatan/info-wabah-penyakit"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export const role3 = [
|
||||
{
|
||||
id: "Pendidikan",
|
||||
name: "Pendidikan",
|
||||
path: "",
|
||||
children: [
|
||||
{
|
||||
id: "Pendidikan_1",
|
||||
name: "Info Sekolah",
|
||||
path: "/admin/pendidikan/info-sekolah/jenjang-pendidikan"
|
||||
},
|
||||
{
|
||||
id: "Pendidikan_2",
|
||||
name: "Beasiswa Desa",
|
||||
path: "/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar"
|
||||
},
|
||||
{
|
||||
id: "Pendidikan_3",
|
||||
@@ -422,5 +786,3 @@ export const navBar = [
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
9
src/app/admin/_com/navigationByRole.ts
Normal file
9
src/app/admin/_com/navigationByRole.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// src/app/(admin)/_com/navigationByRole.ts
|
||||
import { navBar, role1, role2, role3 } from './list_PageAdmin';
|
||||
|
||||
export const navigationByRole = {
|
||||
0: navBar, // SUPERADMIN
|
||||
1: role1, // ADMIN DESA
|
||||
2: role2, // ADMIN KESEHATAN
|
||||
3: role3, // ADMIN PENDIDIKAN
|
||||
} as const;
|
||||
@@ -1,6 +1,269 @@
|
||||
// 'use client'
|
||||
|
||||
// import colors from "@/con/colors";
|
||||
// import {
|
||||
// ActionIcon,
|
||||
// AppShell,
|
||||
// AppShellHeader,
|
||||
// AppShellMain,
|
||||
// AppShellNavbar,
|
||||
// Burger,
|
||||
// Flex,
|
||||
// Group,
|
||||
// Image,
|
||||
// NavLink,
|
||||
// ScrollArea,
|
||||
// Text,
|
||||
// Tooltip,
|
||||
// rem
|
||||
// } from "@mantine/core";
|
||||
// import { useDisclosure } from "@mantine/hooks";
|
||||
// import {
|
||||
// IconChevronLeft,
|
||||
// IconChevronRight,
|
||||
// IconLogout2
|
||||
// } from "@tabler/icons-react";
|
||||
// import _ from "lodash";
|
||||
// import Link from "next/link";
|
||||
// import { useRouter, useSelectedLayoutSegments } from "next/navigation";
|
||||
// import { navBar } from "./_com/list_PageAdmin";
|
||||
|
||||
// export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
// const [opened, { toggle }] = useDisclosure();
|
||||
// const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true);
|
||||
// const router = useRouter();
|
||||
// const segments = useSelectedLayoutSegments().map((s) => _.lowerCase(s));
|
||||
|
||||
// return (
|
||||
// <AppShell
|
||||
// suppressHydrationWarning
|
||||
// header={{ height: 64 }}
|
||||
// navbar={{
|
||||
// width: { base: 260, sm: 280, lg: 300 },
|
||||
// breakpoint: 'sm',
|
||||
// collapsed: {
|
||||
// mobile: !opened,
|
||||
// desktop: !desktopOpened,
|
||||
// },
|
||||
// }}
|
||||
// padding="md"
|
||||
// >
|
||||
// <AppShellHeader
|
||||
// style={{
|
||||
// background: "linear-gradient(90deg, #ffffff, #f9fbff)",
|
||||
// borderBottom: `1px solid ${colors["blue-button"]}20`,
|
||||
// padding: '0 16px',
|
||||
// }}
|
||||
// px={{ base: 'sm', sm: 'md' }}
|
||||
// py={{ base: 'xs', sm: 'sm' }}
|
||||
// >
|
||||
// <Group w="100%" h="100%" justify="space-between" wrap="nowrap">
|
||||
// <Flex align="center" gap="sm">
|
||||
// <Image
|
||||
// src="/assets/images/darmasaba-icon.png"
|
||||
// alt="Logo Darmasaba"
|
||||
// w={{ base: 32, sm: 40 }}
|
||||
// h={{ base: 32, sm: 40 }}
|
||||
// radius="md"
|
||||
// loading="lazy"
|
||||
// style={{
|
||||
// minWidth: '32px',
|
||||
// height: 'auto',
|
||||
// }}
|
||||
// />
|
||||
// <Text
|
||||
// fw={700}
|
||||
// c={colors["blue-button"]}
|
||||
// fz={{ base: 'md', sm: 'xl' }}
|
||||
// >
|
||||
// Admin Darmasaba
|
||||
// </Text>
|
||||
// </Flex>
|
||||
|
||||
// <Group gap="xs">
|
||||
// {!desktopOpened && (
|
||||
// <Tooltip label="Buka Navigasi" position="bottom" withArrow>
|
||||
// <ActionIcon
|
||||
// variant="light"
|
||||
// radius="xl"
|
||||
// size="lg"
|
||||
// onClick={toggleDesktop}
|
||||
// color={colors["blue-button"]}
|
||||
// >
|
||||
// <IconChevronRight />
|
||||
// </ActionIcon>
|
||||
// </Tooltip>
|
||||
// )}
|
||||
|
||||
// <Burger
|
||||
// opened={opened}
|
||||
// onClick={toggle}
|
||||
// hiddenFrom="sm"
|
||||
// size="md"
|
||||
// color={colors["blue-button"]}
|
||||
// mr="xs"
|
||||
// />
|
||||
|
||||
// <Tooltip label="Kembali ke Website Desa" position="bottom" withArrow>
|
||||
// <ActionIcon
|
||||
// onClick={() => {
|
||||
// router.push("/darmasaba");
|
||||
// }}
|
||||
// color={colors["blue-button"]}
|
||||
// radius="xl"
|
||||
// size="lg"
|
||||
// variant="gradient"
|
||||
// gradient={{ from: colors["blue-button"], to: "#228be6" }}
|
||||
// >
|
||||
// <Image
|
||||
// src="/assets/images/darmasaba-icon.png"
|
||||
// alt="Logo Darmasaba"
|
||||
// w={20}
|
||||
// h={20}
|
||||
// radius="md"
|
||||
// loading="lazy"
|
||||
// style={{
|
||||
// minWidth: '20px',
|
||||
// height: 'auto',
|
||||
// }}
|
||||
// />
|
||||
// </ActionIcon>
|
||||
// </Tooltip>
|
||||
// <Tooltip label="Keluar" position="bottom" withArrow>
|
||||
// <ActionIcon
|
||||
// onClick={() => {
|
||||
// router.push("/darmasaba");
|
||||
// }}
|
||||
// color={colors["blue-button"]}
|
||||
// radius="xl"
|
||||
// size="lg"
|
||||
// variant="gradient"
|
||||
// gradient={{ from: colors["blue-button"], to: "#228be6" }}
|
||||
// >
|
||||
// <IconLogout2 size={22} />
|
||||
// </ActionIcon>
|
||||
// </Tooltip>
|
||||
// </Group>
|
||||
// </Group>
|
||||
// </AppShellHeader>
|
||||
|
||||
// <AppShellNavbar
|
||||
// component={ScrollArea}
|
||||
// style={{
|
||||
// background: "#ffffff",
|
||||
// borderRight: `1px solid ${colors["blue-button"]}20`,
|
||||
// }}
|
||||
// p={{ base: 'xs', sm: 'sm' }}
|
||||
// >
|
||||
// <AppShell.Section p="sm">
|
||||
// {navBar.map((v, k) => {
|
||||
// const isParentActive = segments.includes(_.lowerCase(v.name));
|
||||
|
||||
// return (
|
||||
// <NavLink
|
||||
// key={k}
|
||||
// defaultOpened={isParentActive}
|
||||
// c={isParentActive ? colors["blue-button"] : "gray"}
|
||||
// label={
|
||||
// <Text fw={isParentActive ? 600 : 400} fz="sm">
|
||||
// {v.name}
|
||||
// </Text>
|
||||
// }
|
||||
// style={{
|
||||
// borderRadius: rem(10),
|
||||
// marginBottom: rem(4),
|
||||
// transition: "background 150ms ease",
|
||||
// }}
|
||||
// styles={{
|
||||
// root: {
|
||||
// '&:hover': {
|
||||
// backgroundColor: 'rgba(25, 113, 194, 0.05)',
|
||||
// },
|
||||
// },
|
||||
// }}
|
||||
// variant="light"
|
||||
// active={isParentActive}
|
||||
// >
|
||||
// {v.children.map((child, key) => {
|
||||
// const isChildActive = segments.includes(
|
||||
// _.lowerCase(child.name)
|
||||
// );
|
||||
|
||||
// return (
|
||||
// <NavLink
|
||||
// key={key}
|
||||
// href={child.path}
|
||||
// c={isChildActive ? colors["blue-button"] : "gray"}
|
||||
// label={
|
||||
// <Text fw={isChildActive ? 600 : 400} fz="sm">
|
||||
// {child.name}
|
||||
// </Text>
|
||||
// }
|
||||
// styles={{
|
||||
// root: {
|
||||
// borderRadius: rem(8),
|
||||
// marginBottom: rem(2),
|
||||
// transition: 'background 150ms ease',
|
||||
// padding: '6px 12px',
|
||||
// '&:hover': {
|
||||
// backgroundColor: isChildActive ? 'rgba(25, 113, 194, 0.15)' : 'rgba(25, 113, 194, 0.05)',
|
||||
// },
|
||||
// ...(isChildActive && {
|
||||
// backgroundColor: 'rgba(25, 113, 194, 0.1)',
|
||||
// }),
|
||||
// },
|
||||
// }}
|
||||
// active={isChildActive}
|
||||
// component={Link}
|
||||
// />
|
||||
// );
|
||||
// })}
|
||||
// </NavLink>
|
||||
// );
|
||||
// })}
|
||||
// </AppShell.Section>
|
||||
|
||||
// <AppShell.Section py="md">
|
||||
// <Group justify="end" pr="sm">
|
||||
// <Tooltip
|
||||
// label={desktopOpened ? "Tutup Navigasi" : "Buka Navigasi"}
|
||||
// position="top"
|
||||
// withArrow
|
||||
// >
|
||||
// <ActionIcon
|
||||
// variant="light"
|
||||
// radius="xl"
|
||||
// size="lg"
|
||||
// onClick={toggleDesktop}
|
||||
// color={colors["blue-button"]}
|
||||
// >
|
||||
// <IconChevronLeft />
|
||||
// </ActionIcon>
|
||||
// </Tooltip>
|
||||
// </Group>
|
||||
// </AppShell.Section>
|
||||
// </AppShellNavbar>
|
||||
|
||||
// <AppShellMain
|
||||
// style={{
|
||||
// background: "linear-gradient(180deg, #fdfdfd, #f6f9fc)",
|
||||
// minHeight: "100vh",
|
||||
// }}
|
||||
// >
|
||||
// {children}
|
||||
// </AppShellMain>
|
||||
// </AppShell>
|
||||
// );
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
'use client'
|
||||
|
||||
import colors from "@/con/colors";
|
||||
import { authStore } from "@/store/authStore";
|
||||
import {
|
||||
ActionIcon,
|
||||
AppShell,
|
||||
@@ -26,7 +289,8 @@ import {
|
||||
import _ from "lodash";
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSelectedLayoutSegments } from "next/navigation";
|
||||
import { navBar } from "./_com/list_PageAdmin";
|
||||
import { useSnapshot } from "valtio";
|
||||
import { navigationByRole } from "./_com/navigationByRole";
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
const [opened, { toggle }] = useDisclosure();
|
||||
@@ -34,6 +298,15 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter();
|
||||
const segments = useSelectedLayoutSegments().map((s) => _.lowerCase(s));
|
||||
|
||||
//ambil user dari authStore
|
||||
const {user} = useSnapshot(authStore)
|
||||
console.log("Current User:", user); // 👈 Tambahkan ini
|
||||
|
||||
//ambil navigation berdasarkan role
|
||||
const currentNav = user?.roleId !== undefined
|
||||
? navigationByRole[user.roleId as keyof typeof navigationByRole] || []
|
||||
: [];
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
suppressHydrationWarning
|
||||
@@ -156,7 +429,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
p={{ base: 'xs', sm: 'sm' }}
|
||||
>
|
||||
<AppShell.Section p="sm">
|
||||
{navBar.map((v, k) => {
|
||||
{currentNav.map((v, k) => {
|
||||
const isParentActive = segments.includes(_.lowerCase(v.name));
|
||||
|
||||
return (
|
||||
@@ -255,3 +528,4 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,16 @@ const User = new Elysia({ prefix: "/api/user" })
|
||||
id: t.String(),
|
||||
}),
|
||||
}) // pakai PUT untuk soft delete
|
||||
.put("/updt", userUpdate);
|
||||
.put(
|
||||
"/updt",
|
||||
userUpdate,
|
||||
{
|
||||
body: t.Object({
|
||||
id: t.String(),
|
||||
isActive: t.Optional(t.Boolean()),
|
||||
roleId: t.Optional(t.String()),
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
export default User;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Context } from "elysia";
|
||||
|
||||
type FormCreate = {
|
||||
name: string;
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
export default async function roleCreate(context: Context) {
|
||||
@@ -13,7 +12,6 @@ export default async function roleCreate(context: Context) {
|
||||
const result = await prisma.role.create({
|
||||
data: {
|
||||
name: body.name,
|
||||
permissions: body.permissions,
|
||||
},
|
||||
});
|
||||
return {
|
||||
|
||||
@@ -13,7 +13,6 @@ const Role = new Elysia({
|
||||
.post("/create", roleCreate, {
|
||||
body: t.Object({
|
||||
name: t.String(),
|
||||
permissions: t.Array(t.String()),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -27,7 +26,6 @@ const Role = new Elysia({
|
||||
.put("/:id", roleUpdate, {
|
||||
body: t.Object({
|
||||
name: t.String(),
|
||||
permissions: t.Array(t.String()),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", roleDelete);
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Context } from "elysia";
|
||||
|
||||
type FormUpdate = {
|
||||
name: string;
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
export default async function roleUpdate(context: Context) {
|
||||
@@ -15,7 +14,6 @@ export default async function roleUpdate(context: Context) {
|
||||
where: { id },
|
||||
data: {
|
||||
name: body.name,
|
||||
permissions: body.permissions,
|
||||
},
|
||||
});
|
||||
return {
|
||||
|
||||
@@ -4,7 +4,11 @@ import { Context } from "elysia";
|
||||
|
||||
export default async function userUpdate(context: Context) {
|
||||
try {
|
||||
const { id, isActive } = await context.body as { id: string, isActive: boolean };
|
||||
const { id, isActive, roleId } = await context.body as {
|
||||
id: string,
|
||||
isActive?: boolean,
|
||||
roleId?: string
|
||||
};
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
@@ -13,28 +17,53 @@ export default async function userUpdate(context: Context) {
|
||||
};
|
||||
}
|
||||
|
||||
// Optional: cek apakah roleId valid
|
||||
if (roleId) {
|
||||
const cekRole = await prisma.role.findUnique({
|
||||
where: { id: roleId }
|
||||
});
|
||||
|
||||
if (!cekRole) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Role tidak ditemukan",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const updatedUser = await prisma.user.update({
|
||||
where: { id },
|
||||
data: { isActive },
|
||||
data: {
|
||||
...(isActive !== undefined && { isActive }),
|
||||
...(roleId && { roleId }),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
nomor: true,
|
||||
isActive: true,
|
||||
roleId: true,
|
||||
updatedAt: true,
|
||||
role: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `User berhasil ${isActive ? "diaktifkan" : "dinonaktifkan"}`,
|
||||
message: `User berhasil diupdate`,
|
||||
data: updatedUser,
|
||||
};
|
||||
|
||||
} catch (e: any) {
|
||||
console.error("Error update user:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengupdate status user",
|
||||
message: "Gagal mengupdate user",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,11 +108,18 @@ export async function POST(req: Request) {
|
||||
data: { isActive: false },
|
||||
});
|
||||
|
||||
const userData = {
|
||||
id: user.id,
|
||||
name: user.username, // atau user.nama jika ada kolom nama
|
||||
roleId: user.roleId,
|
||||
};
|
||||
|
||||
// Set cookie & respons
|
||||
const response = NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
message: "Berhasil login",
|
||||
user: userData,
|
||||
roleId: user.roleId,
|
||||
},
|
||||
{ status: 200 }
|
||||
|
||||
17
src/store/authStore.ts
Normal file
17
src/store/authStore.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { proxy } from 'valtio';
|
||||
|
||||
export type User = {
|
||||
id: string;
|
||||
name: string;
|
||||
roleId: number; // 0, 1, 2, 3
|
||||
};
|
||||
|
||||
export const authStore = proxy<{
|
||||
user: User | null;
|
||||
setUser: (user: User | null) => void;
|
||||
}>({
|
||||
user: null,
|
||||
setUser(user) {
|
||||
authStore.user = user;
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user