From 8c50768c98df16dee18003675bd360d82d1ef489 Mon Sep 17 00:00:00 2001 From: amal Date: Mon, 13 Apr 2026 11:00:40 +0800 Subject: [PATCH 1/6] upd: tampilan mode dark and light'; --- src/frontend/routes/apps.$appId.logs.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/frontend/routes/apps.$appId.logs.tsx b/src/frontend/routes/apps.$appId.logs.tsx index ce4bdd8..9a21097 100644 --- a/src/frontend/routes/apps.$appId.logs.tsx +++ b/src/frontend/routes/apps.$appId.logs.tsx @@ -172,10 +172,10 @@ function AppLogsPage() { > - Timestamp + Timestamp User & Village - Action - Description + Action + Description @@ -187,7 +187,7 @@ function AppLogsPage() { - + {log.createdAt.split(' ').slice(1).join(' ')} @@ -216,7 +216,10 @@ function AppLogsPage() { color={getActionColor(log.action)} radius="sm" size="xs" - styles={{ root: { fontWeight: 800 } }} + styles={{ + root: { fontWeight: 800 }, + label: { textOverflow: 'clip', overflow: 'visible' } + }} > {log.action} From 315ecc565ef07efe90181a81b58a6488978eb263 Mon Sep 17 00:00:00 2001 From: amal Date: Mon, 13 Apr 2026 11:21:25 +0800 Subject: [PATCH 2/6] upd: api monitoring Deskripsi : - api deactivate or active desa - api edit desa No Issues --- src/frontend/config/api.ts | 2 + .../routes/apps.$appId.users.index.tsx | 20 +- .../apps.$appId.villages.$villageId.tsx | 189 +++++++++++++++++- 3 files changed, 192 insertions(+), 19 deletions(-) diff --git a/src/frontend/config/api.ts b/src/frontend/config/api.ts index 5489f44..d5c8c4e 100644 --- a/src/frontend/config/api.ts +++ b/src/frontend/config/api.ts @@ -23,4 +23,6 @@ export const API_URLS = { listGroup: (id: string) => `${API_BASE_URL}/api/monitoring/list-group-villages?id=${id}`, listPosition: (id: string) => `${API_BASE_URL}/api/monitoring/list-position-villages?id=${id}`, editUser: () => `${API_BASE_URL}/api/monitoring/edit-user`, + updateStatusVillages: () => `${API_BASE_URL}/api/monitoring/update-status-villages`, + editVillages: () => `${API_BASE_URL}/api/monitoring/edit-villages`, } diff --git a/src/frontend/routes/apps.$appId.users.index.tsx b/src/frontend/routes/apps.$appId.users.index.tsx index 76bf9a2..6863d14 100644 --- a/src/frontend/routes/apps.$appId.users.index.tsx +++ b/src/frontend/routes/apps.$appId.users.index.tsx @@ -639,8 +639,8 @@ function UsersIndexPage() { User & IDContact DetailOrganization - Role + RoleStatus - Actions {users.map((user) => ( - + {handleEditOpen(user)}}> - - handleEditOpen(user)} - size="md" - radius="md" - > - - - ))} diff --git a/src/frontend/routes/apps.$appId.villages.$villageId.tsx b/src/frontend/routes/apps.$appId.villages.$villageId.tsx index 4bb7f12..79ff703 100644 --- a/src/frontend/routes/apps.$appId.villages.$villageId.tsx +++ b/src/frontend/routes/apps.$appId.villages.$villageId.tsx @@ -4,14 +4,19 @@ import { Button, Card, Group, + Modal, Paper, SegmentedControl, SimpleGrid, Stack, Text, + Textarea, + TextInput, ThemeIcon, Title } from '@mantine/core' +import { useDisclosure } from '@mantine/hooks' +import { notifications } from '@mantine/notifications' import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' import { useState } from 'react' import { @@ -144,12 +149,120 @@ function VillageDetailPage() { const { appId, villageId } = useParams({ from: '/apps/$appId/villages/$villageId' }) const navigate = useNavigate() - const { data: infoRes, isLoading: infoLoading } = useSWR(API_URLS.infoVillages(villageId), fetcher) + const { data: infoRes, isLoading: infoLoading, mutate } = useSWR(API_URLS.infoVillages(villageId), fetcher) const { data: gridRes, isLoading: gridLoading } = useSWR(API_URLS.gridVillages(villageId), fetcher) + const [confirmModalOpened, { open: openConfirmModal, close: closeConfirmModal }] = useDisclosure(false) + const [editModalOpened, { open: openEditModal, close: closeEditModal }] = useDisclosure(false) + const [isUpdating, setIsUpdating] = useState(false) + const [isEditing, setIsEditing] = useState(false) + const [editForm, setEditForm] = useState({ name: '', desc: '' }) + const village = infoRes?.data const stats = gridRes?.data + const openEdit = () => { + setEditForm({ + name: village?.name || '', + desc: village?.desc || '' + }) + openEditModal() + } + + const handleEditVillage = async () => { + if (!village) return + + if (!editForm.name.trim() || !editForm.desc.trim()) { + notifications.show({ + title: 'Validation Error', + message: 'All fields are required.', + color: 'red' + }) + return + } + + setIsEditing(true) + try { + const res = await fetch(API_URLS.editVillages(), { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + id: village.id, + name: editForm.name, + desc: editForm.desc + }) + }) + + if (res.ok) { + notifications.show({ + title: 'Success', + message: 'Village data has been updated successfully.', + color: 'teal' + }) + mutate() + closeEditModal() + } else { + notifications.show({ + title: 'Error', + message: 'Failed to update village data.', + color: 'red' + }) + } + } catch (error) { + notifications.show({ + title: 'Error', + message: 'A network error occurred.', + color: 'red' + }) + } finally { + setIsEditing(false) + } + } + + const handleConfirmToggle = async () => { + if (!village) return + + setIsUpdating(true) + try { + const res = await fetch(API_URLS.updateStatusVillages(), { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + id: village.id, + active: !village.isActive + }) + }) + + if (res.ok) { + notifications.show({ + title: 'Success', + message: `Village status has been ${!village.isActive ? 'activated' : 'deactivated'}.`, + color: 'teal' + }) + mutate() + closeConfirmModal() + } else { + notifications.show({ + title: 'Error', + message: 'Failed to update village status.', + color: 'red' + }) + } + } catch (error) { + notifications.show({ + title: 'Error', + message: 'A network error occurred.', + color: 'red' + }) + } finally { + setIsUpdating(false) + } + } + const goBack = () => navigate({ to: '/apps/$appId/villages', params: { appId } }) if (infoLoading || gridLoading) { @@ -195,8 +308,9 @@ function VillageDetailPage() { variant="filled" color={village.isActive ? 'red' : 'green'} leftSection={village.isActive ? : } - onClick={() => alert(`Toggle status for ${village.name}`)} + onClick={openConfirmModal} radius="md" + loading={isUpdating} > {village.isActive ? 'Deactivate' : 'Active'} @@ -204,7 +318,7 @@ function VillageDetailPage() { variant="light" color="blue" leftSection={} - onClick={() => alert(`Edit settings for ${village.name}`)} + onClick={openEdit} radius="md" > Edit @@ -347,6 +461,75 @@ function VillageDetailPage() { + {/* ── Confirmation Modal ── */} + Confirm Status Change} + radius="xl" + centered + > + + + Are you sure you want to {village.isActive ? 'deactivate' : 'activate'} village {village.name}? + + + + + + + + + {/* ── Edit Village Modal ── */} + Edit Village Details} + radius="xl" + size="md" + > + + setEditForm(prev => ({ ...prev, name: e.currentTarget.value }))} + /> +