Fix eror build

This commit is contained in:
2026-02-25 21:18:26 +08:00
parent 42dcbcfb22
commit 1e7acac193
13 changed files with 110 additions and 58 deletions

View File

@@ -26,7 +26,24 @@ export async function seedBerita() {
console.log("🔄 Seeding Berita..."); console.log("🔄 Seeding Berita...");
// Build a map of valid kategori IDs
const validKategoriIds = new Set<string>();
const kategoriList = await prisma.kategoriBerita.findMany({
select: { id: true, name: true },
});
kategoriList.forEach((k) => validKategoriIds.add(k.id));
console.log(`📋 Found ${validKategoriIds.size} valid kategori IDs in database`);
for (const b of beritaJson) { for (const b of beritaJson) {
// Validate kategoriBeritaId exists
if (!b.kategoriBeritaId || !validKategoriIds.has(b.kategoriBeritaId)) {
console.warn(
`⚠️ Skipping berita "${b.judul}": Invalid kategoriBeritaId "${b.kategoriBeritaId}"`,
);
continue;
}
let imageId: string | null = null; let imageId: string | null = null;
if (b.imageName) { if (b.imageName) {
@@ -44,26 +61,32 @@ export async function seedBerita() {
} }
} }
await prisma.berita.upsert({ try {
where: { id: b.id }, await prisma.berita.upsert({
update: { where: { id: b.id },
judul: b.judul, update: {
deskripsi: b.deskripsi, judul: b.judul,
content: b.content, deskripsi: b.deskripsi,
kategoriBeritaId: b.kategoriBeritaId, content: b.content,
imageId, kategoriBeritaId: b.kategoriBeritaId,
}, imageId,
create: { },
id: b.id, create: {
judul: b.judul, id: b.id,
deskripsi: b.deskripsi, judul: b.judul,
content: b.content, deskripsi: b.deskripsi,
kategoriBeritaId: b.kategoriBeritaId, content: b.content,
imageId, kategoriBeritaId: b.kategoriBeritaId,
}, imageId,
}); },
});
console.log(`✅ Berita seeded: ${b.judul}`); console.log(`✅ Berita seeded: ${b.judul}`);
} catch (error: any) {
console.error(
`❌ Failed to seed berita "${b.judul}": ${error.message}`,
);
}
} }
console.log("🎉 Berita seed selesai"); console.log("🎉 Berita seed selesai");

View File

@@ -95,7 +95,7 @@ function Page() {
fz={{ base: 'md', md: 'lg' }} fz={{ base: 'md', md: 'lg' }}
lh={{ base: 1.4, md: 1.4 }} lh={{ base: 1.4, md: 1.4 }}
> >
{perbekel.nama || "I.B. Surya Prabhawa Manuaba, S.H., M.H."} I.B. Surya Prabhawa Manuaba, S.H., M.H.
</Text> </Text>
</Paper> </Paper>
</Stack> </Stack>

View File

@@ -1,9 +1,9 @@
'use client' 'use client'
import { authStore } from "@/store/authStore";
import { useDarkMode } from "@/state/darkModeStore";
import { themeTokens, getActiveStateStyles } from "@/utils/themeTokens";
import { DarkModeToggle } from "@/components/admin/DarkModeToggle"; import { DarkModeToggle } from "@/components/admin/DarkModeToggle";
import { useDarkMode } from "@/state/darkModeStore";
import { authStore } from "@/store/authStore";
import { themeTokens } from "@/utils/themeTokens";
import { import {
ActionIcon, ActionIcon,
AppShell, AppShell,

View File

@@ -17,7 +17,6 @@ export default async function kategoriBeritaDelete(context: Context) {
where: { where: {
kategoriBeritaId: id, kategoriBeritaId: id,
isActive: true, isActive: true,
deletedAt: null,
}, },
}); });

View File

