From 1c01397c0d30d2d3596ba771900ce848e9369475 Mon Sep 17 00:00:00 2001 From: nico Date: Wed, 20 Aug 2025 17:17:04 +0800 Subject: [PATCH] Sinkronisasi UI & API Admin - User Submenu lowongan kerja lokal, Menu Ekonomi --- .../_state/ekonomi/lowongan-kerja.ts | 37 +++++++- .../ekonomi/lowongan-kerja-lokal/page.tsx | 51 ++++++---- .../_lib/ekonomi/lowongan-kerja/findMany.ts | 70 ++++++++++---- .../ekonomi/lowongan-kerja-lokal/page.tsx | 95 ++++++++----------- 4 files changed, 157 insertions(+), 96 deletions(-) diff --git a/src/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja.ts b/src/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja.ts index 5858459e..0481bbca 100644 --- a/src/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja.ts +++ b/src/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja.ts @@ -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"; @@ -61,13 +62,39 @@ const lowonganKerjaState = proxy({ findMany: { data: null as | Prisma.LowonganPekerjaanGetPayload<{ - omit: { isActive: true }; + omit: { + isActive: true; + }; }>[] | null, - async load() { - const res = await ApiFetch.api.ekonomi.lowongankerja["find-many"].get(); - if (res.status === 200) { - lowonganKerjaState.findMany.data = res.data?.data ?? []; + page: 1, + totalPages: 1, + loading: false, + search: "", + load: async (page = 1, limit = 10, search = "") => { + lowonganKerjaState.findMany.loading = true; // ✅ Akses langsung via nama path + lowonganKerjaState.findMany.page = page; + lowonganKerjaState.findMany.search = search; + + try { + const query: any = { page, limit }; + if (search) query.search = search; + + const res = await ApiFetch.api.ekonomi.lowongankerja["find-many"].get({ query }); + + if (res.status === 200 && res.data?.success) { + lowonganKerjaState.findMany.data = res.data.data ?? []; + lowonganKerjaState.findMany.totalPages = res.data.totalPages ?? 1; + } else { + lowonganKerjaState.findMany.data = []; + lowonganKerjaState.findMany.totalPages = 1; + } + } catch (err) { + console.error("Gagal fetch lowongan kerja paginated:", err); + lowonganKerjaState.findMany.data = []; + lowonganKerjaState.findMany.totalPages = 1; + } finally { + lowonganKerjaState.findMany.loading = false; } }, }, diff --git a/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/page.tsx b/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/page.tsx index f134486b..98923636 100644 --- a/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/page.tsx @@ -1,6 +1,6 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import HeaderSearch from '../../_com/header'; import JudulList from '../../_com/judulList'; @@ -30,20 +30,21 @@ function ListLowonganKerjaLokal({ search }: { search: string }) { const lowonganState = useProxy(lowonganKerjaState) const router = useRouter(); + const { + data, + page, + totalPages, + loading, + load, + } = lowonganState.findMany + useShallowEffect(() => { - lowonganState.findMany.load(); - }, []) + load(page, 10, search) + }, [page, search]) - const filteredData = (lowonganState.findMany.data || []).filter(item => { - const keyword = search.toLowerCase(); - return ( - item.posisi.toLowerCase().includes(keyword) || - item.namaPerusahaan.toLowerCase().includes(keyword) || - item.lokasi.toLowerCase().includes(keyword) - ); - }); + const filteredData = data || [] - if (!lowonganState.findMany.data) { + if (loading || !data) { return ( @@ -60,18 +61,24 @@ function ListLowonganKerjaLokal({ search }: { search: string }) { - Bekerja Sebagai - Nama Usaha - Alamat Usaha + Pekerjaan + Nama Perusahaan + Lokasi Detail {filteredData.map((item) => ( - {item.posisi} - {item.namaPerusahaan} - {item.lokasi} + + {item.posisi} + + + {item.namaPerusahaan} + + + {item.lokasi} +
+
+ load(newPage)} + total={totalPages} + my="md" + /> +
); } diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/findMany.ts index 6f38d69b..b544b3ad 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/findMany.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/findMany.ts @@ -1,21 +1,55 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// /api/berita/findManyPaginated.ts import prisma from "@/lib/prisma"; +import { Context } from "elysia"; -export default async function lowonganKerjaFindMany() { - try { - const data = await prisma.lowonganPekerjaan.findMany({ - where: { isActive: true }, - }); +async function lowonganKerjaFindMany(context: Context) { + // Ambil parameter dari query + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ''; + const skip = (page - 1) * limit; - return { - success: true, - message: "Success fetch lowongan kerja", - data, - }; - } catch (e) { - console.error("Find many error:", e); - return { - success: false, - message: "Failed fetch lowongan kerja", - }; - } -} \ No newline at end of file + // Buat where clause + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { posisi: { contains: search, mode: 'insensitive' } }, + { namaPerusahaan: { contains: search, mode: 'insensitive' } }, + { lokasi: { contains: search, mode: 'insensitive' } }, + ]; + } + + try { + // Ambil data dan total count secara paralel + const [data, total] = await Promise.all([ + prisma.lowonganPekerjaan.findMany({ + where, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + prisma.lowonganPekerjaan.count({ where }), + ]); + + return { + success: true, + message: "Berhasil ambil lowongan kerja dengan pagination", + data, + page, + limit, + total, + totalPages: Math.ceil(total / limit), + }; + } catch (e) { + console.error("Error di findMany paginated:", e); + return { + success: false, + message: "Gagal mengambil data lowongan kerja", + }; + } +} + +export default lowonganKerjaFindMany; \ No newline at end of file diff --git a/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx b/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx index 8ba40f3d..ad537350 100644 --- a/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx +++ b/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx @@ -1,64 +1,39 @@ 'use client' import colors from '@/con/colors'; -import { Stack, Box, Text, TextInput, Group, SimpleGrid, Paper, Flex, Button } from '@mantine/core'; +import { Stack, Box, Text, TextInput, Group, SimpleGrid, Paper, Flex, Button, Skeleton, Center, Pagination } from '@mantine/core'; import React from 'react'; import BackButton from '../../desa/layanan/_com/BackButto'; import { IconBriefcase, IconClock, IconMapPin, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; +import { useProxy } from 'valtio/utils'; +import lowonganKerjaState from '@/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja'; +import { useShallowEffect } from '@mantine/hooks'; +import { useState } from 'react'; -const data = [ - { - id: 1, - kerja: 'Kasir', - tempat: 'Toko Sumber Rejeki', - alamat: 'Desa Munggu , Badung', - gaji: 'Rp. 2.500.000 / bulan' - - }, - { - id: 2, - kerja: 'Kasir', - tempat: 'Toko Sumber Rejeki', - alamat: 'Desa Munggu , Badung', - gaji: 'Rp. 2.500.000 / bulan' - - }, - { - id: 3, - kerja: 'Kasir', - tempat: 'Toko Sumber Rejeki', - alamat: 'Desa Munggu , Badung', - gaji: 'Rp. 2.500.000 / bulan' - - }, - { - id: 4, - kerja: 'Kasir', - tempat: 'Toko Sumber Rejeki', - alamat: 'Desa Munggu , Badung', - gaji: 'Rp. 2.500.000 / bulan' - - }, - { - id: 5, - kerja: 'Kasir', - tempat: 'Toko Sumber Rejeki', - alamat: 'Desa Munggu , Badung', - gaji: 'Rp. 2.500.000 / bulan' - - }, - { - id: 6, - kerja: 'Kasir', - tempat: 'Toko Sumber Rejeki', - alamat: 'Desa Munggu , Badung', - gaji: 'Rp. 2.500.000 / bulan' - - }, - -] function Page() { + const state = useProxy(lowonganKerjaState) const router = useRouter() + const [search, setSearch] = useState('') + + const { + data, + page, + totalPages, + loading, + load, + } = state.findMany + + useShallowEffect(() => { + load(page, 6, search) + }, [page, search]) + + if (loading || !data) { + return ( + + + + ) + } return ( @@ -74,6 +49,8 @@ function Page() { w={{ base: 500, md: 700 }} placeholder='Cari Pekerjaan' leftSection={} + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} /> @@ -93,15 +70,15 @@ function Page() { - {v.kerja} - {v.tempat} + {v.posisi} + {v.namaPerusahaan} - {v.alamat} + {v.lokasi} @@ -119,6 +96,14 @@ function Page() { ) })} +
+ load(newPage)} + total={totalPages} + my="md" + /> +