API & UI Program Kemiskinan Menu Ekonomi
This commit is contained in:
@@ -1,45 +1,125 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
|
|
||||||
|
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||||
|
import programKemiskinanState from '@/app/admin/(dashboard)/_state/ekonomi/program-kemiskinan';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter, useParams } from 'next/navigation';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
function EditProgramKemiskinan() {
|
function EditProgramKemiskinan() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const params = useParams() as { id: string };
|
||||||
|
const stateProgram = useProxy(programKemiskinanState);
|
||||||
|
const id = params.id;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
stateProgram.findUnique.load(id).then(() => {
|
||||||
|
const data = stateProgram.findUnique.data;
|
||||||
|
if (data) {
|
||||||
|
stateProgram.update.form = {
|
||||||
|
nama: data.nama || '',
|
||||||
|
deskripsi: data.deskripsi || '',
|
||||||
|
ikonUrl: data.ikonUrl || '',
|
||||||
|
statistik: {
|
||||||
|
tahun: data.statistik?.tahun?.toString() || '',
|
||||||
|
jumlah: data.statistik?.jumlah?.toString() || '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
stateProgram.update.id = id;
|
||||||
|
await stateProgram.update.update();
|
||||||
|
router.push('/admin/ekonomi/program-kemiskinan');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box mb={10}>
|
<Box mb={10}>
|
||||||
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
<Button onClick={() => router.back()} variant="subtle" color="blue">
|
||||||
<IconArrowBack color={colors['blue-button']} size={25}/>
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p="md">
|
||||||
<Stack gap={"xs"}>
|
<Stack gap="xs">
|
||||||
<Title order={4}>Edit Program Kemiskinan</Title>
|
<Title order={4}>Edit Program Kemiskinan</Title>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Judul Program</Text>}
|
value={stateProgram.update.form.nama}
|
||||||
placeholder='Masukkan judul program'
|
onChange={(e) => {
|
||||||
|
stateProgram.update.form.nama = e.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fw="bold" fz="md">Judul Program</Text>}
|
||||||
|
placeholder="Masukkan judul program"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fw="bold" fz="md">Deskripsi</Text>
|
||||||
|
<EditEditor
|
||||||
|
value={stateProgram.update.form.deskripsi}
|
||||||
|
onChange={(val) => {
|
||||||
|
stateProgram.update.form.deskripsi = val;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Deskripsi Singkat</Text>}
|
value={stateProgram.update.form.ikonUrl}
|
||||||
placeholder='Masukkan deskripsi'
|
onChange={(e) => {
|
||||||
|
stateProgram.update.form.ikonUrl = e.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fw="bold" fz="md">Ikon URL</Text>}
|
||||||
|
placeholder="Masukkan ikon url"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Text fw="bold" fz="md">Statistik Jumlah Masyarakat Miskin</Text>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Jumlah Masyarakat Miskin</Text>}
|
type="number"
|
||||||
placeholder='Masukkan jumlah masyarakat miskin'
|
value={stateProgram.update.form.statistik.jumlah}
|
||||||
|
onChange={(e) => {
|
||||||
|
stateProgram.update.form.statistik.jumlah = e.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fw="bold" fz="md">Jumlah Masyarakat Miskin</Text>}
|
||||||
|
placeholder="Masukkan jumlah masyarakat miskin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>}
|
type="number"
|
||||||
placeholder='Masukkan deskripsi'
|
value={stateProgram.update.form.statistik.tahun}
|
||||||
|
onChange={(e) => {
|
||||||
|
stateProgram.update.form.statistik.tahun = e.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fw="bold" fz="md">Tahun</Text>}
|
||||||
|
placeholder="Masukkan tahun"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Group>
|
<Group>
|
||||||
<Button bg={colors['blue-button']}>Submit</Button>
|
<Button bg={colors['blue-button']} onClick={handleSubmit}>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
import programKemiskinanState from '../../../_state/ekonomi/program-kemiskinan';
|
import programKemiskinanState from '../../../_state/ekonomi/program-kemiskinan';
|
||||||
@@ -14,15 +12,12 @@ import programKemiskinanState from '../../../_state/ekonomi/program-kemiskinan';
|
|||||||
|
|
||||||
function DetailProgramKemiskinan() {
|
function DetailProgramKemiskinan() {
|
||||||
const programState = useProxy(programKemiskinanState)
|
const programState = useProxy(programKemiskinanState)
|
||||||
const [lineChart, setLineChart] = useState<any[]>([]);
|
|
||||||
const [mounted, setMounted] = useState(false);
|
|
||||||
const [modalHapus, setModalHapus] = useState(false)
|
const [modalHapus, setModalHapus] = useState(false)
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
setMounted(true);
|
|
||||||
programState.findUnique.load(params?.id as string)
|
programState.findUnique.load(params?.id as string)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -35,12 +30,6 @@ function DetailProgramKemiskinan() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (programState.findUnique.data) {
|
|
||||||
setLineChart([programState.findUnique.data]);
|
|
||||||
}
|
|
||||||
}, [programState.findUnique.data])
|
|
||||||
|
|
||||||
if (!programState.findUnique.data) {
|
if (!programState.findUnique.data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
@@ -62,24 +51,24 @@ function DetailProgramKemiskinan() {
|
|||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"}>Judul Program</Text>
|
<Text fw={"bold"}>Judul Program</Text>
|
||||||
<Text fz={"sm"}>{programState.findUnique.data?.nama}</Text>
|
<Text fz={"md"}>{programState.findUnique.data?.nama}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"}>Deskripsi Singkat</Text>
|
<Text fw={"bold"}>Deskripsi Singkat</Text>
|
||||||
<Text fz={"sm"} dangerouslySetInnerHTML={{ __html: programState.findUnique.data?.deskripsi }}></Text>
|
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: programState.findUnique.data?.deskripsi }}></Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"}>Ikon URL</Text>
|
<Text fw={"bold"}>Ikon URL</Text>
|
||||||
<Text fz={"sm"}>{programState.findUnique.data?.ikonUrl}</Text>
|
<Text fz={"md"}>{programState.findUnique.data?.ikonUrl}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Text fw={"bold"}>Statistik Jumlah Masyarakat Miskin</Text>
|
<Text fw={"bold"}>Statistik Jumlah Masyarakat Miskin</Text>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"}>Jumlah Masyarakat Miskin</Text>
|
<Text fw={"bold"}>Jumlah Masyarakat Miskin</Text>
|
||||||
<Text fz={"sm"}>{programState.findUnique.data?.statistik?.jumlah}</Text>
|
<Text fz={"md"}>{programState.findUnique.data?.statistik?.jumlah}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"}>Tahun</Text>
|
<Text fw={"bold"}>Tahun</Text>
|
||||||
<Text fz={"sm"}>{programState.findUnique.data?.statistik?.tahun}</Text>
|
<Text fz={"md"}>{programState.findUnique.data?.statistik?.tahun}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Flex gap={"xs"} mt={10}>
|
<Flex gap={"xs"} mt={10}>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
import HeaderSearch from '../../_com/header';
|
import HeaderSearch from '../../_com/header';
|
||||||
import JudulList from '../../_com/judulList';
|
import JudulList from '../../_com/judulList';
|
||||||
@@ -8,6 +9,8 @@ import { useRouter } from 'next/navigation';
|
|||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import programKemiskinanState from '../../_state/ekonomi/program-kemiskinan';
|
import programKemiskinanState from '../../_state/ekonomi/program-kemiskinan';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from 'recharts';
|
||||||
|
|
||||||
function ProgramKemiskinan() {
|
function ProgramKemiskinan() {
|
||||||
return (
|
return (
|
||||||
@@ -25,11 +28,29 @@ function ProgramKemiskinan() {
|
|||||||
function ListProgramKemiskinan() {
|
function ListProgramKemiskinan() {
|
||||||
const programState = useProxy(programKemiskinanState)
|
const programState = useProxy(programKemiskinanState)
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [lineChart, setLineChart] = useState<any[]>([]);
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
|
setMounted(true)
|
||||||
programState.findMany.load()
|
programState.findMany.load()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (programState.findMany.data) {
|
||||||
|
const chartData = programState.findMany.data
|
||||||
|
.filter(item => item.statistik)
|
||||||
|
.map(item => ({
|
||||||
|
tahun: item.statistik?.tahun,
|
||||||
|
jumlah: Number(item.statistik?.jumlah)
|
||||||
|
}))
|
||||||
|
.sort((a, b) => (a.tahun || 0) - (b.tahun || 0)); // opsional, urutkan tahun
|
||||||
|
|
||||||
|
setLineChart(chartData);
|
||||||
|
|
||||||
|
}
|
||||||
|
}, [programState.findMany.data])
|
||||||
|
|
||||||
if (!programState.findMany.data) {
|
if (!programState.findMany.data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
@@ -71,6 +92,46 @@ function ListProgramKemiskinan() {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
{/* Chart */}
|
||||||
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} >
|
||||||
|
<Stack>
|
||||||
|
<Box >
|
||||||
|
<Title pb={10} order={3}>Grafik Berdasarkan Responden</Title>
|
||||||
|
{mounted && lineChart.length > 0 ? (<Box style={{ width: '100%', height: 'auto', }}>
|
||||||
|
<Box w={"100%"} style={{overflowX: 'auto'}}>
|
||||||
|
<LineChart
|
||||||
|
width={650}
|
||||||
|
height={300}
|
||||||
|
data={lineChart}
|
||||||
|
>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
|
<XAxis dataKey="tahun" />
|
||||||
|
<YAxis />
|
||||||
|
<Tooltip
|
||||||
|
formatter={(value: any, name: string) => [`${value} orang`, name]}
|
||||||
|
labelFormatter={(label: any) => `Tahun: ${label}`}
|
||||||
|
/>
|
||||||
|
<Legend />
|
||||||
|
<Line
|
||||||
|
type="monotone"
|
||||||
|
dataKey="jumlah"
|
||||||
|
name="Jumlah per Tahun"
|
||||||
|
stroke={colors['blue-button']}
|
||||||
|
/>
|
||||||
|
</LineChart>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Text c='dimmed'>Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,29 +15,57 @@ export default async function programKemiskinanUpdate(context: Context) {
|
|||||||
const id = context.params.id as string;
|
const id = context.params.id as string;
|
||||||
const body = context.body as FormUpdate;
|
const body = context.body as FormUpdate;
|
||||||
|
|
||||||
const program = await prisma.programKemiskinan.update({
|
try {
|
||||||
where: { id },
|
// cari ID statistik yang terkait
|
||||||
data: {
|
const existing = await prisma.programKemiskinan.findUnique({
|
||||||
nama: body.nama,
|
where: { id },
|
||||||
deskripsi: body.deskripsi,
|
include: { statistik: true },
|
||||||
ikonUrl: body.ikonUrl,
|
});
|
||||||
statistik: body.statistik
|
|
||||||
? {
|
|
||||||
create: {
|
|
||||||
tahun: Number(body.statistik.tahun),
|
|
||||||
jumlah: Number(body.statistik.jumlah),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
statistik: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
if (!existing) {
|
||||||
success: true,
|
return new Response(JSON.stringify({ success: false, message: "Program tidak ditemukan" }), { status: 404 });
|
||||||
message: "Success update program kemiskinan",
|
}
|
||||||
data: program,
|
|
||||||
};
|
// update statistik (bisa update atau create kalau belum ada)
|
||||||
|
let statistikUpdate;
|
||||||
|
|
||||||
|
if (existing.statistikId) {
|
||||||
|
statistikUpdate = await prisma.statistikKemiskinan.update({
|
||||||
|
where: { id: existing.statistikId },
|
||||||
|
data: {
|
||||||
|
tahun: Number(body.statistik?.tahun),
|
||||||
|
jumlah: Number(body.statistik?.jumlah),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
statistikUpdate = await prisma.statistikKemiskinan.create({
|
||||||
|
data: {
|
||||||
|
tahun: Number(body.statistik?.tahun),
|
||||||
|
jumlah: Number(body.statistik?.jumlah),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const program = await prisma.programKemiskinan.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
nama: body.nama,
|
||||||
|
deskripsi: body.deskripsi,
|
||||||
|
ikonUrl: body.ikonUrl,
|
||||||
|
statistik: {
|
||||||
|
connect: { id: statistikUpdate.id }, // konek ke statistik baru atau yang diperbarui
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: { statistik: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ success: true, data: program }), {
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal update:", err);
|
||||||
|
return new Response(JSON.stringify({ success: false, message: "Gagal update program" }), {
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user