From cc49a1fcd3b7ea3ed3f60624f7cd8fe7d78bb0a6 Mon Sep 17 00:00:00 2001 From: amal Date: Thu, 9 Apr 2026 17:30:55 +0800 Subject: [PATCH] upd: connected api Deskripsi: - tambah desa No Issues --- src/frontend/config/api.ts | 1 + .../routes/apps.$appId.villages.index.tsx | 214 ++++++++++++++++-- 2 files changed, 193 insertions(+), 22 deletions(-) diff --git a/src/frontend/config/api.ts b/src/frontend/config/api.ts index bfcaf88..c5ee969 100644 --- a/src/frontend/config/api.ts +++ b/src/frontend/config/api.ts @@ -17,4 +17,5 @@ export const API_URLS = { getDailyActivity: () => `${API_BASE_URL}/api/monitoring/daily-activity`, getComparisonActivity: () => `${API_BASE_URL}/api/monitoring/comparison-activity`, postVersionUpdate: () => `${API_BASE_URL}/api/monitoring/version-update`, + createVillages: () => `${API_BASE_URL}/api/monitoring/create-villages`, } diff --git a/src/frontend/routes/apps.$appId.villages.index.tsx b/src/frontend/routes/apps.$appId.villages.index.tsx index de7e153..df551a3 100644 --- a/src/frontend/routes/apps.$appId.villages.index.tsx +++ b/src/frontend/routes/apps.$appId.villages.index.tsx @@ -1,41 +1,45 @@ -import { useState } from 'react' -import useSWR from 'swr' import { + ActionIcon, Badge, + Box, + Button, + Card, Container, + Divider, Group, + Modal, + Pagination, + Paper, + SegmentedControl, + Select, + SimpleGrid, Stack, Text, - Title, - Paper, - Button, - ActionIcon, + Textarea, TextInput, - Tooltip, - SimpleGrid, - Avatar, - Box, - SegmentedControl, - Card, - Divider, ThemeIcon, - Pagination, + Title, + Tooltip } from '@mantine/core' +import { notifications } from '@mantine/notifications' import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' +import { useState } from 'react' +import { useDisclosure } from '@mantine/hooks' import { - TbPlus, - TbSearch, + TbArrowRight, TbBuildingCommunity, + TbCalendar, + TbChevronRight, + TbHome2, TbLayoutGrid, TbList, TbMapPin, - TbCalendar, + TbPlus, + TbSearch, TbUser, - TbHome2, - TbArrowRight, - TbChevronRight, TbX, } from 'react-icons/tb' +import useSWR from 'swr' import { API_URLS } from '../config/api' export const Route = createFileRoute('/apps/$appId/villages/')({ @@ -214,14 +218,27 @@ function AppVillagesIndexPage() { const { appId } = useParams({ from: '/apps/$appId' }) const navigate = useNavigate() const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') + const [createModalOpened, { open: openCreateModal, close: closeCreateModal }] = useDisclosure(false) const [page, setPage] = useState(1) const [search, setSearch] = useState('') const [searchQuery, setSearchQuery] = useState('') + // Form State + const [isSubmitting, setIsSubmitting] = useState(false) + const [form, setForm] = useState({ + name: '', + desc: '', + username: '', + phone: '', + nik: '', + email: '', + gender: '' + }) + const isDesaPlus = appId === 'desa-plus' const apiUrl = isDesaPlus ? API_URLS.getVillages(page, searchQuery) : null - const { data: response, error, isLoading } = useSWR(apiUrl, fetcher) + const { data: response, error, isLoading, mutate } = useSWR(apiUrl, fetcher) const villages: APIVillage[] = response?.data || [] const handleVillageClick = (villageId: string) => { @@ -242,6 +259,64 @@ function AppVillagesIndexPage() { setPage(1) } + const handleCreateVillage = async () => { + const requiredFields = ['name', 'desc', 'username', 'phone', 'nik', 'email', 'gender'] as const + const isFormValid = requiredFields.every(field => !!form[field]) + + if (!isFormValid) { + notifications.show({ + title: 'Validation Error', + message: 'All fields are required to register a new village.', + color: 'red' + }) + return + } + + setIsSubmitting(true) + try { + const res = await fetch(API_URLS.createVillages(), { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(form) + }) + + if (res.ok) { + notifications.show({ + title: 'Success', + message: 'Village has been successfully registered.', + color: 'teal' + }) + mutate() // Refresh list + closeCreateModal() + setForm({ + name: '', + desc: '', + username: '', + phone: '', + nik: '', + email: '', + gender: '' + }) + } else { + notifications.show({ + title: 'Error', + message: 'Failed to create village. Please try again.', + color: 'red' + }) + } + } catch (e) { + notifications.show({ + title: 'Network Error', + message: 'Unable to reach API server.', + color: 'red' + }) + } finally { + setIsSubmitting(false) + } + } + if (!isDesaPlus) { return ( @@ -256,6 +331,100 @@ function AppVillagesIndexPage() { return ( + Register New Village} + radius="xl" + size="lg" + > + + + + Village Data + + + setForm(prev => ({ ...prev, name: e.currentTarget.value }))} + /> +