feat: add kependudukan seeders, API routes, year filter, and navbar menu
- Add Prisma models: DataBanjar, DistribusiAgama, DistribusiUmur, MigrasiPenduduk, DinamikaPenduduk - Create seeders for all kependudukan models with year 2026 data - Register Kependudukan API routes in route.ts - Update API findMany endpoints to make tahun parameter optional - Add YearFilter reusable component for admin pages - Update 4 kependudukan admin pages with year filter UI - Fix Mantine color array in AdminThemeProvider (add 10th element) - Fix invalid Mantine color scale in paguTable.tsx (gray.50 -> gray.1) - Add Kependudukan menu to navbar-list-menu.ts - Fix Bun JSON import resolution with loadJsonData helper - Update 74 seeder files to use dynamic JSON loading Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -58,14 +58,17 @@ const dataBanjar = proxy({
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", tahun = new Date().getFullYear()) => {
|
||||
tahun: undefined as number | undefined,
|
||||
load: async (page = 1, limit = 10, search = "", tahun?: number) => {
|
||||
dataBanjar.findMany.loading = true;
|
||||
dataBanjar.findMany.page = page;
|
||||
dataBanjar.findMany.search = search;
|
||||
dataBanjar.findMany.tahun = tahun;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit, tahun };
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (tahun) query.tahun = tahun;
|
||||
|
||||
const res = await ApiFetch.api.kependudukan.databanjar["find-many"].get({ query });
|
||||
|
||||
|
||||
@@ -54,13 +54,14 @@ const distribusiAgama = proxy({
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", tahun = new Date().getFullYear()) => {
|
||||
load: async (page = 1, limit = 10, search = "", tahun?: number) => {
|
||||
distribusiAgama.findMany.loading = true;
|
||||
distribusiAgama.findMany.page = page;
|
||||
distribusiAgama.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit, tahun };
|
||||
const query: any = { page, limit };
|
||||
if (tahun) query.tahun = tahun;
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kependudukan.distribusiagama["find-many"].get({ query });
|
||||
|
||||
@@ -54,13 +54,14 @@ const distribusiUmur = proxy({
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", tahun = new Date().getFullYear()) => {
|
||||
load: async (page = 1, limit = 10, search = "", tahun?: number) => {
|
||||
distribusiUmur.findMany.loading = true;
|
||||
distribusiUmur.findMany.page = page;
|
||||
distribusiUmur.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit, tahun };
|
||||
const query: any = { page, limit };
|
||||
if (tahun) query.tahun = tahun;
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kependudukan.distribusiumur["find-many"].get({ query });
|
||||
|
||||
@@ -60,13 +60,14 @@ const migrasiPenduduk = proxy({
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", tahun = new Date().getFullYear()) => {
|
||||
load: async (page = 1, limit = 10, search = "", tahun?: number) => {
|
||||
migrasiPenduduk.findMany.loading = true;
|
||||
migrasiPenduduk.findMany.page = page;
|
||||
migrasiPenduduk.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit, tahun };
|
||||
const query: any = { page, limit };
|
||||
if (tahun) query.tahun = tahun;
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kependudukan.migrasipenduduk["find-many"].get({ query });
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Select, Group } from '@mantine/core';
|
||||
import { IconCalendar } from '@tabler/icons-react';
|
||||
|
||||
interface YearFilterProps {
|
||||
value?: number;
|
||||
onChange: (year: number | undefined) => void;
|
||||
minYear?: number;
|
||||
maxYear?: number;
|
||||
}
|
||||
|
||||
export function YearFilter({
|
||||
value,
|
||||
onChange,
|
||||
minYear = 2020,
|
||||
maxYear = new Date().getFullYear() + 1,
|
||||
}: YearFilterProps) {
|
||||
const years = Array.from(
|
||||
{ length: maxYear - minYear + 1 },
|
||||
(_, i) => maxYear - i
|
||||
);
|
||||
|
||||
const options = [
|
||||
{ value: '', label: 'Semua Tahun' },
|
||||
...years.map((year) => ({
|
||||
value: year.toString(),
|
||||
label: year.toString(),
|
||||
})),
|
||||
];
|
||||
|
||||
return (
|
||||
<Select
|
||||
placeholder="Pilih Tahun"
|
||||
data={options}
|
||||
value={value?.toString() ?? ''}
|
||||
onChange={(val) => onChange(val ? parseInt(val) : undefined)}
|
||||
leftSection={<IconCalendar size={18} />}
|
||||
clearable
|
||||
w={200}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default YearFilter;
|
||||
@@ -27,9 +27,12 @@ import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
|
||||
import dataBanjar from '../../_state/kependudukan/data-banjar';
|
||||
import { YearFilter } from '../_components/YearFilter';
|
||||
|
||||
function DataBanjarAdmin() {
|
||||
const [search, setSearch] = useState('');
|
||||
const [selectedYear, setSelectedYear] = useState<number | undefined>(undefined);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
@@ -39,12 +42,18 @@ function DataBanjarAdmin() {
|
||||
value={search}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListDataBanjar search={search} />
|
||||
<Box mt="md">
|
||||
<YearFilter value={selectedYear} onChange={(year) => {
|
||||
setSelectedYear(year);
|
||||
dataBanjar.findMany.page = 1;
|
||||
}} />
|
||||
</Box>
|
||||
<ListDataBanjar search={search} year={selectedYear} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListDataBanjar({ search }: { search: string }) {
|
||||
function ListDataBanjar({ search, year }: { search: string; year?: number }) {
|
||||
type DataBanjarType = {
|
||||
id: string;
|
||||
nama: string;
|
||||
@@ -77,8 +86,8 @@ function ListDataBanjar({ search }: { search: string }) {
|
||||
};
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
load(page, 10, debouncedSearch, year);
|
||||
}, [page, debouncedSearch, year]);
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
|
||||
@@ -25,12 +25,14 @@ import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { YearFilter } from '../_components/YearFilter';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
|
||||
import distribusiAgama from '../../_state/kependudukan/distribusi-agama';
|
||||
|
||||
function DistribusiAgamaAdmin() {
|
||||
const [search, setSearch] = useState('');
|
||||
const [selectedYear, setSelectedYear] = useState<number | undefined>(undefined);
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
@@ -40,12 +42,18 @@ function DistribusiAgamaAdmin() {
|
||||
value={search}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListDistribusiAgama search={search} />
|
||||
<Box mt="md">
|
||||
<YearFilter value={selectedYear} onChange={(year) => {
|
||||
setSelectedYear(year);
|
||||
distribusiAgama.findMany.page = 1;
|
||||
}} />
|
||||
</Box>
|
||||
<ListDistribusiAgama search={search} year={selectedYear} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListDistribusiAgama({ search }: { search: string }) {
|
||||
function ListDistribusiAgama({ search, year }: { search: string; year?: number }) {
|
||||
type DistribusiAgamaType = {
|
||||
id: string;
|
||||
agama: string;
|
||||
@@ -76,8 +84,8 @@ function ListDistribusiAgama({ search }: { search: string }) {
|
||||
};
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
load(page, 10, debouncedSearch, year);
|
||||
}, [page, debouncedSearch, year]);
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
|
||||
@@ -25,12 +25,14 @@ import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { YearFilter } from '../_components/YearFilter';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
|
||||
import distribusiUmur from '../../_state/kependudukan/distribusi-umur';
|
||||
|
||||
function DistribusiUmurAdmin() {
|
||||
const [search, setSearch] = useState('');
|
||||
const [selectedYear, setSelectedYear] = useState<number | undefined>(undefined);
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
@@ -40,12 +42,18 @@ function DistribusiUmurAdmin() {
|
||||
value={search}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListDistribusiUmur search={search} />
|
||||
<Box mt="md">
|
||||
<YearFilter value={selectedYear} onChange={(year) => {
|
||||
setSelectedYear(year);
|
||||
distribusiUmur.findMany.page = 1;
|
||||
}} />
|
||||
</Box>
|
||||
<ListDistribusiUmur search={search} year={selectedYear} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListDistribusiUmur({ search }: { search: string }) {
|
||||
function ListDistribusiUmur({ search, year }: { search: string; year?: number }) {
|
||||
type DistribusiUmurType = {
|
||||
id: string;
|
||||
rentangUmur: string;
|
||||
@@ -77,8 +85,8 @@ function ListDistribusiUmur({ search }: { search: string }) {
|
||||
};
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
load(page, 10, debouncedSearch, year);
|
||||
}, [page, debouncedSearch, year]);
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
|
||||
@@ -24,12 +24,14 @@ import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { YearFilter } from '../_components/YearFilter';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
|
||||
import migrasiPenduduk from '../../_state/kependudukan/migrasi-penduduk';
|
||||
|
||||
function MigrasiPendudukAdmin() {
|
||||
const [search, setSearch] = useState('');
|
||||
const [selectedYear, setSelectedYear] = useState<number | undefined>(undefined);
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
@@ -39,12 +41,18 @@ function MigrasiPendudukAdmin() {
|
||||
value={search}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListMigrasiPenduduk search={search} />
|
||||
<Box mt="md">
|
||||
<YearFilter value={selectedYear} onChange={(year) => {
|
||||
setSelectedYear(year);
|
||||
migrasiPenduduk.findMany.page = 1;
|
||||
}} />
|
||||
</Box>
|
||||
<ListMigrasiPenduduk search={search} year={selectedYear} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListMigrasiPenduduk({ search }: { search: string }) {
|
||||
function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }) {
|
||||
type MigrasiPendudukType = {
|
||||
id: string;
|
||||
jenis: string;
|
||||
@@ -78,8 +86,8 @@ function ListMigrasiPenduduk({ search }: { search: string }) {
|
||||
};
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
load(page, 10, debouncedSearch, year);
|
||||
}, [page, debouncedSearch, year]);
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user