UI & API Menu Pendidikan, Submenu Beasiswa tab beasiswa pendaftar

This commit is contained in:
2025-07-29 12:29:25 +08:00
parent 50e8999205
commit 0109886e00
18 changed files with 1129 additions and 6 deletions

View File

@@ -0,0 +1,63 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
function LayoutTabs({ children }: { children: React.ReactNode }) {
const router = useRouter()
const pathname = usePathname()
const tabs = [
{
label: "Beasiswa Pendaftar",
value: "beasiswa-pendaftar",
href: "/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar"
},
{
label: "Keunggulan Program",
value: "keunggulan-program",
href: "/admin/pendidikan/beasiswa-desa/keunggulan-program"
},
];
const curentTab = tabs.find(tab => tab.href === pathname)
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
const handleTabChange = (value: string | null) => {
const tab = tabs.find(t => t.value === value)
if (tab) {
router.push(tab.href)
}
setActiveTab(value)
}
useEffect(() => {
const match = tabs.find(tab => tab.href === pathname)
if (match) {
setActiveTab(match.value)
}
}, [pathname])
return (
<Stack>
<Title order={3}>Beasiswa Desa</Title>
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
{tabs.map((e, i) => (
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
))}
</TabsList>
{tabs.map((e, i) => (
<TabsPanel key={i} value={e.value}>
{/* Konten dummy, bisa diganti tergantung routing */}
<></>
</TabsPanel>
))}
</Tabs>
{children}
</Stack>
);
}
export default LayoutTabs;

View File

@@ -0,0 +1,137 @@
'use client'
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
import beasiswaDesaState from '@/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa';
import colors from '@/con/colors';
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
function DetailBeasiswaPendaftar() {
const state = useProxy(beasiswaDesaState.beasiswaPendaftar)
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null);
const router = useRouter()
const params = useParams()
useShallowEffect(() => {
state.findUnique.load(params?.id as string)
}, [])
const handleHapus = () => {
if (selectedId) {
state.delete.delete(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar")
}
}
if (!state.findUnique.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
<Stack>
<Flex justify="space-between" gap={"xs"}>
<Text fz={"xl"} fw={"bold"}>Detail Beasiswa Pendaftar</Text>
<Button
onClick={() => {
if (state.findUnique.data) {
setSelectedId(state.findUnique.data.id);
setModalHapus(true);
}
}}
disabled={state.delete.loading || !state.findUnique.data}
color={"red"}
>
<IconX size={20} />
</Button>
</Flex>
{state.findUnique.data ? (
<Paper key={state.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fw={"bold"} fz={"lg"}>Nama Lengkap</Text>
<Text fz={"lg"}>{state.findUnique.data?.namaLengkap}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>NIK</Text>
<Text fz={"lg"}>{state.findUnique.data?.nik}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Tempat Lahir</Text>
<Text fz={"lg"}>{state.findUnique.data?.tempatLahir}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Tanggal Lahir</Text>
<Text fz={"lg"}>{state.findUnique.data?.tanggalLahir ? new Date(state.findUnique.data.tanggalLahir).toLocaleDateString() : '-'}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Jenis Kelamin</Text>
<Text fz={"lg"}>{state.findUnique.data?.jenisKelamin}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Kewarganegaraan</Text>
<Text fz={"lg"}>{state.findUnique.data?.kewarganegaraan}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Agama</Text>
<Text fz={"lg"}>{state.findUnique.data?.agama}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Alamat KTP</Text>
<Text fz={"lg"}>{state.findUnique.data?.alamatKTP}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Alamat Domisili</Text>
<Text fz={"lg"}>{state.findUnique.data?.alamatDomisili}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>No HP</Text>
<Text fz={"lg"}>{state.findUnique.data?.noHp}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Email</Text>
<Text fz={"lg"}>{state.findUnique.data?.email}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Status Pernikahan</Text>
<Text fz={"lg"}>{state.findUnique.data?.statusPernikahan}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Ukuran Baju</Text>
<Text fz={"lg"}>{state.findUnique.data?.ukuranBaju}</Text>
</Box>
</Stack>
</Paper>
) : null}
</Stack>
</Paper>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text='Apakah anda yakin ingin menghapus beasiswa desa ini?'
/>
</Box>
);
}
export default DetailBeasiswaPendaftar;

View File

@@ -0,0 +1,107 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import HeaderSearch from '../../../_com/header';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import beasiswaDesaState from '../../../_state/pendidikan/beasiswa-desa';
function BeasiswaPendaftar() {
const [search, setSearch] = useState('');
return (
<Box>
<HeaderSearch
title='Beasiswa Pendaftar'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListBeasiswaPendaftar search={search} />
</Box>
);
}
function ListBeasiswaPendaftar({ search }: { search: string }) {
const listDataState = useProxy(beasiswaDesaState.beasiswaPendaftar)
const router = useRouter();
useEffect(() => {
listDataState.findMany.load()
}, [])
const filteredData = (listDataState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.namaLengkap.toLowerCase().includes(keyword) ||
item.alamatKTP.toLowerCase().includes(keyword) ||
(item.tanggalLahir ? new Date(item.tanggalLahir).toLocaleDateString() : '').toLowerCase().includes(keyword) ||
item.jenisKelamin.toLowerCase().includes(keyword)
);
});
if (!listDataState.findMany.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p="md">
<Stack>
<Title order={4}>List Beasiswa Pendaftar</Title>
<Box style={{ overflowX: 'auto' }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>No</TableTh>
<TableTh>Nama Lengkap</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Tanggal Lahir</TableTh>
<TableTh>Jenis Kelamin</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item, index) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{index + 1}</Text>
</Box>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"}>{item.namaLengkap}</Text>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"}>{item.alamatKTP}</Text>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"}>{item.tanggalLahir ? new Date(item.tanggalLahir).toLocaleDateString() : '-'}</Text>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"}>{item.jenisKelamin}</Text>
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default BeasiswaPendaftar;

View File

@@ -3,13 +3,14 @@ import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import HeaderSearch from '../../_com/header';
import HeaderSearch from '../../../_com/header';
function BeasiswaDesa() {
return (
<Box>
<HeaderSearch
title='Beasiswa Desa'
title='Keunggulan Program'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>

View File

@@ -0,0 +1,13 @@
'use client'
import React from 'react';
import LayoutTabs from './_lib/layoutTabs';
function Layout({ children }: { children: React.ReactNode }) {
return (
<LayoutTabs>
{children}
</LayoutTabs>
);
}
export default Layout;