QC Admin - User Menu Ekonomi : Jumlah Pengangguran

This commit is contained in:
2025-09-16 10:11:54 +08:00
parent a5d841bb6b
commit 4ceea5203f
97 changed files with 6023 additions and 3481 deletions

View File

@@ -7,9 +7,13 @@ import { z } from "zod";
const templateApbDesa = z.object({
tahun: z.number().min(4, "Tahun minimal 4 karakter"),
pembiayaanIds: z.array(z.string().uuid()).nonempty("Pilih minimal 1 pembiayaan"),
pembiayaanIds: z
.array(z.string().uuid())
.nonempty("Pilih minimal 1 pembiayaan"),
belanjaIds: z.array(z.string().uuid()).nonempty("Pilih minimal 1 belanja"),
pendapatanIds: z.array(z.string().uuid()).nonempty("Pilih minimal 1 pendapatan"),
pendapatanIds: z
.array(z.string().uuid())
.nonempty("Pilih minimal 1 pendapatan"),
});
const ApbDesaDefaultForm = {
@@ -54,32 +58,46 @@ const ApbDesa = proxy({
},
},
findMany: {
data: null as
| Prisma.ApbDesaGetPayload<{
include: {
pendapatan: true;
belanja: true;
pembiayaan: true;
};
}>[]
| null,
data: null as
| Prisma.ApbDesaGetPayload<{
include: {
pendapatan: true;
belanja: true;
pembiayaan: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
loading: false,
async load() {
search: "",
load: async (page = 1, limit = 10, search = "") => {
ApbDesa.findMany.loading = true; // ✅ Akses langsung via nama path
ApbDesa.findMany.page = page;
ApbDesa.findMany.search = search;
try {
this.loading = true;
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.apbdesa[
"find-many"
].get();
if (res.status === 200) {
this.data = res.data?.data ?? [];
].get({ query });
if (res.status === 200 && res.data?.success) {
ApbDesa.findMany.data = res.data.data ?? [];
ApbDesa.findMany.totalPages =
res.data.totalPages ?? 1;
} else {
toast.error(res.data?.message || "Gagal mengambil APB Desa");
ApbDesa.findMany.data = [];
ApbDesa.findMany.totalPages = 1;
}
} catch (error) {
console.error("Find many error:", error);
toast.error("Gagal mengambil APB Desa");
} catch (err) {
console.error("Gagal fetch APB Desa paginated:", err);
ApbDesa.findMany.data = [];
ApbDesa.findMany.totalPages = 1;
} finally {
this.loading = false;
ApbDesa.findMany.loading = false;
}
},
},
@@ -106,13 +124,13 @@ const ApbDesa = proxy({
throw new Error("Gagal mengambil APB Desa");
}
const result = await response.json();
if (!result.success) {
throw new Error(result.message || "Gagal memuat APB Desa");
}
const data = result.data;
this.id = id;
this.form = {
tahun: data.tahun || 0,
@@ -120,7 +138,7 @@ const ApbDesa = proxy({
belanjaIds: data.belanja?.map((b: any) => b.id) || [],
pembiayaanIds: data.pembiayaan?.map((p: any) => p.id) || [],
};
return data;
} catch (error) {
console.error("Error loading APB Desa:", error);
@@ -189,7 +207,7 @@ const ApbDesa = proxy({
data: null as Prisma.ApbDesaGetPayload<{
include: { pendapatan: true; belanja: true; pembiayaan: true };
}> | null,
async load(id: string) {
try {
const response = await fetch(
@@ -199,11 +217,11 @@ const ApbDesa = proxy({
throw new Error("Gagal mengambil detail APB Desa");
}
const result = await response.json();
if (!result.success) {
throw new Error(result.message || "Gagal mengambil data");
}
this.data = result.data; // ✅ fix utama di sini
return result.data;
} catch (error) {
@@ -264,34 +282,32 @@ const pendapatan = proxy({
data: null as any[] | null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => {
// Change to arrow function
pendapatan.findMany.loading = true; // Use the full path to access the property
search: "",
load: async (page = 1, limit = 10, search = "") => {
pendapatan.findMany.loading = true; // ✅ Akses langsung via nama path
pendapatan.findMany.page = page;
pendapatan.findMany.search = search;
try {
const res =
await ApiFetch.api.ekonomi.pendapatanaslidesa.pendapatanasli[
"find-many"
].get({
query: { page, limit },
});
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.pendapatanasli[
"find-many"
].get({ query });
if (res.status === 200 && res.data?.success) {
pendapatan.findMany.data = res.data.data || [];
pendapatan.findMany.total = res.data.total || 0;
pendapatan.findMany.totalPages = res.data.totalPages || 1;
pendapatan.findMany.data = res.data.data ?? [];
pendapatan.findMany.totalPages =
res.data.totalPages ?? 1;
} else {
console.error("Failed to load pendapatan:", res.data?.message);
pendapatan.findMany.data = [];
pendapatan.findMany.total = 0;
pendapatan.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading pendapatan:", error);
} catch (err) {
console.error("Gagal fetch pendapatan asli desa paginated:", err);
pendapatan.findMany.data = [];
pendapatan.findMany.total = 0;
pendapatan.findMany.totalPages = 1;
} finally {
pendapatan.findMany.loading = false;
@@ -308,12 +324,15 @@ const pendapatan = proxy({
return null;
}
try {
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/pendapatanasli/${id}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const response = await fetch(
`/api/ekonomi/pendapatanaslidesa/pendapatanasli/${id}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
@@ -349,16 +368,19 @@ const pendapatan = proxy({
try {
pendapatan.update.loading = true;
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/pendapatanasli/${this.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
value: this.form.value,
}),
});
const response = await fetch(
`/api/ekonomi/pendapatanaslidesa/pendapatanasli/${this.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
value: this.form.value,
}),
}
);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
@@ -495,23 +517,37 @@ const belanja = proxy({
name: string;
value: number;
}>,
page: 1,
totalPages: 1,
loading: false,
async load() {
search: "",
load: async (page = 1, limit = 10, search = "") => {
belanja.findMany.loading = true; // ✅ Akses langsung via nama path
belanja.findMany.page = page;
belanja.findMany.search = search;
try {
this.loading = true;
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.belanja[
"find-many"
].get();
if (res.status === 200) {
this.data = res.data?.data ?? [];
].get({ query });
if (res.status === 200 && res.data?.success) {
belanja.findMany.data = res.data.data ?? [];
belanja.findMany.totalPages =
res.data.totalPages ?? 1;
} else {
toast.error(res.data?.message || "Gagal mengambil Belanja");
belanja.findMany.data = [];
belanja.findMany.totalPages = 1;
}
} catch (error) {
console.error("Find many error:", error);
toast.error("Gagal mengambil Belanja");
} catch (err) {
console.error("Gagal fetch Belanja paginated:", err);
belanja.findMany.data = [];
belanja.findMany.totalPages = 1;
} finally {
this.loading = false;
belanja.findMany.loading = false;
}
},
},
@@ -525,12 +561,15 @@ const belanja = proxy({
return null;
}
try {
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/belanja/${id}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const response = await fetch(
`/api/ekonomi/pendapatanaslidesa/belanja/${id}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
@@ -566,16 +605,19 @@ const belanja = proxy({
try {
belanja.update.loading = true;
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/belanja/${this.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
value: this.form.value,
}),
});
const response = await fetch(
`/api/ekonomi/pendapatanaslidesa/belanja/${this.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
value: this.form.value,
}),
}
);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
@@ -710,23 +752,37 @@ const pembiayaan = proxy({
name: string;
value: number;
}>,
page: 1,
totalPages: 1,
loading: false,
async load() {
search: "",
load: async (page = 1, limit = 10, search = "") => {
pembiayaan.findMany.loading = true; // ✅ Akses langsung via nama path
pembiayaan.findMany.page = page;
pembiayaan.findMany.search = search;
try {
this.loading = true;
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.pembiayaan[
"find-many"
].get();
if (res.status === 200) {
this.data = res.data?.data ?? [];
].get({ query });
if (res.status === 200 && res.data?.success) {
pembiayaan.findMany.data = res.data.data ?? [];
pembiayaan.findMany.totalPages =
res.data.totalPages ?? 1;
} else {
toast.error(res.data?.message || "Gagal mengambil Pembiayaan");
pembiayaan.findMany.data = [];
pembiayaan.findMany.totalPages = 1;
}
} catch (error) {
console.error("Find many error:", error);
toast.error("Gagal mengambil Pembiayaan");
} catch (err) {
console.error("Gagal fetch Pembiayaan paginated:", err);
pembiayaan.findMany.data = [];
pembiayaan.findMany.totalPages = 1;
} finally {
this.loading = false;
pembiayaan.findMany.loading = false;
}
},
},
@@ -740,12 +796,15 @@ const pembiayaan = proxy({
return null;
}
try {
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/pembiayaan/${id}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const response = await fetch(
`/api/ekonomi/pendapatanaslidesa/pembiayaan/${id}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
@@ -781,16 +840,19 @@ const pembiayaan = proxy({
try {
pembiayaan.update.loading = true;
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/pembiayaan/${this.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
value: this.form.value,
}),
});
const response = await fetch(
`/api/ekonomi/pendapatanaslidesa/pembiayaan/${this.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
value: this.form.value,
}),
}
);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(

View File

@@ -15,7 +15,8 @@ const templateJumlahPengngguran = z.object({
uneducatedUnemployment: z
.number()
.min(1, "Pengangguran tidak pendidikan harus diisi"),
percentageChange: z.number().min(0, "Persentase perubahan harus diisi"),
percentageChange: z.number({ invalid_type_error: "Persentase perubahan harus angka" }),
});
type JumlahPengangguran = {
@@ -29,7 +30,7 @@ type JumlahPengangguran = {
const jumlahPengangguranForm: JumlahPengangguran = {
month: "",
year: 0,
year: new Date().getFullYear(), // Default to current year
totalUnemployment: 0,
educatedUnemployment: 0,
uneducatedUnemployment: 0,
@@ -60,13 +61,21 @@ const jumlahPengangguran = proxy({
form: jumlahPengangguranForm,
loading: false,
async create() {
const cek = templateJumlahPengngguran.safeParse(
jumlahPengangguran.create.form
);
// Ensure all number fields are actual numbers
const formData = {
...jumlahPengangguran.create.form,
year: Number(jumlahPengangguran.create.form.year) || new Date().getFullYear(),
totalUnemployment: Number(jumlahPengangguran.create.form.totalUnemployment) || 0,
educatedUnemployment: Number(jumlahPengangguran.create.form.educatedUnemployment) || 0,
uneducatedUnemployment: Number(jumlahPengangguran.create.form.uneducatedUnemployment) || 0,
percentageChange: Number(jumlahPengangguran.create.form.percentageChange) || 0,
};
const cek = templateJumlahPengngguran.safeParse(formData);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
.map((v) => `${v.path.join(".")} (${v.message})`)
.join("\n")}]`;
toast.error(err);
return null;
}
@@ -78,7 +87,7 @@ const jumlahPengangguran = proxy({
].post(jumlahPengangguran.create.form);
if (res.status === 200) {
const id = res.data?.data?.id;
const id = res.data?.id;
if (id) {
toast.success("Success create");
jumlahPengangguran.create.form = { ...jumlahPengangguranForm };
@@ -103,16 +112,40 @@ const jumlahPengangguran = proxy({
omit: { isActive: true };
}>[]
| null,
async load() {
const res =
await ApiFetch.api.ekonomi.jumlahpengangguran.detaildatapengangguran[
"find-many"
].get();
if (res.status === 200) {
jumlahPengangguran.findMany.data = res.data?.data ?? [];
}
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
jumlahPengangguran.findMany.loading = true; // ✅ Akses langsung via nama path
jumlahPengangguran.findMany.page = page;
jumlahPengangguran.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ekonomi.jumlahpengangguran.detaildatapengangguran[
"find-many"
].get({ query });
if (res.status === 200 && res.data?.success) {
jumlahPengangguran.findMany.data = res.data.data ?? [];
jumlahPengangguran.findMany.totalPages =
res.data.totalPages ?? 1;
} else {
jumlahPengangguran.findMany.data = [];
jumlahPengangguran.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch jumlah pengangguran paginated:", err);
jumlahPengangguran.findMany.data = [];
jumlahPengangguran.findMany.totalPages = 1;
} finally {
jumlahPengangguran.findMany.loading = false;
}
},
},
},
findUnique: {
data: null as Prisma.DetailDataPengangguranGetPayload<{

View File

@@ -161,18 +161,34 @@ const posisiOrganisasi = proxy({
deskripsi: string | null;
hierarki: number;
}>,
async load() {
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
posisiOrganisasi.findMany.loading = true; // ✅ Akses langsung via nama path
posisiOrganisasi.findMany.page = page;
posisiOrganisasi.findMany.search = search;
try {
const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
"posisi-organisasi"
]["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 ?? [];
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
posisiOrganisasi.findMany.data = res.data.data ?? [];
posisiOrganisasi.findMany.totalPages = res.data.totalPages ?? 1;
} else {
posisiOrganisasi.findMany.data = [];
posisiOrganisasi.findMany.totalPages = 1;
}
} catch (error) {
console.error("Find many error:", error);
this.data = [];
} catch (err) {
console.error("Gagal fetch posisi organisasi paginated:", err);
posisiOrganisasi.findMany.data = [];
posisiOrganisasi.findMany.totalPages = 1;
} finally {
posisiOrganisasi.findMany.loading = false;
}
},
},

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -97,13 +98,37 @@ const laporanPublikState = proxy({
include: { penanganan: true };
}>[]
| null,
async load() {
const res = await ApiFetch.api.keamanan.laporanpublik["find-many"].get();
if (res.status === 200) {
laporanPublikState.findMany.data = res.data?.data ?? [];
}
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
laporanPublikState.findMany.loading = true; // ✅ Akses langsung via nama path
laporanPublikState.findMany.page = page;
laporanPublikState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.keamanan.laporanpublik["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
laporanPublikState.findMany.data = res.data.data ?? [];
laporanPublikState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
laporanPublikState.findMany.data = [];
laporanPublikState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch laporan publik paginated:", err);
laporanPublikState.findMany.data = [];
laporanPublikState.findMany.totalPages = 1;
} finally {
laporanPublikState.findMany.loading = false;
}
},
},
},
findUnique: {
data: null as Prisma.LaporanPublikGetPayload<{
include: { penanganan: true };