@@ -23,10 +23,9 @@ export default async function findUnique(
// ✅ Filter by isActive and deletedAt // ✅ Filter by isActive and deletedAt
const data = await prisma.potensiDesa.findFirst({ const data = await prisma.potensiDesa.findFirst({
where: { where: {
id, id,
isActive: true, isActive: true,
deletedAt: null,
}, },
include: { include: {
image: true, image: true,

View File

@@ -17,7 +17,6 @@ export default async function kategoriPotensiDelete(context: Context) {
where: { where: {
kategoriId: id, kategoriId: id,
isActive: true, isActive: true,
deletedAt: null,
}, },
}); });

View File

@@ -1,10 +1,9 @@
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { requireAuth } from "@/lib/api-auth"; import { requireAuth } from "@/lib/api-auth";
export default async function sejarahDesaFindFirst(request: Request) { export default async function sejarahDesaFindFirst() {
// ✅ Authentication check // ✅ Authentication check
const headers = new Headers(request.url); const authResult = await requireAuth();
const authResult = await requireAuth({ headers });
if (!authResult.authenticated) { if (!authResult.authenticated) {
return authResult.response; return authResult.response;
} }
@@ -12,9 +11,8 @@ export default async function sejarahDesaFindFirst(request: Request) {
try { try {
// Get the first active record // Get the first active record
const data = await prisma.sejarahDesa.findFirst({ const data = await prisma.sejarahDesa.findFirst({
where: { where: {
isActive: true, isActive: true,
deletedAt: null
}, },
orderBy: { createdAt: 'asc' } // Get the oldest one first orderBy: { createdAt: 'asc' } // Get the oldest one first
}); });

View File

@@ -7,8 +7,8 @@ const SejarahDesa = new Elysia({
prefix: "/sejarah", prefix: "/sejarah",
tags: ["Desa/Profile"], tags: ["Desa/Profile"],
}) })
.get("/first", async (context) => { .get("/first", async () => {
const response = await sejarahDesaFindFirst(new Request(context.request)); const response = await sejarahDesaFindFirst();
return response; return response;
}) })
.get("/:id", async (context) => { .get("/:id", async (context) => {

View File

@@ -4,7 +4,7 @@ import { Context } from "elysia";
export default async function sejarahDesaUpdate(context: Context) { export default async function sejarahDesaUpdate(context: Context) {
// ✅ Authentication check // ✅ Authentication check
const authResult = await requireAuth(context); const authResult = await requireAuth();
if (!authResult.authenticated) { if (!authResult.authenticated) {
return authResult.response; return authResult.response;
} }

View File

@@ -2,7 +2,7 @@
import { useDarkMode } from '@/state/darkModeStore'; import { useDarkMode } from '@/state/darkModeStore';
import { themeTokens } from '@/utils/themeTokens'; import { themeTokens } from '@/utils/themeTokens';
import { Paper, Box, BoxProps, Divider, DividerProps } from '@mantine/core'; import { Box, BoxProps, Divider, DividerProps, Paper } from '@mantine/core';
import React from 'react'; import React from 'react';
/** /**
@@ -22,7 +22,6 @@ import React from 'react';
// ============================================================================ // ============================================================================
// Unified Card Component // Unified Card Component
* ============================================================================
interface UnifiedCardProps extends BoxProps { interface UnifiedCardProps extends BoxProps {
withBorder?: boolean; withBorder?: boolean;
@@ -63,12 +62,18 @@ export function UnifiedCard({
} }
}; };
const getShadow = () => {
if (shadow === 'none') return 'none';
return tokens.shadows[shadow];
};
return ( return (
<Paper <Paper
withBorder={withBorder} withBorder={withBorder}
bg={tokens.colors.bg.card} bg={tokens.colors.bg.card}
p={getPadding()} p={getPadding()}
radius={tokens.radius.lg} // 12-16px sesuai spec radius={tokens.radius.lg} // 12-16px sesuai spec
shadow={getShadow()}
style={{ style={{
borderColor: tokens.colors.border.default, borderColor: tokens.colors.border.default,
transition: hoverable transition: hoverable

View File

@@ -5,6 +5,8 @@ import { themeTokens, getResponsiveFz } from '@/utils/themeTokens';
import { Text, Title, Box, BoxProps } from '@mantine/core'; import { Text, Title, Box, BoxProps } from '@mantine/core';
import React from 'react'; import React from 'react';
type TextTruncate = 'end' | 'start' | boolean;
/** /**
* Unified Typography Components * Unified Typography Components
* *
@@ -73,7 +75,7 @@ export function UnifiedTitle({
const getColor = () => { const getColor = () => {
if (color === 'primary') return tokens.colors.text.primary; if (color === 'primary') return tokens.colors.text.primary;
if (color === 'secondary') return tokens.colors.text.secondary; if (color === 'secondary') return tokens.colors.text.secondary;
if (color === 'brand') return tokens.colors.brand; if (color === 'brand') return tokens.colors.text.brand;
return color; return color;
}; };
@@ -109,8 +111,14 @@ interface UnifiedTextProps {
align?: 'left' | 'center' | 'right'; align?: 'left' | 'center' | 'right';
color?: 'primary' | 'secondary' | 'tertiary' | 'muted' | 'brand' | 'link' | string; color?: 'primary' | 'secondary' | 'tertiary' | 'muted' | 'brand' | 'link' | string;
lineClamp?: number; lineClamp?: number;
truncate?: 'start' | 'end' | 'middle' | boolean; truncate?: TextTruncate;
span?: boolean; span?: boolean;
mt?: string;
mb?: string;
ml?: string;
mr?: string;
mx?: string;
my?: string;
style?: React.CSSProperties; style?: React.CSSProperties;
} }
@@ -123,6 +131,12 @@ export function UnifiedText({
lineClamp, lineClamp,
truncate, truncate,
span = false, span = false,
mt,
mb,
ml,
mr,
mx,
my,
style, style,
}: UnifiedTextProps) { }: UnifiedTextProps) {
const { isDark } = useDarkMode(); const { isDark } = useDarkMode();
@@ -163,7 +177,7 @@ export function UnifiedText({
case 'muted': case 'muted':
return tokens.colors.text.muted; return tokens.colors.text.muted;
case 'brand': case 'brand':
return tokens.colors.brand; return tokens.colors.text.brand;
case 'link': case 'link':
return tokens.colors.text.link; return tokens.colors.text.link;
default: default:
@@ -177,7 +191,7 @@ export function UnifiedText({
if (span) { if (span) {
return ( return (
<Text.Span <Text
ta={align} ta={align}
fz={typo.fz} fz={typo.fz}
fw={fw} fw={fw}
@@ -185,10 +199,16 @@ export function UnifiedText({
c={textColor} c={textColor}
lineClamp={lineClamp} lineClamp={lineClamp}
truncate={truncate} truncate={truncate}
mt={mt}
mb={mb}
ml={ml}
mr={mr}
mx={mx}
my={my}
style={style} style={style}
> >
{children} {children}
</Text.Span> </Text>
); );
} }
@@ -201,6 +221,12 @@ export function UnifiedText({
c={textColor} c={textColor}
lineClamp={lineClamp} lineClamp={lineClamp}
truncate={truncate} truncate={truncate}
mt={mt}
mb={mb}
ml={ml}
mr={mr}
mx={mx}
my={my}
style={style} style={style}
> >
{children} {children}

View File

@@ -1,11 +1,11 @@
/** /**
* Authentication helper untuk API endpoints * Authentication helper untuk API endpoints
* *
* Usage: * Usage:
* import { requireAuth } from "@/lib/api-auth"; * import { requireAuth } from "@/lib/api-auth";
* *
* export default async function myEndpoint(context: Context) { * export default async function myEndpoint() {
* const authResult = await requireAuth(context); * const authResult = await requireAuth();
* if (!authResult.authenticated) { * if (!authResult.authenticated) {
* return authResult.response; * return authResult.response;
* } * }
@@ -13,24 +13,24 @@
* } * }
*/ */
import { getSession } from "@/lib/session"; import { getSession, SessionData } from "@/lib/session";
export type AuthResult = export type AuthResult =
| { authenticated: true; user: any } | { authenticated: true; user: NonNullable<SessionData["user"]> }
| { authenticated: false; response: Response }; | { authenticated: false; response: Response };
export async function requireAuth(context: any): Promise<AuthResult> { export async function requireAuth(): Promise<AuthResult> {
try { try {
// Cek session dari cookies // Cek session dari cookies
const session = await getSession(); const session = await getSession();
if (!session || !session.user) { if (!session || !session.user) {
return { return {
authenticated: false, authenticated: false,
response: new Response(JSON.stringify({ response: new Response(JSON.stringify({
success: false, success: false,
message: "Unauthorized - Silakan login terlebih dahulu" message: "Unauthorized - Silakan login terlebih dahulu"
}), { }), {
status: 401, status: 401,
headers: { 'Content-Type': 'application/json' } headers: { 'Content-Type': 'application/json' }
}) })
@@ -44,7 +44,7 @@ export async function requireAuth(context: any): Promise<AuthResult> {
response: new Response(JSON.stringify({ response: new Response(JSON.stringify({
success: false, success: false,
message: "Akun Anda tidak aktif. Hubungi administrator." message: "Akun Anda tidak aktif. Hubungi administrator."
}), { }), {
status: 403, status: 403,
headers: { 'Content-Type': 'application/json' } headers: { 'Content-Type': 'application/json' }
}) })
@@ -55,14 +55,13 @@ export async function requireAuth(context: any): Promise<AuthResult> {
authenticated: true, authenticated: true,
user: session.user user: session.user
}; };
} catch (error) { } catch {
console.error("Auth error:", error);
return { return {
authenticated: false, authenticated: false,
response: new Response(JSON.stringify({ response: new Response(JSON.stringify({
success: false, success: false,
message: "Authentication error" message: "Authentication error"
}), { }), {
status: 500, status: 500,
headers: { 'Content-Type': 'application/json' } headers: { 'Content-Type': 'application/json' }
}) })
@@ -74,11 +73,11 @@ export async function requireAuth(context: any): Promise<AuthResult> {
* Optional auth - tidak error jika tidak authenticated * Optional auth - tidak error jika tidak authenticated
* Berguna untuk endpoint yang bisa diakses public atau private * Berguna untuk endpoint yang bisa diakses public atau private
*/ */
export async function optionalAuth(context: any): Promise<any> { export async function optionalAuth(): Promise<NonNullable<SessionData["user"]> | null> {
try { try {
const session = await getSession(); const session = await getSession();
return session?.user || null; return session?.user || null;
} catch (error) { } catch {
return null; return null;
} }
} }

View File

@@ -223,6 +223,10 @@ export const themeTokens = (isDark: boolean = false): ThemeTokens => {
hoverSoft: 'rgba(25, 113, 194, 0.03)', hoverSoft: 'rgba(25, 113, 194, 0.03)',
hoverMedium: 'rgba(25, 113, 194, 0.05)', hoverMedium: 'rgba(25, 113, 194, 0.05)',
activeAccent: 'rgba(25, 113, 194, 0.1)', activeAccent: 'rgba(25, 113, 194, 0.1)',
success: '#22c55e',
warning: '#facc15',
error: '#ef4444',
info: '#38bdf8',
}; };
const current = isDark ? darkColors : lightColors; const current = isDark ? darkColors : lightColors;