UI & API Menu PPID, Submenu Struktur PPID

This commit is contained in:
2025-07-31 16:22:02 +08:00
parent 4e61695649
commit 024d5517fa
62 changed files with 3790 additions and 712 deletions

View File

@@ -1,169 +1,683 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
name: z.string().min(3, "Nama minimal 3 karakter"),
imageId: z.string().min(1, "Gambar wajib dipilih"),
})
name: z.string().min(3, "Nama minimal 3 karakter"),
imageId: z.string().min(1, "Gambar wajib dipilih"),
});
const defaultForm = {
name: "",
imageId: "",
name: "",
imageId: "",
};
type StrukturPPIDForm = Prisma.StrukturPPIDGetPayload<{
select: {
id: true;
name: true;
imageId: true;
image?: {
select: {
link: true;
};
};
select: {
id: true;
name: true;
imageId: true;
image?: {
select: {
link: true;
};
};
};
}>;
const stateStrukturPPID = proxy({
struktur: {
data: null as StrukturPPIDForm | null,
loading: false,
error: null as string | null,
const stateStruktur = proxy({
struktur: {
data: null as StrukturPPIDForm | null,
loading: false,
error: null as string | null,
async load(id: string) {
if(!id) {
toast.warn("ID tidak valid")
return null
}
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
this.loading = true;
this.error = null;
this.loading = true;
this.error = null;
try {
const response = await fetch(`/api/ppid/strukturppid/${id}`);
if(!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
try {
const response = await fetch(`/api/ppid/strukturppid/${id}`);
const result = await response.json();
if(result.success) {
this.data = result.data;
return result.data
} else {
throw new Error(result.message || "Gagal mengambil data struktur")
}
} catch (error) {
const errorMessage = (error as Error).message;
this.error = errorMessage;
console.error("Load struktur error:", errorMessage);
toast.error("Terjadi kesalahan saat mengambil data struktur");
return null;
} finally {
this.loading = false;
}
},
reset() {
this.data = null;
this.error = null;
this.loading = false;
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
},
editStruktur: {
id: "",
form: { ...defaultForm },
loading: false,
error: null as string | null,
isReadOnly: false,
const result = await response.json();
initialize(strukturData: StrukturPPIDForm) {
this.id = strukturData.id;
this.isReadOnly = false;
this.form = {
name: strukturData.name || "",
imageId: strukturData.imageId || "",
};
},
updateField(field: keyof typeof defaultForm, value: string) {
this.form[field] = value;
},
async submit() {
const validation = templateForm.safeParse(this.form);
if (!validation.success) {
const errors = validation.error.issues
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
.join(", ");
toast.error(`Form tidak valid: ${errors}`);
return false;
}
this.loading = true;
this.error = null;
try {
const response = await fetch(`/api/ppid/strukturppid/${this.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(this.form),
})
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.success) {
toast.success("Berhasil update struktur");
await stateStrukturPPID.struktur.load(this.id);
return true;
} else {
throw new Error(result.message || "Gagal update struktur");
}
} catch (error) {
const errorMessage = (error as Error).message;
this.error = errorMessage;
console.error("Update struktur error:", errorMessage);
toast.error("Terjadi kesalahan saat update struktur");
return false;
} finally {
this.loading = false;
}
},
reset() {
this.id = "";
this.form = { ...defaultForm };
this.error = null;
this.loading = false;
this.isReadOnly = false;
if (result.success) {
this.data = result.data;
return result.data;
} else {
throw new Error(result.message || "Gagal mengambil data struktur");
}
},
async loadForEdit(id: string) {
const strukturData = await this.struktur.load(id);
if (strukturData) {
this.editStruktur.initialize(strukturData);
}
return strukturData;
} catch (error) {
const errorMessage = (error as Error).message;
this.error = errorMessage;
console.error("Load struktur error:", errorMessage);
toast.error("Terjadi kesalahan saat mengambil data struktur");
return null;
} finally {
this.loading = false;
}
},
reset() {
this.struktur.reset();
this.editStruktur.reset();
this.data = null;
this.error = null;
this.loading = false;
},
},
editStruktur: {
id: "",
form: { ...defaultForm },
loading: false,
error: null as string | null,
isReadOnly: false,
initialize(strukturData: StrukturPPIDForm) {
this.id = strukturData.id;
this.isReadOnly = false;
this.form = {
name: strukturData.name || "",
imageId: strukturData.imageId || "",
};
},
updateField(field: keyof typeof defaultForm, value: string) {
this.form[field] = value;
},
async submit() {
const validation = templateForm.safeParse(this.form);
if (!validation.success) {
const errors = validation.error.issues
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
.join(", ");
toast.error(`Form tidak valid: ${errors}`);
return false;
}
this.loading = true;
this.error = null;
try {
const response = await fetch(`/api/ppid/strukturppid/${this.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(this.form),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message || `HTTP error! status: ${response.status}`
);
}
const result = await response.json();
if (result.success) {
toast.success("Berhasil update struktur");
await stateStruktur.struktur.load(this.id);
return true;
} else {
throw new Error(result.message || "Gagal update struktur");
}
} catch (error) {
const errorMessage = (error as Error).message;
this.error = errorMessage;
console.error("Update struktur error:", errorMessage);
toast.error("Terjadi kesalahan saat update struktur");
return false;
} finally {
this.loading = false;
}
},
reset() {
this.id = "";
this.form = { ...defaultForm };
this.error = null;
this.loading = false;
this.isReadOnly = false;
},
},
async loadForEdit(id: string) {
const strukturData = await this.struktur.load(id);
if (strukturData) {
this.editStruktur.initialize(strukturData);
}
})
return strukturData;
},
reset() {
this.struktur.reset();
this.editStruktur.reset();
},
});
const templatePosisiOrganisasi = z.object({
nama: z.string().min(1, "Nama harus diisi"),
deskripsi: z.string().optional(),
hierarki: z.number().int().positive("Hierarki harus angka positif"),
});
const posisiOrganisasiDefaultForm = {
nama: "",
deskripsi: "",
hierarki: 0,
};
const posisiOrganisasi = proxy({
create: {
form: { ...posisiOrganisasiDefaultForm },
loading: false,
async submit() {
const cek = templatePosisiOrganisasi.safeParse(this.form);
if (!cek.success) {
const err = cek.error.issues.map((v) => v.message).join("\n");
return toast.error(err);
}
try {
this.loading = true;
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi[
"create"
].post(this.form);
if (res.status === 200) {
toast.success("Berhasil menambahkan posisi organisasi");
posisiOrganisasi.findMany.load();
this.reset();
} else {
toast.error(res.data?.message || "Gagal menambahkan posisi");
}
} catch (error) {
console.error("Create error:", error);
toast.error("Terjadi kesalahan saat menambahkan posisi");
} finally {
this.loading = false;
}
},
reset() {
this.form = { ...posisiOrganisasiDefaultForm };
},
},
findUnique: {
data: null as Prisma.StrukturOrganisasiPPIDGetPayload<{
omit: { isActive: true };
}> | null,
async load(id: string) {
try {
const res = await fetch(
`/api/ppid/strukturppid/posisiorganisasi/${id}`
);
if (res.ok) {
const data = await res.json();
posisiOrganisasi.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch posisiOrganisasi:", res.statusText);
posisiOrganisasi.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching posisiOrganisasi:", error);
posisiOrganisasi.findUnique.data = null;
}
},
},
edit: {
id: "",
form: { ...posisiOrganisasiDefaultForm },
loading: false,
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(
`/api/ppid/strukturppid/posisiorganisasi/${id}`,
{
method: "GET",
headers: {
"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 = {
nama: data.nama,
deskripsi: data.deskripsi,
hierarki: data.hierarki,
};
return data;
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading posisi organisasi:", error);
toast.error(
error instanceof Error ? error.message : "Gagal memuat data"
);
return null;
}
},
async update() {
const cek = templatePosisiOrganisasi.safeParse(this.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
this.loading = true;
const response = await fetch(
`/api/ppid/strukturppid/posisiorganisasi/${this.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
nama: this.form.nama,
deskripsi: this.form.deskripsi,
hierarki: this.form.hierarki,
}),
}
);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message || `HTTP error! status: ${response.status}`
);
}
const result = await response.json();
if (result.success) {
toast.success("Berhasil update posisi organisasi");
await posisiOrganisasi.findMany.load(); // refresh list
return true;
} else {
throw new Error(
result.message || "Gagal mengupdate posisi organisasi"
);
}
} catch (error) {
console.error("Error updating posisi organisasi:", error);
toast.error(
error instanceof Error
? error.message
: "Gagal mengupdate posisi organisasi"
);
return false;
} finally {
this.loading = false;
}
},
reset() {
this.id = "";
this.form = { ...posisiOrganisasiDefaultForm };
},
},
findMany: {
data: [] as Array<{
id: string;
nama: string;
deskripsi: string | null;
hierarki: number;
}>,
async load() {
try {
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi[
"find-many"
].get();
if (res.status === 200) {
// The API now returns the id field, so we can use it directly
this.data = res.data?.data ?? [];
}
} catch (error) {
console.error("Find many error:", error);
this.data = [];
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
posisiOrganisasi.delete.loading = true;
const response = await fetch(
`/api/ppid/strukturppid/posisiorganisasi/del/${id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Posisi organisasi berhasil dihapus");
await posisiOrganisasi.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus posisi organisasi");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus posisi organisasi");
} finally {
posisiOrganisasi.delete.loading = false;
}
},
},
});
const templatePegawai = z.object({
namaLengkap: z.string().min(1, "Nama wajib diisi"),
gelarAkademik: z.string().min(1, "Gelar Akademik wajib diisi"),
imageId: z.string().min(1, "Gambar wajib dipilih"),
tanggalMasuk: z.string().min(1, "Tanggal masuk wajib diisi"), // ISO format
email: z.string().email("Email tidak valid").optional(),
telepon: z.string().min(1, "Telepom wajib diisi"),
alamat: z.string().min(1, "Alamat wajib diisi"),
posisiId: z.string().min(1, "Posisi wajib diisi"),
isActive: z.boolean().default(true),
});
const pegawaiDefaultForm = {
namaLengkap: "",
gelarAkademik: "",
imageId: "",
tanggalMasuk: "",
email: "",
telepon: "",
alamat: "",
posisiId: "",
isActive: true,
};
const pegawai = proxy({
create: {
form: { ...pegawaiDefaultForm },
loading: false,
async submit() {
const cek = templatePegawai.safeParse(pegawai.create.form);
if (!cek.success) {
const err = cek.error.issues.map((i) => i.message).join("\n");
toast.error(err);
return;
}
try {
pegawai.create.loading = true;
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
"create"
].post(pegawai.create.form);
if (res.status === 200) {
toast.success("Pegawai berhasil ditambahkan");
await pegawai.findMany.load();
} else {
toast.error(res.data?.message ?? "Gagal tambah pegawai");
}
} catch (error) {
console.error("Gagal create:", error);
toast.error("Terjadi kesalahan saat menambahkan pegawai");
} finally {
pegawai.create.loading = false;
}
},
},
// In struktur-organisasi.ts
findMany: {
data: null as any[] | null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
pegawai.findMany.loading = true; // Use the full path to access the property
pegawai.findMany.page = page;
try {
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
"find-many"
].get({
query: { page, limit },
});
if (res.status === 200 && res.data?.success) {
pegawai.findMany.data = res.data.data || [];
pegawai.findMany.total = res.data.total || 0;
pegawai.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load pegawai:", res.data?.message);
pegawai.findMany.data = [];
pegawai.findMany.total = 0;
pegawai.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading pegawai:", error);
pegawai.findMany.data = [];
pegawai.findMany.total = 0;
pegawai.findMany.totalPages = 1;
} finally {
pegawai.findMany.loading = false;
}
},
},
findUnique: {
data: null as
| (Prisma.PegawaiGetPayload<{
include: { posisi: true; image: true };
}> & { isActive: boolean })
| null,
async load(id: string) {
const res = await fetch(`/api/ppid/strukturppid/pegawai/${id}`);
if (res.ok) {
const json = await res.json();
pegawai.findUnique.data = json.data
? {
...json.data,
isActive: json.data.isActive ?? json.data.aktif ?? true, // Fallback ke aktif:true jika tidak ada data
}
: null;
} else {
pegawai.findUnique.data = null;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
pegawai.delete.loading = true;
const res = await fetch(
`/api/ppid/strukturppid/pegawai/del/${id}`,
{
method: "DELETE",
}
);
const json = await res.json();
if (res.ok) {
toast.success(json.message ?? "Berhasil hapus pegawai");
await pegawai.findMany.load();
} else {
toast.error(json.message ?? "Gagal hapus pegawai");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus");
} finally {
pegawai.delete.loading = false;
}
},
},
edit: {
id: "",
form: { ...pegawaiDefaultForm },
loading: false,
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(
`/api/ppid/strukturppid/pegawai/${id}`,
{
method: "GET",
headers: {
"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 = {
namaLengkap: data.namaLengkap,
gelarAkademik: data.gelarAkademik,
imageId: data.imageId,
tanggalMasuk: data.tanggalMasuk,
email: data.email,
telepon: data.telepon,
alamat: data.alamat,
posisiId: data.posisiId,
isActive: data.isActive,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading berita:", error);
toast.error(
error instanceof Error ? error.message : "Gagal memuat data"
);
return null;
}
},
async submit() {
const cek = templatePegawai.safeParse(pegawai.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return false;
}
try {
pegawai.edit.loading = true;
// Format tanggalMasuk to ISO string if it exists
const formattedTanggalMasuk = this.form.tanggalMasuk
? new Date(this.form.tanggalMasuk).toISOString()
: undefined;
const response = await fetch(
`/api/ppid/strukturppid/pegawai/${this.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: this.id,
namaLengkap: this.form.namaLengkap,
gelarAkademik: this.form.gelarAkademik,
imageId: this.form.imageId || null,
tanggalMasuk: formattedTanggalMasuk,
email: this.form.email,
telepon: this.form.telepon,
alamat: this.form.alamat,
posisiId: this.form.posisiId,
isActive: this.form.isActive,
}),
}
);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message || `HTTP error! status: ${response.status}`
);
}
const result = await response.json();
if (result.success) {
toast.success("Berhasil update pegawai");
await pegawai.findMany.load(); // refresh list
return true;
} else {
throw new Error(result.message || "Gagal update pegawai");
}
} catch (error) {
console.error("Error updating pegawai:", error);
toast.error(
error instanceof Error
? error.message
: "Terjadi kesalahan saat update pegawai"
);
return false;
} finally {
pegawai.edit.loading = false;
}
},
reset() {
pegawai.edit.id = "";
pegawai.edit.form = { ...pegawaiDefaultForm };
},
},
});
const stateStrukturPPID = proxy({
stateStruktur,
posisiOrganisasi,
pegawai
});
export default stateStrukturPPID;

View File

@@ -1,220 +1,456 @@
import { proxy } from "valtio";
import { toast } from "react-toastify";
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { z } from "zod";
import { proxy } from 'valtio'
import { toast } from 'react-toastify'
import ApiFetch from '@/lib/api-fetch'
import { Prisma } from '@prisma/client'
import { z } from 'zod'
// 1. Validasi Zod
const userSchema = z.object({
nama: z.string().min(1, "Nama harus diisi"),
email: z.string().email("Email tidak valid"),
password: z.string().min(6, "Password minimal 6 karakter"),
nama: z.string().min(1, 'Nama harus diisi'),
email: z.string().email('Email tidak valid'),
password: z.string().min(6, 'Password minimal 6 karakter'),
roleId: z.string().optional(),
});
})
const defaultForm = {
nama: "",
email: "",
password: "",
roleId: "",
};
const defaultForm = { nama: '', email: '', password: '', roleId: '' }
// 2. State Valtio
const userState = proxy({
create: {
form: { ...defaultForm },
loading: false,
async create(isAdmin: boolean = false) {
const valid = userSchema.safeParse(userState.create.form);
if (!valid.success) {
const err = valid.error.issues.map((i) => i.message).join(", ");
return toast.error(err);
}
try {
userState.create.loading = true;
const res = await ApiFetch.api.user[
isAdmin ? "create" : "register"
].post(userState.create.form);
if (res.status === 200) {
toast.success("User berhasil dibuat");
userState.findMany.load();
} else {
toast.error(res.data?.message || "Gagal membuat user");
}
} catch (e) {
console.error(e);
toast.error("Terjadi kesalahan saat membuat user");
} finally {
userState.create.loading = false;
}
},
},
login: {
form: { email: "", password: "" },
loading: false,
async submit() {
try {
userState.login.loading = true;
const res = await ApiFetch.api.user.login.post(userState.login.form);
if (res.status === 200) {
toast.success("Login berhasil");
const token = res.data?.data?.token;
if (typeof token === "string") {
localStorage.setItem("token", token);
}
} else {
toast.error(res.data?.message || "Login gagal");
}
} catch (e) {
console.error(e);
toast.error("Terjadi kesalahan saat login");
} finally {
userState.login.loading = false;
}
},
},
// Register
register: {
form: { ...defaultForm },
loading: false,
async submit() {
const valid = userSchema.safeParse(userState.register.form);
const valid = userSchema.omit({ roleId: true }).safeParse(userState.register.form)
if (!valid.success) {
const err = valid.error.issues.map(i => i.message).join(", ");
return toast.error(err);
const err = valid.error.issues.map(i => i.message).join(', ')
return toast.error(err)
}
try {
userState.register.loading = true;
const res = await ApiFetch.api.user.register.post(userState.register.form);
userState.register.loading = true
const res = await ApiFetch.api.user.register.post(userState.register.form)
if (res.status === 200) {
toast.success("Registrasi berhasil, silakan login");
userState.register.form = { ...defaultForm }; // Reset form
toast.success('Registrasi berhasil, silakan login')
userState.register.form = { ...defaultForm } // reset
} else {
toast.error(res.data?.message || "Gagal registrasi");
toast.error(res.data?.message || 'Gagal registrasi')
}
} catch (e) {
console.error(e);
toast.error("Terjadi kesalahan saat registrasi");
console.error(e)
toast.error('Terjadi kesalahan saat registrasi')
} finally {
userState.register.loading = false;
userState.register.loading = false
}
},
},
// Login
login: {
form: { email: '', password: '' },
loading: false,
async submit() {
try {
userState.login.loading = true
const res = await ApiFetch.api.user.login.post(userState.login.form)
if (res.status === 200) {
toast.success('Login berhasil')
const token = res.data?.data?.token
if (typeof token === 'string') {
localStorage.setItem('token', token)
// Optional: simpan user role untuk otorisasi
const user = res.data?.data?.user
localStorage.setItem('user', JSON.stringify(user))
}
} else {
toast.error(res.data?.message || 'Login gagal')
}
} catch (e) {
console.error(e)
toast.error('Terjadi kesalahan saat login')
} finally {
userState.login.loading = false
}
},
},
// CRUD User (untuk admin)
create: {
form: { ...defaultForm },
loading: false,
async create(isAdmin = false) {
const valid = userSchema.safeParse(userState.create.form)
if (!valid.success) {
const err = valid.error.issues.map(i => i.message).join(', ')
return toast.error(err)
}
try {
userState.create.loading = true
const endpoint = isAdmin ? 'create' : 'register'
const res = await ApiFetch.api.user[endpoint].post(userState.create.form)
if (res.status === 200) {
toast.success('User berhasil dibuat')
userState.findMany.load() // refresh list
userState.create.form = { ...defaultForm } // reset form
} else {
toast.error(res.data?.message || 'Gagal membuat user')
}
} catch (e) {
console.error(e)
toast.error('Gagal membuat user')
} finally {
userState.create.loading = false
}
},
},
// Find Many
findMany: {
data: [] as Prisma.UserGetPayload<{ include: { role: true } }>[],
loading: false,
async load() {
userState.findMany.loading = true;
const res = await ApiFetch.api.user.findMany.get();
if (res.status === 200) {
userState.findMany.data = res.data?.data ?? [];
this.loading = true
try {
const res = await ApiFetch.api.user.findMany.get()
if (res.status === 200) {
this.data = res.data?.data || []
}
} catch (e) {
console.error(e)
toast.error('Gagal muat data user')
} finally {
this.loading = false
}
userState.findMany.loading = false;
},
},
// Find Unique
findUnique: {
data: null as Prisma.UserGetPayload<{ include: { role: true } }> | null,
loading: false,
async load(id: string) {
this.loading = true
try {
userState.findUnique.loading = true;
const res = await fetch(`/api/user/findUnique/${id}`);
const data = await res.json();
const res = await fetch(`/api/user/findUnique/${id}`)
const data = await res.json()
if (res.status === 200) {
userState.findUnique.data = data.data ?? null;
this.data = data.data
} else {
toast.error(data.message)
}
} catch (e) {
console.error(e);
toast.error("Terjadi kesalahan saat mengambil data user");
console.error(e)
toast.error('Gagal ambil data user')
} finally {
userState.findUnique.loading = false;
this.loading = false
}
},
},
// Update
update: {
id: "",
id: '',
form: { ...defaultForm },
loading: false,
async load(id: string) {
this.loading = true
try {
userState.update.loading = true;
const res = await fetch(`/api/user/findUnique/${id}`);
const data = await res.json();
const res = await fetch(`/api/user/findUnique/${id}`)
const data = await res.json()
if (res.status === 200) {
const user = data.data;
userState.update.id = user.id;
userState.update.form = {
const user = data.data
this.id = user.id
this.form = {
nama: user.nama,
email: user.email,
password: "",
password: '', // jangan kirim password lama
roleId: user.roleId,
};
}
}
} catch (e) {
console.error(e);
toast.error("Terjadi kesalahan saat mengambil data user");
console.error(e)
toast.error('Gagal muat data')
} finally {
userState.update.loading = false;
this.loading = false
}
},
async submit() {
this.loading = true
try {
userState.update.loading = true;
const res = await fetch(`/api/user/update/${userState.update.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(userState.update.form),
});
const data = await res.json();
const res = await fetch(`/api/user/update/${this.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(this.form),
})
const data = await res.json()
if (res.status === 200) {
toast.success("Berhasil update user");
userState.findMany.load();
toast.success('Update berhasil')
userState.findMany.load()
} else {
toast.error(data?.message || "Gagal update user");
toast.error(data.message || 'Gagal update')
}
} catch (e) {
console.error(e);
toast.error("Terjadi kesalahan saat update user");
console.error(e)
toast.error('Gagal update user')
} finally {
userState.update.loading = false;
this.loading = false
}
},
},
// Delete (Soft Delete)
delete: {
loading: false,
async submit(id: string) {
this.loading = true
try {
const res = await fetch(`/api/user/del/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
})
const data = await res.json()
if (res.status === 200) {
toast.success('User dinonaktifkan')
userState.findMany.load()
} else {
toast.error(data.message || 'Gagal hapus')
}
} catch (e) {
console.error(e)
toast.error('Gagal hapus user')
} finally {
this.loading = false
}
},
},
})
const templateRole = z.object({
name: z.string().min(1, "Nama harus diisi"),
});
const defaultRole = {
name: "",
};
const roleState = proxy({
create: {
form: { ...defaultRole },
loading: false,
async create() {
const cek = templateRole.safeParse(roleState.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
roleState.create.loading = true;
const res =
await ApiFetch.api.role[
"create"
].post(roleState.create.form);
if (res.status === 200) {
roleState.findMany.load();
return toast.success("Data role Berhasil Dibuat");
}
console.log(res);
return toast.error("failed create");
} catch (error) {
console.log(error);
return toast.error("failed create");
} finally {
roleState.create.loading = false;
}
},
},
findMany: {
data: [] as Prisma.RoleGetPayload<{
omit: {
isActive: true;
};
}>[],
loading: false,
async load() {
const res =
await ApiFetch.api.role[
"findMany"
].get();
if (res.status === 200) {
roleState.findMany.data = res.data?.data ?? [];
}
},
},
findUnique: {
data: null as Prisma.RoleGetPayload<{
omit: {
isActive: true;
};
}> | null,
loading: false,
async load(id: string) {
try {
const res = await fetch(
`/api/role/${id}`
);
if (res.ok) {
const data = await res.json();
roleState.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch data", res.status, res.statusText);
roleState.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching data:", error);
roleState.findUnique.data = null;
}
},
},
delete: {
loading: false,
async submit(id: string) {
async delete(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
userState.delete.loading = true;
const res = await fetch(`/api/user/del/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await res.json();
if (res.status === 200) {
toast.success("User berhasil dihapus");
userState.findMany.load();
roleState.delete.loading = true;
const response = await fetch(
`/api/role/del/${id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
const result = await response.json();
if (response.ok && result?.success) {
toast.success(
result.message || "Data role berhasil dihapus"
);
await roleState.findMany.load(); // refresh list
} else {
toast.error(data?.message || "Gagal hapus user");
toast.error(result?.message || "Gagal menghapus Data role");
}
} catch (e) {
console.error(e);
toast.error("Terjadi kesalahan saat hapus user");
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus Data role");
} finally {
userState.delete.loading = false;
roleState.delete.loading = false;
}
},
},
update: {
id: "",
form: { ...defaultRole },
loading: false,
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(
`/api/role/${id}`,
{
method: "GET",
headers: {
"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 = {
name: data.name,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading role:", error);
toast.error(
error instanceof Error ? error.message : "Gagal memuat data"
);
return null;
}
},
async update() {
const cek = templateRole.safeParse(roleState.update.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return false;
}
try {
roleState.update.loading = true;
const response = await fetch(
`/api/role/${this.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
}),
}
);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message || `HTTP error! status: ${response.status}`
);
}
const result = await response.json();
if (result.success) {
toast.success("Berhasil update data role");
await roleState.findMany.load(); // refresh list
return true;
} else {
throw new Error(result.message || "Gagal update data role");
}
} catch (error) {
console.error("Error updating data role:", error);
toast.error(
error instanceof Error
? error.message
: "Terjadi kesalahan saat update data role"
);
return false;
} finally {
roleState.update.loading = false;
}
},
reset() {
roleState.update.id = "";
roleState.update.form = { ...defaultRole };
},
},
});
export default userState;
const user = proxy({
userState,
roleState,
})
export default user