Compare commits
9 Commits
tasks/kepe
...
deploy/stg
| Author | SHA1 | Date | |
|---|---|---|---|
| 09c7fd8f3a | |||
| 656ffcc561 | |||
| 76ffa662c5 | |||
| 46423409fd | |||
| 2edf5e9b11 | |||
| af368eeee0 | |||
| e104cd8fcc | |||
| 50801e5c8a | |||
| 80186bf493 |
@@ -59,9 +59,16 @@ COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/next.config.* ./
|
||||
COPY --chmod=755 docker-entrypoint.sh ./docker-entrypoint.sh
|
||||
|
||||
# Create uploads directory with proper permissions
|
||||
RUN mkdir -p /app/uploads && chown nextjs:nodejs /app/uploads
|
||||
|
||||
USER nextjs
|
||||
|
||||
# Persistent storage for uploaded files
|
||||
VOLUME ["/app/uploads"]
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["bun", "start"]
|
||||
CMD ["/app/docker-entrypoint.sh"]
|
||||
5
QWEN.md
5
QWEN.md
@@ -229,4 +229,7 @@ Common issues and solutions:
|
||||
3. Test database changes with `bunx prisma db push`
|
||||
4. Use the integrated Swagger docs at `/api/docs` for API testing
|
||||
5. Check environment variables are properly configured
|
||||
6. Verify responsive design on different screen sizes
|
||||
6. Verify responsive design on different screen sizes
|
||||
|
||||
## Qwen Added Memories
|
||||
- **GitHub Workflows**: Project ini memiliki workflow GitHub Action untuk deployment. User akan menangani workflow secara manual di GitHub.
|
||||
|
||||
13
docker-entrypoint.sh
Normal file
13
docker-entrypoint.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🔄 Running database migrations..."
|
||||
cd /app
|
||||
bunx prisma migrate deploy || {
|
||||
echo "❌ Migration failed!"
|
||||
exit 1
|
||||
}
|
||||
echo "✅ Migrations completed successfully"
|
||||
|
||||
echo "🚀 Starting application..."
|
||||
exec bun start
|
||||
@@ -11,6 +11,11 @@ const compat = new FlatCompat({
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
{
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "desa-darmasaba",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { loadJsonData } from "../../load-json";
|
||||
|
||||
const posisiOrganisasiBumDes = loadJsonData("ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json");
|
||||
const pegawai = loadJsonData("ekonomi/struktur-organisasi/pegawai-bumdes.json");
|
||||
interface PosisiOrganisasi {
|
||||
id: string;
|
||||
nama: string;
|
||||
deskripsi: string;
|
||||
hierarki: number;
|
||||
parentId: string | null;
|
||||
}
|
||||
|
||||
interface PegawaiBumDes {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string;
|
||||
tanggalMasuk: string;
|
||||
email: string;
|
||||
telepon: string;
|
||||
alamat: string;
|
||||
posisiId: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
const posisiOrganisasiBumDes = loadJsonData<PosisiOrganisasi[][]>("ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json");
|
||||
const pegawai = loadJsonData<PegawaiBumDes[]>("ekonomi/struktur-organisasi/pegawai-bumdes.json");
|
||||
|
||||
export async function seedStrukturBumdes() {
|
||||
const flattenedPosisi = posisiOrganisasiBumDes.flat();
|
||||
|
||||
@@ -1,8 +1,29 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { loadJsonData } from "../../../load-json";
|
||||
|
||||
const pegawaiPpid = loadJsonData("ppid/struktur-ppid/pegawai-PPID.json");
|
||||
const posisiOrganisasiPPID = loadJsonData("ppid/struktur-ppid/posisi-organisasi-PPID.json");
|
||||
interface PegawaiPPID {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string;
|
||||
tanggalMasuk: string;
|
||||
email: string;
|
||||
telepon: string;
|
||||
alamat: string;
|
||||
imageName?: string;
|
||||
posisiId: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
interface PosisiOrganisasiPPID {
|
||||
id: string;
|
||||
nama: string;
|
||||
deskripsi: string;
|
||||
hierarki: number;
|
||||
parentId: string | null;
|
||||
}
|
||||
|
||||
const pegawaiPpid = loadJsonData<PegawaiPPID[]>("ppid/struktur-ppid/pegawai-PPID.json");
|
||||
const posisiOrganisasiPPID = loadJsonData<PosisiOrganisasiPPID[][]>("ppid/struktur-ppid/posisi-organisasi-PPID.json");
|
||||
|
||||
export async function seedPegawaiPpid() {
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "DataBanjar" (
|
||||
"id" TEXT NOT NULL,
|
||||
"nama" TEXT NOT NULL,
|
||||
"penduduk" INTEGER NOT NULL,
|
||||
"kk" INTEGER NOT NULL,
|
||||
"miskin" INTEGER NOT NULL,
|
||||
"tahun" INTEGER NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "DataBanjar_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DistribusiAgama" (
|
||||
"id" TEXT NOT NULL,
|
||||
"agama" TEXT NOT NULL,
|
||||
"jumlah" INTEGER NOT NULL,
|
||||
"tahun" INTEGER NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "DistribusiAgama_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DistribusiUmur" (
|
||||
"id" TEXT NOT NULL,
|
||||
"rentangUmur" TEXT NOT NULL,
|
||||
"jumlah" INTEGER NOT NULL,
|
||||
"tahun" INTEGER NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "DistribusiUmur_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "MigrasiPenduduk" (
|
||||
"id" TEXT NOT NULL,
|
||||
"nama" TEXT NOT NULL,
|
||||
"jenis" "JenisMigrasi" NOT NULL,
|
||||
"tanggal" TIMESTAMP(3) NOT NULL,
|
||||
"asal" TEXT,
|
||||
"tujuan" TEXT,
|
||||
"alasan" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "MigrasiPenduduk_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DinamikaPenduduk" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tahun" INTEGER NOT NULL,
|
||||
"kelahiran" INTEGER NOT NULL,
|
||||
"kematian" INTEGER NOT NULL,
|
||||
"masuk" INTEGER NOT NULL,
|
||||
"keluar" INTEGER NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "DinamikaPenduduk_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DataBanjar_tahun_idx" ON "DataBanjar"("tahun");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DataBanjar_isActive_idx" ON "DataBanjar"("isActive");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DistribusiAgama_tahun_idx" ON "DistribusiAgama"("tahun");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DistribusiAgama_isActive_idx" ON "DistribusiAgama"("isActive");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DistribusiUmur_tahun_idx" ON "DistribusiUmur"("tahun");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DistribusiUmur_isActive_idx" ON "DistribusiUmur"("isActive");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "MigrasiPenduduk_tanggal_idx" ON "MigrasiPenduduk"("tanggal");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "MigrasiPenduduk_isActive_idx" ON "MigrasiPenduduk"("isActive");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DinamikaPenduduk_tahun_idx" ON "DinamikaPenduduk"("tahun");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DinamikaPenduduk_isActive_idx" ON "DinamikaPenduduk"("isActive");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "DinamikaPenduduk_tahun_key" ON "DinamikaPenduduk"("tahun");
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "JenisMigrasi" AS ENUM ('MASUK', 'KELUAR');
|
||||
@@ -1,16 +1,35 @@
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { proxy } from "valtio";
|
||||
|
||||
interface DashboardSummary {
|
||||
tahun: number;
|
||||
summary: {
|
||||
totalPenduduk: number;
|
||||
totalKK: number;
|
||||
totalKelahiran: number;
|
||||
totalKemiskinan: number;
|
||||
};
|
||||
dinamika: {
|
||||
kelahiran: number;
|
||||
kematian: number;
|
||||
pindahMasuk: number;
|
||||
pindahKeluar: number;
|
||||
};
|
||||
agama: unknown[];
|
||||
umur: unknown[];
|
||||
banjar: unknown[];
|
||||
}
|
||||
|
||||
const kependudukanDashboard = proxy({
|
||||
summary: {
|
||||
data: null as any,
|
||||
data: null as DashboardSummary | null,
|
||||
loading: false,
|
||||
async load() {
|
||||
kependudukanDashboard.summary.loading = true;
|
||||
try {
|
||||
const res = await ApiFetch.api.kependudukan.dashboard.summary.get();
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kependudukanDashboard.summary.data = res.data.data;
|
||||
kependudukanDashboard.summary.data = res.data.data as unknown as DashboardSummary;
|
||||
} else {
|
||||
kependudukanDashboard.summary.data = null;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ const dataBanjar = proxy({
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
data: null as unknown[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
@@ -66,7 +66,7 @@ const dataBanjar = proxy({
|
||||
dataBanjar.findMany.tahun = tahun;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
const query: Record<string, string | number> = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (tahun) query.tahun = tahun;
|
||||
|
||||
@@ -90,7 +90,7 @@ const dataBanjar = proxy({
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as any | null,
|
||||
data: null as unknown | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kependudukan/databanjar/${id}`);
|
||||
|
||||
@@ -49,7 +49,7 @@ const distribusiAgama = proxy({
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
data: null as unknown[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
@@ -60,7 +60,7 @@ const distribusiAgama = proxy({
|
||||
distribusiAgama.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
const query: Record<string, string | number> = { page, limit };
|
||||
if (tahun) query.tahun = tahun;
|
||||
if (search) query.search = search;
|
||||
|
||||
@@ -84,7 +84,7 @@ const distribusiAgama = proxy({
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as any | null,
|
||||
data: null as unknown | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kependudukan/distribusiagama/${id}`);
|
||||
|
||||
@@ -49,7 +49,7 @@ const distribusiUmur = proxy({
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
data: null as unknown[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
@@ -60,7 +60,7 @@ const distribusiUmur = proxy({
|
||||
distribusiUmur.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
const query: Record<string, string | number> = { page, limit };
|
||||
if (tahun) query.tahun = tahun;
|
||||
if (search) query.search = search;
|
||||
|
||||
@@ -84,7 +84,7 @@ const distribusiUmur = proxy({
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as any | null,
|
||||
data: null as unknown | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kependudukan/distribusiumur/${id}`);
|
||||
|
||||
@@ -9,7 +9,6 @@ const templateMigrasiPenduduk = z.object({
|
||||
tanggal: z.string().min(1, "Tanggal harus diisi"),
|
||||
asalTujuan: z.string().min(1, "Asal/Tujuan harus diisi"),
|
||||
alasan: z.string().optional(),
|
||||
jenisKelamin: z.string().optional(),
|
||||
});
|
||||
|
||||
const migrasiPenduduk = proxy({
|
||||
@@ -20,7 +19,6 @@ const migrasiPenduduk = proxy({
|
||||
tanggal: "",
|
||||
asalTujuan: "",
|
||||
alasan: "",
|
||||
jenisKelamin: "",
|
||||
},
|
||||
loading: false,
|
||||
async create() {
|
||||
@@ -38,7 +36,7 @@ const migrasiPenduduk = proxy({
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Sukses menambahkan data migrasi penduduk");
|
||||
migrasiPenduduk.create.form = { jenis: "", nama: "", tanggal: "", asalTujuan: "", alasan: "", jenisKelamin: "" };
|
||||
migrasiPenduduk.create.form = { jenis: "", nama: "", tanggal: "", asalTujuan: "", alasan: "" };
|
||||
migrasiPenduduk.findMany.load();
|
||||
return id;
|
||||
}
|
||||
@@ -55,7 +53,7 @@ const migrasiPenduduk = proxy({
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
data: null as unknown[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
@@ -66,7 +64,7 @@ const migrasiPenduduk = proxy({
|
||||
migrasiPenduduk.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
const query: Record<string, string | number> = { page, limit };
|
||||
if (tahun) query.tahun = tahun;
|
||||
if (search) query.search = search;
|
||||
|
||||
@@ -90,7 +88,7 @@ const migrasiPenduduk = proxy({
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as any | null,
|
||||
data: null as unknown | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kependudukan/migrasipenduduk/${id}`);
|
||||
@@ -116,7 +114,6 @@ const migrasiPenduduk = proxy({
|
||||
tanggal: "",
|
||||
asalTujuan: "",
|
||||
alasan: "",
|
||||
jenisKelamin: "",
|
||||
},
|
||||
loading: false,
|
||||
async submit() {
|
||||
@@ -132,7 +129,6 @@ const migrasiPenduduk = proxy({
|
||||
tanggal: this.form.tanggal,
|
||||
asalTujuan: this.form.asalTujuan,
|
||||
alasan: this.form.alasan,
|
||||
jenisKelamin: this.form.jenisKelamin,
|
||||
};
|
||||
|
||||
const cek = templateMigrasiPenduduk.safeParse(formData);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Select, Group } from '@mantine/core';
|
||||
import { Select } from '@mantine/core';
|
||||
import { IconCalendar } from '@tabler/icons-react';
|
||||
|
||||
interface YearFilterProps {
|
||||
|
||||
@@ -20,7 +20,7 @@ import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import dataBanjar from '../../../_state/kependudukan/data-banjar';
|
||||
|
||||
interface FormData {
|
||||
interface DataBanjarForm {
|
||||
nama: string;
|
||||
penduduk: number;
|
||||
kk: number;
|
||||
@@ -33,14 +33,14 @@ export default function EditDataBanjar() {
|
||||
const { id } = useParams() as { id: string };
|
||||
const stateDataBanjar = useProxy(dataBanjar);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
const [formData, setFormData] = useState<DataBanjarForm>({
|
||||
nama: '',
|
||||
penduduk: 0,
|
||||
kk: 0,
|
||||
miskin: 0,
|
||||
tahun: new Date().getFullYear(),
|
||||
});
|
||||
const [originalData, setOriginalData] = useState<FormData>({
|
||||
const [originalData, setOriginalData] = useState<DataBanjarForm>({
|
||||
nama: '',
|
||||
penduduk: 0,
|
||||
kk: 0,
|
||||
@@ -72,7 +72,7 @@ export default function EditDataBanjar() {
|
||||
stateDataBanjar.update.id = id;
|
||||
await stateDataBanjar.findUnique.load(id);
|
||||
|
||||
const data = stateDataBanjar.findUnique.data;
|
||||
const data = stateDataBanjar.findUnique.data as DataBanjarForm | null;
|
||||
if (data) {
|
||||
setFormData({
|
||||
nama: data.nama ?? '',
|
||||
@@ -101,14 +101,22 @@ export default function EditDataBanjar() {
|
||||
}, [id]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(field: keyof FormData) =>
|
||||
(value: any) => {
|
||||
(field: keyof DataBanjarForm) =>
|
||||
(value: string | number | undefined) => {
|
||||
const val =
|
||||
field === 'penduduk' || field === 'kk' || field === 'miskin' || field === 'tahun'
|
||||
? Number(value || 0)
|
||||
: value;
|
||||
|
||||
setFormData((prev) => ({ ...prev, [field]: val }));
|
||||
setFormData((prev) => ({ ...prev, [field]: val as never }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleChangeText = useCallback(
|
||||
(field: keyof DataBanjarForm) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: e.currentTarget.value as never }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
@@ -173,7 +181,7 @@ export default function EditDataBanjar() {
|
||||
label="Nama Banjar"
|
||||
placeholder="Masukkan nama banjar"
|
||||
value={formData.nama}
|
||||
onChange={handleChange('nama')}
|
||||
onChange={handleChangeText('nama')}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client';
|
||||
|
||||
import colors from '@/con/colors';
|
||||
@@ -27,10 +25,6 @@ function CreateDataBanjar() {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
const yearOptions = Array.from({ length: 10 }, (_, i) => ({
|
||||
value: String(currentYear - i),
|
||||
label: String(currentYear - i),
|
||||
}));
|
||||
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
@@ -89,7 +88,7 @@ function ListDataBanjar({ search, year }: { search: string; year?: number }) {
|
||||
load(page, 10, debouncedSearch, year);
|
||||
}, [page, debouncedSearch, year]);
|
||||
|
||||
const filteredData = data || [];
|
||||
const filteredData = (data as DataBanjarType[]) || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
@@ -140,7 +139,7 @@ function ListDataBanjar({ search, year }: { search: string; year?: number }) {
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item: DataBanjarType) => (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>{item.penduduk.toLocaleString('id-ID')}</TableTd>
|
||||
@@ -198,7 +197,7 @@ function ListDataBanjar({ search, year }: { search: string; year?: number }) {
|
||||
<Box hiddenFrom="md">
|
||||
<Stack gap="xs">
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item: DataBanjarType) => (
|
||||
filteredData.map((item) => (
|
||||
<Paper key={item.id} withBorder p="sm" radius="sm">
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
Title,
|
||||
NumberInput,
|
||||
Select
|
||||
@@ -21,7 +20,7 @@ import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import distribusiAgama from '../../../_state/kependudukan/distribusi-agama';
|
||||
|
||||
interface FormData {
|
||||
interface DistribusiAgamaForm {
|
||||
agama: string;
|
||||
jumlah: number;
|
||||
tahun: number;
|
||||
@@ -32,12 +31,12 @@ export default function EditDistribusiAgama() {
|
||||
const { id } = useParams() as { id: string };
|
||||
const stateDistribusiAgama = useProxy(distribusiAgama);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
const [formData, setFormData] = useState<DistribusiAgamaForm>({
|
||||
agama: '',
|
||||
jumlah: 0,
|
||||
tahun: new Date().getFullYear(),
|
||||
});
|
||||
const [originalData, setOriginalData] = useState<FormData>({
|
||||
const [originalData, setOriginalData] = useState<DistribusiAgamaForm>({
|
||||
agama: '',
|
||||
jumlah: 0,
|
||||
tahun: new Date().getFullYear(),
|
||||
@@ -78,7 +77,7 @@ export default function EditDistribusiAgama() {
|
||||
stateDistribusiAgama.update.id = id;
|
||||
await stateDistribusiAgama.findUnique.load(id);
|
||||
|
||||
const data = stateDistribusiAgama.findUnique.data;
|
||||
const data = stateDistribusiAgama.findUnique.data as DistribusiAgamaForm | null;
|
||||
if (data) {
|
||||
setFormData({
|
||||
agama: data.agama ?? '',
|
||||
@@ -102,15 +101,18 @@ export default function EditDistribusiAgama() {
|
||||
loadData();
|
||||
}, [id]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(field: keyof FormData) =>
|
||||
(value: any) => {
|
||||
const val =
|
||||
field === 'jumlah' || field === 'tahun'
|
||||
? Number(value || 0)
|
||||
: value;
|
||||
const handleChangeNumber = useCallback(
|
||||
(field: keyof DistribusiAgamaForm) =>
|
||||
(value: string | number) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: (typeof value === 'string' ? Number(value) || 0 : (value ?? 0)) as never }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
setFormData((prev) => ({ ...prev, [field]: val }));
|
||||
const handleChangeSelect = useCallback(
|
||||
(field: keyof DistribusiAgamaForm) =>
|
||||
(value: string | null) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: (value || '') as never }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
@@ -174,7 +176,7 @@ export default function EditDistribusiAgama() {
|
||||
placeholder="Pilih agama"
|
||||
data={agamaOptions}
|
||||
value={formData.agama}
|
||||
onChange={handleChange('agama')}
|
||||
onChange={handleChangeSelect('agama')}
|
||||
required
|
||||
searchable
|
||||
/>
|
||||
@@ -183,7 +185,7 @@ export default function EditDistribusiAgama() {
|
||||
label="Jumlah"
|
||||
placeholder="Masukkan jumlah"
|
||||
value={formData.jumlah}
|
||||
onChange={handleChange('jumlah')}
|
||||
onChange={handleChangeNumber('jumlah')}
|
||||
min={0}
|
||||
required
|
||||
/>
|
||||
@@ -193,7 +195,7 @@ export default function EditDistribusiAgama() {
|
||||
placeholder="Pilih tahun"
|
||||
data={yearOptions}
|
||||
value={String(formData.tahun)}
|
||||
onChange={handleChange('tahun')}
|
||||
onChange={handleChangeSelect('tahun')}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client';
|
||||
|
||||
import colors from '@/con/colors';
|
||||
@@ -10,7 +8,6 @@ import {
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
Title,
|
||||
NumberInput,
|
||||
Select
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Center,
|
||||
Flex,
|
||||
Group,
|
||||
Pagination,
|
||||
Paper,
|
||||
@@ -23,7 +21,7 @@ import {
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { YearFilter } from '../_components/YearFilter';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
@@ -87,7 +85,7 @@ function ListDistribusiAgama({ search, year }: { search: string; year?: number }
|
||||
load(page, 10, debouncedSearch, year);
|
||||
}, [page, debouncedSearch, year]);
|
||||
|
||||
const filteredData = data || [];
|
||||
const filteredData = (data as DistribusiAgamaType[]) || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
@@ -136,7 +134,7 @@ function ListDistribusiAgama({ search, year }: { search: string; year?: number }
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item: DistribusiAgamaType) => (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.agama}</TableTd>
|
||||
<TableTd>{item.jumlah.toLocaleString('id-ID')}</TableTd>
|
||||
@@ -192,7 +190,7 @@ function ListDistribusiAgama({ search, year }: { search: string; year?: number }
|
||||
<Box hiddenFrom="md">
|
||||
<Stack gap="xs">
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item: DistribusiAgamaType) => (
|
||||
filteredData.map((item) => (
|
||||
<Paper key={item.id} withBorder p="sm" radius="sm">
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
|
||||
@@ -20,7 +20,7 @@ import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import distribusiUmur from '../../../_state/kependudukan/distribusi-umur';
|
||||
|
||||
interface FormData {
|
||||
interface DistribusiUmurForm {
|
||||
rentangUmur: string;
|
||||
jumlah: number;
|
||||
tahun: number;
|
||||
@@ -31,12 +31,12 @@ export default function EditDistribusiUmur() {
|
||||
const { id } = useParams() as { id: string };
|
||||
const stateDistribusiUmur = useProxy(distribusiUmur);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
const [formData, setFormData] = useState<DistribusiUmurForm>({
|
||||
rentangUmur: '',
|
||||
jumlah: 0,
|
||||
tahun: new Date().getFullYear(),
|
||||
});
|
||||
const [originalData, setOriginalData] = useState<FormData>({
|
||||
const [originalData, setOriginalData] = useState<DistribusiUmurForm>({
|
||||
rentangUmur: '',
|
||||
jumlah: 0,
|
||||
tahun: new Date().getFullYear(),
|
||||
@@ -78,7 +78,7 @@ export default function EditDistribusiUmur() {
|
||||
stateDistribusiUmur.update.id = id;
|
||||
await stateDistribusiUmur.findUnique.load(id);
|
||||
|
||||
const data = stateDistribusiUmur.findUnique.data;
|
||||
const data = stateDistribusiUmur.findUnique.data as DistribusiUmurForm | null;
|
||||
if (data) {
|
||||
setFormData({
|
||||
rentangUmur: data.rentangUmur ?? '',
|
||||
@@ -102,15 +102,18 @@ export default function EditDistribusiUmur() {
|
||||
loadData();
|
||||
}, [id]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(field: keyof FormData) =>
|
||||
(value: any) => {
|
||||
const val =
|
||||
field === 'jumlah' || field === 'tahun'
|
||||
? Number(value || 0)
|
||||
: value;
|
||||
const handleChangeNumber = useCallback(
|
||||
(field: keyof DistribusiUmurForm) =>
|
||||
(value: string | number) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: (typeof value === 'string' ? Number(value) || 0 : (value ?? 0)) as never }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
setFormData((prev) => ({ ...prev, [field]: val }));
|
||||
const handleChangeSelect = useCallback(
|
||||
(field: keyof DistribusiUmurForm) =>
|
||||
(value: string | null) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: (value || '') as never }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
@@ -174,7 +177,7 @@ export default function EditDistribusiUmur() {
|
||||
placeholder="Pilih rentang umur"
|
||||
data={rentangUmurOptions}
|
||||
value={formData.rentangUmur}
|
||||
onChange={handleChange('rentangUmur')}
|
||||
onChange={handleChangeSelect('rentangUmur')}
|
||||
required
|
||||
searchable
|
||||
/>
|
||||
@@ -183,7 +186,7 @@ export default function EditDistribusiUmur() {
|
||||
label="Jumlah"
|
||||
placeholder="Masukkan jumlah"
|
||||
value={formData.jumlah}
|
||||
onChange={handleChange('jumlah')}
|
||||
onChange={handleChangeNumber('jumlah')}
|
||||
min={0}
|
||||
required
|
||||
/>
|
||||
@@ -193,7 +196,7 @@ export default function EditDistribusiUmur() {
|
||||
placeholder="Pilih tahun"
|
||||
data={yearOptions}
|
||||
value={String(formData.tahun)}
|
||||
onChange={handleChange('tahun')}
|
||||
onChange={handleChangeSelect('tahun')}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client';
|
||||
|
||||
import colors from '@/con/colors';
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Center,
|
||||
Flex,
|
||||
Group,
|
||||
Pagination,
|
||||
Paper,
|
||||
@@ -88,7 +86,7 @@ function ListDistribusiUmur({ search, year }: { search: string; year?: number })
|
||||
load(page, 10, debouncedSearch, year);
|
||||
}, [page, debouncedSearch, year]);
|
||||
|
||||
const filteredData = data || [];
|
||||
const filteredData = (data as DistribusiUmurType[]) || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
@@ -137,7 +135,7 @@ function ListDistribusiUmur({ search, year }: { search: string; year?: number })
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item: DistribusiUmurType) => (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.rentangUmur}</TableTd>
|
||||
<TableTd>{item.jumlah.toLocaleString('id-ID')}</TableTd>
|
||||
@@ -193,7 +191,7 @@ function ListDistribusiUmur({ search, year }: { search: string; year?: number })
|
||||
<Box hiddenFrom="md">
|
||||
<Stack gap="xs">
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item: DistribusiUmurType) => (
|
||||
filteredData.map((item) => (
|
||||
<Paper key={item.id} withBorder p="sm" radius="sm">
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
|
||||
@@ -22,13 +22,12 @@ import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import migrasiPenduduk from '../../../_state/kependudukan/migrasi-penduduk';
|
||||
|
||||
interface FormData {
|
||||
interface MigrasiPendudukForm {
|
||||
jenis: string;
|
||||
nama: string;
|
||||
tanggal: string;
|
||||
asalTujuan: string;
|
||||
alasan: string;
|
||||
jenisKelamin: string;
|
||||
}
|
||||
|
||||
export default function EditMigrasiPenduduk() {
|
||||
@@ -36,21 +35,19 @@ export default function EditMigrasiPenduduk() {
|
||||
const { id } = useParams() as { id: string };
|
||||
const stateMigrasiPenduduk = useProxy(migrasiPenduduk);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
const [formData, setFormData] = useState<MigrasiPendudukForm>({
|
||||
jenis: '',
|
||||
nama: '',
|
||||
tanggal: '',
|
||||
asalTujuan: '',
|
||||
alasan: '',
|
||||
jenisKelamin: '',
|
||||
});
|
||||
const [originalData, setOriginalData] = useState<FormData>({
|
||||
const [originalData, setOriginalData] = useState<MigrasiPendudukForm>({
|
||||
jenis: '',
|
||||
nama: '',
|
||||
tanggal: '',
|
||||
asalTujuan: '',
|
||||
alasan: '',
|
||||
jenisKelamin: '',
|
||||
});
|
||||
|
||||
const jenisOptions = [
|
||||
@@ -58,11 +55,6 @@ export default function EditMigrasiPenduduk() {
|
||||
{ value: 'KELUAR', label: 'Keluar' },
|
||||
];
|
||||
|
||||
const jenisKelaminOptions = [
|
||||
{ value: 'L', label: 'Laki-laki' },
|
||||
{ value: 'P', label: 'Perempuan' },
|
||||
];
|
||||
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.jenis?.trim() !== '' &&
|
||||
@@ -81,23 +73,23 @@ export default function EditMigrasiPenduduk() {
|
||||
stateMigrasiPenduduk.update.id = id;
|
||||
await stateMigrasiPenduduk.findUnique.load(id);
|
||||
|
||||
const data = stateMigrasiPenduduk.findUnique.data;
|
||||
const data = stateMigrasiPenduduk.findUnique.data as any;
|
||||
if (data) {
|
||||
const asalTujuan = data.jenis === 'MASUK' ? (data.asal || '') : (data.tujuan || '');
|
||||
|
||||
setFormData({
|
||||
jenis: data.jenis ?? '',
|
||||
nama: data.nama ?? '',
|
||||
tanggal: data.tanggal ?? '',
|
||||
asalTujuan: data.asalTujuan ?? '',
|
||||
tanggal: data.tanggal ? new Date(data.tanggal).toISOString().split('T')[0] : '',
|
||||
asalTujuan: asalTujuan,
|
||||
alasan: data.alasan ?? '',
|
||||
jenisKelamin: data.jenisKelamin ?? '',
|
||||
});
|
||||
setOriginalData({
|
||||
jenis: data.jenis ?? '',
|
||||
nama: data.nama ?? '',
|
||||
tanggal: data.tanggal ?? '',
|
||||
asalTujuan: data.asalTujuan ?? '',
|
||||
tanggal: data.tanggal ? new Date(data.tanggal).toISOString().split('T')[0] : '',
|
||||
asalTujuan: asalTujuan,
|
||||
alasan: data.alasan ?? '',
|
||||
jenisKelamin: data.jenisKelamin ?? '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -111,11 +103,18 @@ export default function EditMigrasiPenduduk() {
|
||||
loadData();
|
||||
}, [id]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(field: keyof FormData) =>
|
||||
(value: any) => {
|
||||
const val = value || '';
|
||||
setFormData((prev) => ({ ...prev, [field]: val }));
|
||||
const handleChangeText = useCallback(
|
||||
(field: keyof MigrasiPendudukForm) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: e.currentTarget.value }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleChangeSelect = useCallback(
|
||||
(field: keyof MigrasiPendudukForm) =>
|
||||
(value: string | null) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value || '' }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
@@ -127,7 +126,6 @@ export default function EditMigrasiPenduduk() {
|
||||
tanggal: originalData.tanggal,
|
||||
asalTujuan: originalData.asalTujuan,
|
||||
alasan: originalData.alasan,
|
||||
jenisKelamin: originalData.jenisKelamin,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
@@ -182,7 +180,7 @@ export default function EditMigrasiPenduduk() {
|
||||
placeholder="Pilih jenis migrasi"
|
||||
data={jenisOptions}
|
||||
value={formData.jenis}
|
||||
onChange={handleChange('jenis')}
|
||||
onChange={handleChangeSelect('jenis')}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -190,7 +188,7 @@ export default function EditMigrasiPenduduk() {
|
||||
label="Nama"
|
||||
placeholder="Masukkan nama lengkap"
|
||||
value={formData.nama}
|
||||
onChange={handleChange('nama')}
|
||||
onChange={handleChangeText('nama')}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -204,6 +202,7 @@ export default function EditMigrasiPenduduk() {
|
||||
tanggal: val || '',
|
||||
}));
|
||||
}}
|
||||
valueFormat="YYYY-MM-DD"
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -211,7 +210,7 @@ export default function EditMigrasiPenduduk() {
|
||||
label={formData.jenis === 'MASUK' ? 'Asal' : 'Tujuan'}
|
||||
placeholder={formData.jenis === 'MASUK' ? 'Masukkan asal' : 'Masukkan tujuan'}
|
||||
value={formData.asalTujuan}
|
||||
onChange={handleChange('asalTujuan')}
|
||||
onChange={handleChangeText('asalTujuan')}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -219,19 +218,11 @@ export default function EditMigrasiPenduduk() {
|
||||
label="Alasan"
|
||||
placeholder="Masukkan alasan (opsional)"
|
||||
value={formData.alasan}
|
||||
onChange={handleChange('alasan')}
|
||||
onChange={handleChangeText('alasan')}
|
||||
autosize
|
||||
minRows={2}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Pilih jenis kelamin"
|
||||
data={jenisKelaminOptions}
|
||||
value={formData.jenisKelamin}
|
||||
onChange={handleChange('jenisKelamin')}
|
||||
/>
|
||||
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client';
|
||||
|
||||
import colors from '@/con/colors';
|
||||
@@ -33,11 +31,6 @@ function CreateMigrasiPenduduk() {
|
||||
{ value: 'KELUAR', label: 'Keluar' },
|
||||
];
|
||||
|
||||
const jenisKelaminOptions = [
|
||||
{ value: 'L', label: 'Laki-laki' },
|
||||
{ value: 'P', label: 'Perempuan' },
|
||||
];
|
||||
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateMigrasiPenduduk.create.form.jenis?.trim() !== '' &&
|
||||
@@ -54,12 +47,12 @@ function CreateMigrasiPenduduk() {
|
||||
tanggal: '',
|
||||
asalTujuan: '',
|
||||
alasan: '',
|
||||
jenisKelamin: '',
|
||||
};
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const id = await stateMigrasiPenduduk.create.create();
|
||||
if (id) {
|
||||
resetForm();
|
||||
@@ -128,6 +121,7 @@ function CreateMigrasiPenduduk() {
|
||||
onChange={(val: string | null) => {
|
||||
stateMigrasiPenduduk.create.form.tanggal = val || '';
|
||||
}}
|
||||
valueFormat="YYYY-MM-DD"
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -152,16 +146,6 @@ function CreateMigrasiPenduduk() {
|
||||
minRows={2}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Pilih jenis kelamin"
|
||||
data={jenisKelaminOptions}
|
||||
value={stateMigrasiPenduduk.create.form.jenisKelamin}
|
||||
onChange={(val) => {
|
||||
stateMigrasiPenduduk.create.form.jenisKelamin = val || '';
|
||||
}}
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
@@ -58,9 +57,9 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
jenis: string;
|
||||
nama: string;
|
||||
tanggal: string;
|
||||
asalTujuan: string;
|
||||
asal: string | null;
|
||||
tujuan: string | null;
|
||||
alasan: string | null;
|
||||
jenisKelamin: string | null;
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
@@ -89,7 +88,7 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
load(page, 10, debouncedSearch, year);
|
||||
}, [page, debouncedSearch, year]);
|
||||
|
||||
const filteredData = data || [];
|
||||
const filteredData = (data as MigrasiPendudukType[]) || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
@@ -143,16 +142,17 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '10%' }}>Jenis</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Nama</TableTh>
|
||||
<TableTh style={{ width: '12%' }}>Tanggal</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Asal/Tujuan</TableTh>
|
||||
<TableTh style={{ width: '10%' }}>L/P</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>Tanggal</TableTh>
|
||||
<TableTh style={{ width: '25%' }}>Asal/Tujuan</TableTh>
|
||||
<TableTh style={{ width: '10%' }}>Edit</TableTh>
|
||||
<TableTh style={{ width: '10%' }}>Hapus</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item: MigrasiPendudukType) => (
|
||||
filteredData.map((item) => {
|
||||
const asalTujuan = item.jenis === 'MASUK' ? (item.asal || '-') : (item.tujuan || '-');
|
||||
return (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Text
|
||||
@@ -165,8 +165,7 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</TableTd>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>{formatTanggal(item.tanggal)}</TableTd>
|
||||
<TableTd>{item.asalTujuan}</TableTd>
|
||||
<TableTd>{item.jenisKelamin || '-'}</TableTd>
|
||||
<TableTd>{asalTujuan}</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
variant="light"
|
||||
@@ -198,10 +197,11 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<TableTr>
|
||||
<TableTd colSpan={7}>
|
||||
<TableTd colSpan={6}>
|
||||
<Center py={20}>
|
||||
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||
Tidak ada data migrasi penduduk yang cocok
|
||||
@@ -218,7 +218,9 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
<Box hiddenFrom="md">
|
||||
<Stack gap="xs">
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item: MigrasiPendudukType) => (
|
||||
filteredData.map((item) => {
|
||||
const asalTujuan = item.jenis === 'MASUK' ? (item.asal || '-') : (item.tujuan || '-');
|
||||
return (
|
||||
<Paper key={item.id} withBorder p="sm" radius="sm">
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
@@ -251,10 +253,10 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz="sm" fw={600} lh={1.4}>
|
||||
Asal/Tujuan
|
||||
{item.jenis === 'MASUK' ? 'Asal' : 'Tujuan'}
|
||||
</Text>
|
||||
<Text fz="sm" fw={500} lh={1.4}>
|
||||
{item.asalTujuan}
|
||||
{asalTujuan}
|
||||
</Text>
|
||||
</Box>
|
||||
{item.alasan && (
|
||||
@@ -267,14 +269,6 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Box>
|
||||
<Text fz="sm" fw={600} lh={1.4}>
|
||||
Jenis Kelamin
|
||||
</Text>
|
||||
<Text fz="sm" fw={500} lh={1.4}>
|
||||
{item.jenisKelamin || '-'}
|
||||
</Text>
|
||||
</Box>
|
||||
<Group justify="flex-end" gap="xs">
|
||||
<Button
|
||||
variant="light"
|
||||
@@ -305,7 +299,8 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
))
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Center py={20}>
|
||||
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||
|
||||
@@ -79,7 +79,7 @@ const fileStorageCreate = async (context: Context) => {
|
||||
data: {
|
||||
name: finalName,
|
||||
realName: file.name,
|
||||
path: rootPath,
|
||||
path: pathName, // Store relative path (e.g., "images", "audio", "documents")
|
||||
mimeType: finalMimeType,
|
||||
category,
|
||||
link: `/api/fileStorage/findUnique/${finalName}`,
|
||||
|
||||
@@ -36,7 +36,7 @@ const fileStorageDelete = async (context: Context) => {
|
||||
};
|
||||
}
|
||||
|
||||
const filePath = path.join(file.path, file.name);
|
||||
const filePath = path.join(UPLOAD_DIR, file.path, file.name);
|
||||
|
||||
try {
|
||||
// Hapus file dari filesystem
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Context } from "elysia";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
|
||||
const UPLOAD_DIR = process.env.WIBU_UPLOAD_DIR;
|
||||
|
||||
const fileStorageFindUnique = async (context: Context) => {
|
||||
const { name } = context.params;
|
||||
|
||||
@@ -20,9 +22,17 @@ const fileStorageFindUnique = async (context: Context) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (!UPLOAD_DIR) {
|
||||
context.set.status = "Internal Server Error";
|
||||
return {
|
||||
status: 500,
|
||||
message: "UPLOAD_DIR is not defined",
|
||||
};
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
|
||||
const file = await fs.readFile(path.join(data.path, data.name));
|
||||
const file = await fs.readFile(path.join(UPLOAD_DIR, data.path, data.name));
|
||||
context.set.headers = {
|
||||
"Content-Type": data.mimeType,
|
||||
"Content-Length": file.length,
|
||||
|
||||
@@ -10,7 +10,6 @@ export default async function dashboardSummary() {
|
||||
totalKK,
|
||||
totalKelahiran,
|
||||
totalKemiskinan,
|
||||
kelahiranData,
|
||||
kematianData,
|
||||
pindahMasukData,
|
||||
pindahKeluarData,
|
||||
|
||||
@@ -1,38 +1,36 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormCreate = Prisma.MigrasiPendudukGetPayload<{
|
||||
select: {
|
||||
jenis: true;
|
||||
nama: true;
|
||||
tanggal: true;
|
||||
asalTujuan: true;
|
||||
alasan: true;
|
||||
jenisKelamin: true;
|
||||
}
|
||||
}>
|
||||
type FormCreate = {
|
||||
jenis: string;
|
||||
nama: string;
|
||||
tanggal: string;
|
||||
asalTujuan: string;
|
||||
alasan: string | null;
|
||||
}
|
||||
|
||||
export default async function migrasiPendudukCreate(context: Context) {
|
||||
const body = context.body as FormCreate;
|
||||
|
||||
const isMasuk = body.jenis === 'MASUK';
|
||||
|
||||
const created = await prisma.migrasiPenduduk.create({
|
||||
data: {
|
||||
jenis: body.jenis,
|
||||
jenis: body.jenis as 'MASUK' | 'KELUAR',
|
||||
nama: body.nama,
|
||||
tanggal: new Date(body.tanggal),
|
||||
asalTujuan: body.asalTujuan,
|
||||
asal: isMasuk ? body.asalTujuan : null,
|
||||
tujuan: !isMasuk ? body.asalTujuan : null,
|
||||
alasan: body.alasan,
|
||||
jenisKelamin: body.jenisKelamin,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
jenis: true,
|
||||
nama: true,
|
||||
tanggal: true,
|
||||
asalTujuan: true,
|
||||
asal: true,
|
||||
tujuan: true,
|
||||
alasan: true,
|
||||
jenisKelamin: true,
|
||||
}
|
||||
});
|
||||
return {
|
||||
|
||||
@@ -21,7 +21,6 @@ const MigrasiPenduduk = new Elysia({
|
||||
tanggal: t.String(),
|
||||
asalTujuan: t.String(),
|
||||
alasan: t.Optional(t.String()),
|
||||
jenisKelamin: t.Optional(t.String()),
|
||||
}),
|
||||
})
|
||||
.put("/:id", migrasiPendudukUpdate, {
|
||||
@@ -34,7 +33,6 @@ const MigrasiPenduduk = new Elysia({
|
||||
tanggal: t.String(),
|
||||
asalTujuan: t.String(),
|
||||
alasan: t.Optional(t.String()),
|
||||
jenisKelamin: t.Optional(t.String()),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", migrasiPendudukDelete, {
|
||||
|
||||
@@ -11,13 +11,12 @@ export default async function migrasiPendudukUpdate(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
const {jenis, nama, tanggal, asalTujuan, alasan, jenisKelamin} = context.body as {
|
||||
const {jenis, nama, tanggal, asalTujuan, alasan} = context.body as {
|
||||
jenis: string;
|
||||
nama: string;
|
||||
tanggal: string;
|
||||
asalTujuan: string;
|
||||
alasan?: string;
|
||||
jenisKelamin?: string;
|
||||
}
|
||||
|
||||
const existing = await prisma.migrasiPenduduk.findUnique({
|
||||
@@ -36,12 +35,21 @@ export default async function migrasiPendudukUpdate(context: Context) {
|
||||
const updated = await prisma.migrasiPenduduk.update({
|
||||
where: { id },
|
||||
data: {
|
||||
jenis,
|
||||
jenis: jenis as 'MASUK' | 'KELUAR',
|
||||
nama,
|
||||
tanggal: new Date(tanggal),
|
||||
asalTujuan,
|
||||
asal: jenis === 'MASUK' ? asalTujuan : null,
|
||||
tujuan: jenis === 'KELUAR' ? asalTujuan : null,
|
||||
alasan,
|
||||
jenisKelamin,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
jenis: true,
|
||||
nama: true,
|
||||
tanggal: true,
|
||||
asal: true,
|
||||
tujuan: true,
|
||||
alasan: true,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Paper, Text, Title, SimpleGrid, Skeleton, Group, Badge, Center, Image } from '@mantine/core';
|
||||
import { Stack, Box, Paper, Text, Title, SimpleGrid, Skeleton, Group, Badge, Center } from '@mantine/core';
|
||||
import { IconUsers, IconHome, IconBasket, IconCoin, IconDatabaseOff } from '@tabler/icons-react';
|
||||
import React from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import kependudukanDashboard from '@/app/admin/(dashboard)/_state/kependudukan/dashboard';
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Paper, Text, Title, Skeleton, Table, Center } from '@mantine/core';
|
||||
import { IconDatabaseOff } from '@tabler/icons-react';
|
||||
import React from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import dataBanjar from '@/app/admin/(dashboard)/_state/kependudukan/data-banjar';
|
||||
@@ -15,7 +14,16 @@ function Page() {
|
||||
state.findMany.load()
|
||||
}, [])
|
||||
|
||||
const data = state.findMany.data || [];
|
||||
interface DataBanjarItem {
|
||||
id: string;
|
||||
nama: string;
|
||||
penduduk: number;
|
||||
kk: number;
|
||||
miskin: number;
|
||||
tahun: number;
|
||||
}
|
||||
|
||||
const data = (state.findMany.data as DataBanjarItem[]) || [];
|
||||
|
||||
if (state.findMany.loading) {
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Paper, Text, Title, Skeleton, Flex, ColorSwatch, Center } from '@mantine/core';
|
||||
import { IconDatabaseOff } from '@tabler/icons-react';
|
||||
import React from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import distribusiAgama from '@/app/admin/(dashboard)/_state/kependudukan/distribusi-agama';
|
||||
@@ -16,7 +15,14 @@ function Page() {
|
||||
state.findMany.load()
|
||||
}, [])
|
||||
|
||||
const data = state.findMany.data || [];
|
||||
interface DistribusiAgamaItem {
|
||||
id: string;
|
||||
agama: string;
|
||||
jumlah: number;
|
||||
tahun: number;
|
||||
}
|
||||
|
||||
const data = (state.findMany.data as DistribusiAgamaItem[]) || [];
|
||||
|
||||
if (state.findMany.loading) {
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Paper, Text, Title, Skeleton, Center } from '@mantine/core';
|
||||
import { IconDatabaseOff } from '@tabler/icons-react';
|
||||
import React from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import distribusiUmur from '@/app/admin/(dashboard)/_state/kependudukan/distribusi-umur';
|
||||
@@ -16,7 +15,14 @@ function Page() {
|
||||
state.findMany.load()
|
||||
}, [])
|
||||
|
||||
const data = state.findMany.data || [];
|
||||
interface DistribusiUmurItem {
|
||||
id: string;
|
||||
rentangUmur: string;
|
||||
jumlah: number;
|
||||
tahun: number;
|
||||
}
|
||||
|
||||
const data = (state.findMany.data as DistribusiUmurItem[]) || [];
|
||||
|
||||
if (state.findMany.loading) {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user