diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/find-by-id.ts index ad95798b..9e4a44c3 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/find-by-id.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/find-by-id.ts @@ -20,12 +20,25 @@ export default async function profilePerbekelFindById(request: Request) { }, { status: 400 }); } - const data = await prisma.profilPerbekel.findUnique({ + let data; + + // Special handling for 'edit' - get the first/only record + if (id === 'edit') { + data = await prisma.profilPerbekel.findFirst({ + where: { isActive: true }, + include: { + image: true, + }, + orderBy: { createdAt: 'asc' } // Get the oldest one first + }); + } else { + data = await prisma.profilPerbekel.findUnique({ where: { id }, include: { - image: true, + image: true, } - }); + }); + } if (!data) { return Response.json({ diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/find-by-id.ts index 3e5d68f9..ce71ed51 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/find-by-id.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/find-by-id.ts @@ -26,9 +26,19 @@ export default async function lambangDesaFindById(request: Request) { ); } - const data = await prisma.lambangDesa.findUnique({ - where: { id }, - }); + let data; + + // Special handling for 'edit' - get the first/only record + if (id === 'edit') { + data = await prisma.lambangDesa.findFirst({ + where: { isActive: true }, + orderBy: { createdAt: 'asc' } // Get the oldest one first + }); + } else { + data = await prisma.lambangDesa.findUnique({ + where: { id }, + }); + } if (!data) { return Response.json( diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/find-by-id.ts index e16ddd7d..140ea55c 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/find-by-id.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/find-by-id.ts @@ -4,7 +4,7 @@ export default async function maskotDesaFindById(request: Request){ const url = new URL(request.url); const pathSegments = url.pathname.split('/'); const id = pathSegments[pathSegments.length - 1]; - + if (!id) { return Response.json({ success: false, @@ -20,16 +20,33 @@ export default async function maskotDesaFindById(request: Request){ }, {status: 400}) } - const data = await prisma.maskotDesa.findUnique({ - where: { id }, - include: { - images: { - include: { - image: true, + let data; + + // Special handling for 'edit' - get the first/only record + if (id === 'edit') { + data = await prisma.maskotDesa.findFirst({ + where: { isActive: true }, + include: { + images: { + include: { + image: true, + } + } + }, + orderBy: { createdAt: 'asc' } // Get the oldest one first + }); + } else { + data = await prisma.maskotDesa.findUnique({ + where: { id }, + include: { + images: { + include: { + image: true, + } } } - } - }) + }) + } if(!data) { return Response.json({ diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-by-id.ts index cc8b5890..57b04395 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-by-id.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-by-id.ts @@ -4,7 +4,7 @@ export default async function sejarahDesaFindById(request: Request) { const url = new URL(request.url); const pathSegments = url.pathname.split('/'); const id = pathSegments[pathSegments.length - 1]; - + if (!id) { return Response.json({ success: false, @@ -20,9 +20,19 @@ export default async function sejarahDesaFindById(request: Request) { }, {status: 400}) } - const data = await prisma.sejarahDesa.findUnique({ - where: { id }, - }) + let data; + + // Special handling for 'edit' - get the first/only record + if (id === 'edit') { + data = await prisma.sejarahDesa.findFirst({ + where: { isActive: true }, + orderBy: { createdAt: 'asc' } // Get the oldest one first + }); + } else { + data = await prisma.sejarahDesa.findUnique({ + where: { id }, + }) + } if (!data) { return Response.json({ diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/find-by-id.ts index ff0f4c00..e71ebafc 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/find-by-id.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/find-by-id.ts @@ -4,7 +4,7 @@ export default async function visiMisiDesaFindById(request: Request) { const url = new URL(request.url); const pathSegments = url.pathname.split('/'); const id = pathSegments[pathSegments.length - 1]; - + if (!id) { return Response.json({ success: false, @@ -20,9 +20,19 @@ export default async function visiMisiDesaFindById(request: Request) { }, {status: 400}) } - const data = await prisma.visiMisiDesa.findUnique({ - where: { id }, - }) + let data; + + // Special handling for 'edit' - get the first/only record + if (id === 'edit') { + data = await prisma.visiMisiDesa.findFirst({ + where: { isActive: true }, + orderBy: { createdAt: 'asc' } // Get the oldest one first + }); + } else { + data = await prisma.visiMisiDesa.findUnique({ + where: { id }, + }) + } if (!data) { return Response.json({ diff --git a/src/app/api/[[...slugs]]/_lib/ppid/profile_ppid/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/ppid/profile_ppid/find-by-id.ts index 468178cb..aa266751 100644 --- a/src/app/api/[[...slugs]]/_lib/ppid/profile_ppid/find-by-id.ts +++ b/src/app/api/[[...slugs]]/_lib/ppid/profile_ppid/find-by-id.ts @@ -20,12 +20,25 @@ export default async function handler(request: Request) { }, { status: 400 }); } - const data = await prisma.profilePPID.findUnique({ - where: { id }, - include: { - image: true, - } - }); + let data; + + // Special handling for 'edit' - get the first/only record + if (id === 'edit') { + data = await prisma.profilePPID.findFirst({ + where: { isActive: true }, + include: { + image: true, + }, + orderBy: { createdAt: 'asc' } // Get the oldest one first + }); + } else { + data = await prisma.profilePPID.findUnique({ + where: { id }, + include: { + image: true, + } + }); + } if (!data) { return Response.json({ diff --git a/src/app/darmasaba/(pages)/ekonomi/sektor-unggulan-desa/page.tsx b/src/app/darmasaba/(pages)/ekonomi/sektor-unggulan-desa/page.tsx index 6ae9be12..5e044687 100644 --- a/src/app/darmasaba/(pages)/ekonomi/sektor-unggulan-desa/page.tsx +++ b/src/app/darmasaba/(pages)/ekonomi/sektor-unggulan-desa/page.tsx @@ -1,7 +1,16 @@ 'use client' import colors from '@/con/colors'; -import { Stack, Box, Text, Paper, Skeleton, Center, Title } from '@mantine/core'; -import React from 'react'; +import { + Stack, + Box, + Text, + Paper, + Skeleton, + Center, + Title, + Pagination +} from '@mantine/core'; +import React, { useState } from 'react'; import BackButton from '../../desa/layanan/_com/BackButto'; import { BarChart } from '@mantine/charts'; import { useProxy } from 'valtio/utils'; @@ -13,11 +22,15 @@ function Page() { const { data, - loading, + loading } = state.findMany + const [activePage, setActivePage] = useState(1); + const itemsPerPage = 3; + useShallowEffect(() => { - state.findMany.load() + // Muat semua data tanpa batasan jumlah per halaman + state.findMany.load() // Ambil banyak data sekaligus }, []) if (loading || !data) { @@ -44,6 +57,11 @@ function Page() { ); } + // Filter data untuk halaman saat ini + const startIndex = (activePage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + const currentData = data.slice(startIndex, endIndex); + const chartData = data .filter(item => item?.name && typeof item.value === 'number') .map((item) => ({ @@ -74,9 +92,9 @@ function Page() { - {data.map((v, k) => { + {currentData.map((v, k) => { return ( - + {v.name} @@ -90,6 +108,18 @@ function Page() { ); })} + + {/* Pagination */} +
+ +
+ diff --git a/src/app/darmasaba/(pages)/keamanan/laporan-publik/page.tsx b/src/app/darmasaba/(pages)/keamanan/laporan-publik/page.tsx index 05cce9e2..1342ecfc 100644 --- a/src/app/darmasaba/(pages)/keamanan/laporan-publik/page.tsx +++ b/src/app/darmasaba/(pages)/keamanan/laporan-publik/page.tsx @@ -162,6 +162,9 @@ function Page() { p="lg" shadow="sm" style={{ + height: '100%', + display: 'flex', + flexDirection: 'column', '&:hover': { transform: 'translateY(-4px)', boxShadow: '0 8px 20px rgba(0,0,0,0.1)', @@ -169,7 +172,7 @@ function Page() { transition: 'transform 0.2s ease, box-shadow 0.2s ease', }} > - <Stack gap="sm"> + <Stack gap="sm" style={{ flex: 1 }}> <Text c={colors['blue-button']} lineClamp={2} @@ -190,7 +193,7 @@ function Page() { : '-'} </Text> - <Box> + <Box style={{ flex: 1 }}> <Text fw="bold" fz="sm"> Penanganan: </Text> @@ -257,6 +260,7 @@ function Page() { onClick={() => router.push(`/darmasaba/keamanan/laporan-publik/${v.id}`)} size={mobile ? 'sm' : 'md'} fullWidth + style={{ marginTop: 'auto' }} > {mobile ? 'Detail' : 'Lihat Detail Kronologi'} </Button> diff --git a/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/page.tsx b/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/page.tsx index 574009f3..94aff2a5 100644 --- a/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/page.tsx +++ b/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/page.tsx @@ -55,9 +55,9 @@ function Page() { <SimpleGrid px={{ base: 20, md: 100 }} cols={{ base: 1, md: 2 }} - spacing="xl" + spacing={{ base: 'md', md: 'xl' }} > - <Paper p="xl" radius="xl" shadow="lg" > + <Paper p="xl" radius="xl" shadow="lg" bg="white"> <Title order={2} c={colors['blue-button']} fw="bold" lh={1.2}> Program Keamanan Berjalan @@ -85,9 +85,9 @@ function Page() { - + Program Keamanan Berjalan @@ -108,6 +108,7 @@ function Page() { cursor: 'pointer', backgroundColor: colors['blue-button'], transition: 'all 0.2s ease', + marginBottom: '10px' // Add space between items }} onClick={() => router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/${item.id}`) @@ -160,7 +161,7 @@ function Page() { {findFirst.loading ? (
) : findFirst.data ? ( - + {findFirst.data?.linkVideo ? ( ) : ( diff --git a/src/lib/seafile-auth-service.ts b/src/lib/seafile-auth-service.ts index 0c9eeecd..073668c6 100644 --- a/src/lib/seafile-auth-service.ts +++ b/src/lib/seafile-auth-service.ts @@ -1,109 +1,17 @@ -import { Mutex } from 'async-mutex'; - -// Store the token and its expiration time -let authToken: string | null = null; -let tokenExpirationTime: number | null = null; - -// Mutex to prevent multiple simultaneous token refresh attempts -const mutex = new Mutex(); - -// Function to authenticate with Seafile and get a new token -async function authenticateWithSeafile(): Promise<{token: string, expirationTime: number}> { - // First, check if we have a static token as fallback - const staticToken = process.env.SEAFILE_TOKEN; - if (staticToken) { - console.log("Using static SEAFILE_TOKEN from environment variables"); - // For static tokens, we'll set a conservative expiration (e.g., 6 days) to force periodic refresh attempts - const conservativeExpiration = Date.now() + (6 * 24 * 60 * 60 * 1000); // 6 days from now - return { - token: staticToken, - expirationTime: conservativeExpiration - }; - } - - // Otherwise, use username/password to get a new token - const username = process.env.SEAFILE_USERNAME; - const password = process.env.SEAFILE_PASSWORD; - const baseUrl = process.env.SEAFILE_URL; - - if (!username || !password || !baseUrl) { - throw new Error('Missing required Seafile environment variables (either SEAFILE_TOKEN or SEAFILE_USERNAME/SEAFILE_PASSWORD/SEAFILE_URL)'); - } - - const response = await fetch(`${baseUrl}/api2/auth-token/`, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: new URLSearchParams({ - username, - password, - // Note: Seafile tokens typically last 7 days by default, but we'll refresh earlier - }).toString(), - }); - - if (!response.ok) { - throw new Error(`Authentication failed: ${response.status} ${response.statusText}`); - } - - const data = await response.json(); - - // Calculate expiration time (set to refresh 1 hour before actual expiration) - // Seafile tokens typically last 7 days (604800 seconds), so we refresh after 6 days and 23 hours (601200 seconds) - const refreshTokenThreshold = 601200; // 6 days and 23 hours in seconds - const expirationTime = Date.now() + (refreshTokenThreshold * 1000); - - return { - token: data.token, - expirationTime - }; -} - -// Function to get a valid authentication token +// Function to get the static authentication token from environment variables export async function getValidAuthToken(): Promise { - // Check if we have a valid token that hasn't expired - if (authToken && tokenExpirationTime && Date.now() < tokenExpirationTime) { - return authToken; + const staticToken = process.env.SEAFILE_TOKEN; + + if (!staticToken) { + throw new Error('SEAFILE_TOKEN environment variable is not set'); } - // Acquire lock to prevent multiple simultaneous refresh attempts - return mutex.runExclusive(async () => { - // Double-check after acquiring the lock - if (authToken && tokenExpirationTime && Date.now() < tokenExpirationTime) { - return authToken; - } + console.log("Using static SEAFILE_TOKEN from environment variables"); - // Get a new token - const { token, expirationTime } = await authenticateWithSeafile(); - - // Update the stored token and expiration time - authToken = token; - tokenExpirationTime = expirationTime; - - console.log('New Seafile token acquired and cached'); - - return token; - }); + return staticToken; } -// Function to force refresh the token (useful for manual refresh or testing) -export async function refreshAuthToken(): Promise { - return mutex.runExclusive(async () => { - const { token, expirationTime } = await authenticateWithSeafile(); - - // Update the stored token and expiration time - authToken = token; - tokenExpirationTime = expirationTime; - - console.log('Seafile token refreshed'); - - return token; - }); -} - -// Function to check if the token is still valid +// Function to check if the token is set (always true since we're using static token) export function isTokenValid(): boolean { - return authToken !== null && - tokenExpirationTime !== null && - Date.now() < tokenExpirationTime; + return !!process.env.SEAFILE_TOKEN; } \ No newline at end of file diff --git a/x.json b/x.json index d4a9cec1..4100b49d 100644 --- a/x.json +++ b/x.json @@ -66,4 +66,4 @@ "version": 1, "salt": "" } -] +] \ No newline at end of file diff --git a/x.sh b/x.sh index 2fd84854..d4a5e429 100644 --- a/x.sh +++ b/x.sh @@ -1,8 +1,8 @@ # ambil token -# curl -X POST https://cld-dkr-makuro-seafile.wibudev.com/api2/auth-token/ \ -# -d "username=nico@bip.com" \ -# -d "password=Production_123" +curl -X POST https://cld-dkr-makuro-seafile.wibudev.com/api2/auth-token/ \ + -d "username=nico@bip.com" \ + -d "password=Production_123" # ambil list repo / library