PPID > Profile PPID
Desa > Profile Visi Misi Desa Keamanan > Pencegahan Kriminalitas Grid Kiri Datanya Mepet Keamanan > Laporan Publik Ekonomi > Sektor Unggulan Desa Coba tampilin 3 Aja
This commit is contained in:
@@ -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,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return Response.json({
|
||||
|
||||
@@ -26,9 +26,19 @@ export default async function lambangDesaFindById(request: Request) {
|
||||
);
|
||||
}
|
||||
|
||||
const data = await prisma.lambangDesa.findUnique({
|
||||
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(
|
||||
|
||||
@@ -20,7 +20,23 @@ export default async function maskotDesaFindById(request: Request){
|
||||
}, {status: 400})
|
||||
}
|
||||
|
||||
const data = await prisma.maskotDesa.findUnique({
|
||||
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: {
|
||||
@@ -30,6 +46,7 @@ export default async function maskotDesaFindById(request: Request){
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(!data) {
|
||||
return Response.json({
|
||||
|
||||
@@ -20,9 +20,19 @@ export default async function sejarahDesaFindById(request: Request) {
|
||||
}, {status: 400})
|
||||
}
|
||||
|
||||
const data = await prisma.sejarahDesa.findUnique({
|
||||
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({
|
||||
|
||||
@@ -20,9 +20,19 @@ export default async function visiMisiDesaFindById(request: Request) {
|
||||
}, {status: 400})
|
||||
}
|
||||
|
||||
const data = await prisma.visiMisiDesa.findUnique({
|
||||
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({
|
||||
|
||||
@@ -20,12 +20,25 @@ export default async function handler(request: Request) {
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
const data = await prisma.profilePPID.findUnique({
|
||||
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({
|
||||
|
||||
@@ -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() {
|
||||
</Box>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<Stack gap="lg" justify="center">
|
||||
{data.map((v, k) => {
|
||||
{currentData.map((v, k) => {
|
||||
return (
|
||||
<Paper p="xl" key={k}>
|
||||
<Paper p="xl" key={`${startIndex + k}`}>
|
||||
<Title order={3} fw="bold">
|
||||
{v.name}
|
||||
</Title>
|
||||
@@ -90,6 +108,18 @@ function Page() {
|
||||
</Paper>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Pagination */}
|
||||
<Center mt="xl">
|
||||
<Pagination
|
||||
total={Math.ceil(data.length / itemsPerPage)}
|
||||
value={activePage}
|
||||
onChange={setActivePage}
|
||||
size="lg"
|
||||
radius="md"
|
||||
/>
|
||||
</Center>
|
||||
|
||||
<Box style={{ width: '100%', overflowX: 'auto' }}>
|
||||
<Paper p="xl">
|
||||
<Title order={3} fw="bold" pb="md">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
</Title>
|
||||
@@ -85,9 +85,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
|
||||
</Title>
|
||||
@@ -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 ? (
|
||||
<Center><Skeleton h={400} /></Center>
|
||||
) : findFirst.data ? (
|
||||
<Paper p="xl" radius="xl" shadow="lg">
|
||||
<Paper p="xl" radius="xl" shadow="lg" bg="white">
|
||||
{findFirst.data?.linkVideo ? (
|
||||
<Box
|
||||
component="iframe"
|
||||
@@ -168,7 +169,7 @@ function Page() {
|
||||
width="100%"
|
||||
height={300}
|
||||
allowFullScreen
|
||||
style={{ borderRadius: 8 }}
|
||||
style={{ borderRadius: 8, marginBottom: 15 }}
|
||||
/>
|
||||
) : (
|
||||
<Text fz={{ base: 'xs', md: 'sm' }} c="dimmed" lh={1.4}>
|
||||
|
||||
@@ -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<string> {
|
||||
// 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");
|
||||
|
||||
return staticToken;
|
||||
}
|
||||
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
|
||||
// Function to force refresh the token (useful for manual refresh or testing)
|
||||
export async function refreshAuthToken(): Promise<string> {
|
||||
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;
|
||||
}
|
||||
6
x.sh
6
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user