From f446aec7344e82bcb13d7c2956d2ec612e6e0d0a Mon Sep 17 00:00:00 2001 From: amal Date: Thu, 16 Apr 2026 09:52:17 +0800 Subject: [PATCH] upd: role akses --- src/frontend/components/DashboardCharts.tsx | 39 ++++---- src/frontend/routes/apps.$appId.index.tsx | 6 +- .../apps.$appId.villages.$villageId.tsx | 5 + src/frontend/routes/users.tsx | 93 ++++++++++--------- 4 files changed, 80 insertions(+), 63 deletions(-) diff --git a/src/frontend/components/DashboardCharts.tsx b/src/frontend/components/DashboardCharts.tsx index bd67156..6322ac2 100644 --- a/src/frontend/components/DashboardCharts.tsx +++ b/src/frontend/components/DashboardCharts.tsx @@ -1,15 +1,15 @@ -import { - Paper, - Stack, - Text, - Group, - ThemeIcon, - Box, +import { BarChart, LineChart } from '@mantine/charts' +import { Badge, + Box, + Group, + Paper, + Stack, + Text, + ThemeIcon, useMantineTheme } from '@mantine/core' -import { LineChart, BarChart } from '@mantine/charts' -import { TbTimeline, TbChartBar, TbArrowUpRight } from 'react-icons/tb' +import { TbArrowUpRight, TbChartBar, TbTimeline } from 'react-icons/tb' interface ChartProps { data?: any[] @@ -18,7 +18,7 @@ interface ChartProps { export function VillageActivityLineChart({ data = [], isLoading }: ChartProps) { const theme = useMantineTheme() - + return ( @@ -32,9 +32,14 @@ export function VillageActivityLineChart({ data = [], isLoading }: ChartProps) { Trend over the last 7 days - }> - {isLoading ? '...' : 'Live'} - + { + isLoading && ( + }> + ... + + ) + } + @@ -91,12 +96,12 @@ export function VillageComparisonBarChart({ data = [], isLoading }: ChartProps) tickLine="none" gridAxis="y" barProps={{ - radius: [8, 8, 4, 4], + radius: [8, 8, 4, 4], }} styles={{ - bar: { - fill: 'url(#barGradient)', - } + bar: { + fill: 'url(#barGradient)', + } }} > diff --git a/src/frontend/routes/apps.$appId.index.tsx b/src/frontend/routes/apps.$appId.index.tsx index 9ae228e..6e19ea0 100644 --- a/src/frontend/routes/apps.$appId.index.tsx +++ b/src/frontend/routes/apps.$appId.index.tsx @@ -2,6 +2,7 @@ import { useQuery } from '@tanstack/react-query' import { VillageActivityLineChart, VillageComparisonBarChart } from '@/frontend/components/DashboardCharts' import { ErrorDataTable } from '@/frontend/components/ErrorDataTable' import { SummaryCard } from '@/frontend/components/SummaryCard' +import { useSession } from '@/frontend/hooks/useAuth' import { Badge, Button, @@ -39,6 +40,8 @@ function AppOverviewPage() { const navigate = useNavigate() const isDesaPlus = appId === 'desa-plus' const [versionModalOpened, { open: openVersionModal, close: closeVersionModal }] = useDisclosure(false) + const { data: session } = useSession() + const isDeveloper = session?.user?.role === 'DEVELOPER' // Form State const [latestVersion, setLatestVersion] = useState('') @@ -177,7 +180,7 @@ function AppOverviewPage() { value={gridLoading ? '...' : (grid?.version?.mobile_latest_version || 'N/A')} icon={TbVersions} color="brand-blue" - onClick={openVersionModal} + onClick={isDeveloper ? openVersionModal : undefined} > @@ -220,6 +223,7 @@ function AppOverviewPage() { icon={TbAlertTriangle} color="red" isError={true} + onClick={() => navigate({ to: `/apps/${appId}/errors` })} /> diff --git a/src/frontend/routes/apps.$appId.villages.$villageId.tsx b/src/frontend/routes/apps.$appId.villages.$villageId.tsx index 70c1a0c..0304a1c 100644 --- a/src/frontend/routes/apps.$appId.villages.$villageId.tsx +++ b/src/frontend/routes/apps.$appId.villages.$villageId.tsx @@ -37,6 +37,7 @@ import { } from 'react-icons/tb' import useSWR from 'swr' import { API_URLS } from '../config/api' +import { useSession } from '../hooks/useAuth' const fetcher = (url: string) => fetch(url).then((res) => res.json()) @@ -149,6 +150,9 @@ function VillageDetailPage() { const { appId, villageId } = useParams({ from: '/apps/$appId/villages/$villageId' }) const navigate = useNavigate() + const { data: session } = useSession() + const isDeveloper = session?.user?.role === 'DEVELOPER' + const { data: infoRes, isLoading: infoLoading, mutate } = useSWR(API_URLS.infoVillages(villageId), fetcher) const { data: gridRes, isLoading: gridLoading } = useSWR(API_URLS.gridVillages(villageId), fetcher) @@ -323,6 +327,7 @@ function VillageDetailPage() { onClick={openConfirmModal} radius="md" loading={isUpdating} + disabled={!isDeveloper} > {village.isActive ? 'Deactivate' : 'Active'} diff --git a/src/frontend/routes/users.tsx b/src/frontend/routes/users.tsx index 2ebb47b..27c7289 100644 --- a/src/frontend/routes/users.tsx +++ b/src/frontend/routes/users.tsx @@ -1,48 +1,47 @@ +import { DashboardLayout } from '@/frontend/components/DashboardLayout' +import { StatsCard } from '@/frontend/components/StatsCard' import { ActionIcon, + Avatar, Badge, Button, Card, Container, + Divider, Group, + List, + Modal, + Pagination, + Paper, + PasswordInput, + Select, + SimpleGrid, Stack, Table, + Tabs, Text, TextInput, - Title, - Paper, - Tabs, - Avatar, - SimpleGrid, ThemeIcon, - List, - Divider, - Pagination, - Modal, - Select, - PasswordInput, + Title, } from '@mantine/core' -import { createFileRoute } from '@tanstack/react-router' -import { useState, useEffect } from 'react' import { useDisclosure } from '@mantine/hooks' import { notifications } from '@mantine/notifications' -import { - TbPlus, - TbSearch, - TbPencil, - TbTrash, - TbUserCheck, - TbShieldCheck, +import { createFileRoute } from '@tanstack/react-router' +import { useEffect, useState } from 'react' +import { TbAccessPoint, TbCircleCheck, TbCircleX, - TbClock, - TbApps, + TbPencil, + TbPlus, + TbSearch, + TbShieldCheck, + TbTrash, + TbUserCheck } from 'react-icons/tb' -import { DashboardLayout } from '@/frontend/components/DashboardLayout' -import { StatsCard } from '@/frontend/components/StatsCard' import useSWR from 'swr' import { API_URLS } from '../config/api' +import { useSession } from '../hooks/useAuth' export const Route = createFileRoute('/users')({ component: UsersPage, @@ -59,13 +58,13 @@ const getRoleColor = (role: string) => { } const roles = [ - { - name: 'DEVELOPER', + { + name: 'DEVELOPER', color: 'red', permissions: ['Full Access', 'Error Feedback', 'Error Management', 'App Version Management', 'User Management'] }, - { - name: 'ADMIN', + { + name: 'ADMIN', color: 'orange', permissions: ['View All Apps', 'View Logs', 'Report Errors'] }, @@ -75,6 +74,8 @@ function UsersPage() { const [search, setSearch] = useState('') const [debouncedSearch, setDebouncedSearch] = useState('') const [page, setPage] = useState(1) + const { data: session } = useSession() + const isDeveloper = session?.user?.role === 'DEVELOPER' useEffect(() => { const timer = setTimeout(() => setDebouncedSearch(search), 300) @@ -244,15 +245,17 @@ function UsersPage() { setPage(1) }} /> - + {isDeveloper && ( + + )} @@ -302,12 +305,12 @@ function UsersPage() { - handleOpenEdit(user)}> - - - handleOpenDelete(user)}> - - + handleOpenEdit(user)}> + + + handleOpenDelete(user)}> + + @@ -340,14 +343,14 @@ function UsersPage() { - + {role.name.replace('_', ' ')} Core role for secure app management. - + Key Permissions