diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonContent.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonContent.tsx
new file mode 100644
index 0000000..69e2eb4
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonContent.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { MDXRemote } from 'next-mdx-remote/rsc';
+
+interface HackathonContentProps {
+ content: string;
+}
+
+export function HackathonContent({ content }: HackathonContentProps) {
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonDetailPage.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonDetailPage.tsx
deleted file mode 100644
index c245ce9..0000000
--- a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonDetailPage.tsx
+++ /dev/null
@@ -1,224 +0,0 @@
-'use client';
-
-import React from 'react';
-import { motion } from 'framer-motion';
-import { MDXRemote } from 'next-mdx-remote/rsc';
-import { HackathonContent } from '@/content/hackathons/types';
-import {
- getDaysLeftText,
- getProgressPercent,
- formatDateRange
-} from '@/content/hackathons/utils';
-
-interface Props {
- hackathon: HackathonContent;
-}
-
-export function HackathonDetailPage({ hackathon }: Props) {
- const { metadata, content } = hackathon;
- const progressPercent = getProgressPercent(metadata);
- const daysLeftText = getDaysLeftText(metadata);
-
- return (
-
- {/* Hero Section */}
-
-
-
-
-
- {metadata.name}
-
- {metadata.theme && (
-
- {metadata.theme}
-
- )}
- {metadata.description && (
-
- {metadata.description}
-
- )}
-
-
-
-
- {/* Status Bar */}
- {(progressPercent !== null && daysLeftText && parseInt(daysLeftText) > -5) && (
-
-
-
-
-
- {metadata.status?.charAt(0).toUpperCase() + metadata.status?.slice(1)}
-
- {metadata.submissionWindow && (
-
- {formatDateRange(metadata.submissionWindow)}
-
- )}
-
- {daysLeftText && (
-
- {daysLeftText}
-
- )}
-
- {progressPercent !== null && (
-
- )}
-
-
- )}
-
-
-
- {/* Main Content */}
-
-
-
-
-
-
- {/* Sidebar */}
-
-
- {/* Quick Info */}
-
-
Quick Info
-
- {metadata.prize && (
-
- Prize Pool:
- {metadata.prize}
-
- )}
- {metadata.minTeamSize && metadata.maxTeamSize && (
-
- Team Size:
-
- {metadata.minTeamSize === metadata.maxTeamSize
- ? `${metadata.minTeamSize} person${metadata.minTeamSize > 1 ? 's' : ''}`
- : `${metadata.minTeamSize}-${metadata.maxTeamSize} people`
- }
-
-
- )}
- {metadata.submissionsCount && (
-
- Submissions:
- {metadata.submissionsCount}
-
- )}
- {metadata.partnersCount && (
-
- Partners:
- {metadata.partnersCount}
-
- )}
-
-
-
- {/* Tags */}
- {metadata.tags && metadata.tags.length > 0 && (
-
-
Tags
-
- {metadata.tags.map((tag) => (
-
- {tag}
-
- ))}
-
-
- )}
-
- {/* Requirements */}
- {metadata.requirements && metadata.requirements.length > 0 && (
-
-
Requirements
-
- {metadata.requirements.map((req) => (
-
-
-
-
{req.name}
-
- {req.description}
-
-
-
- ))}
-
-
- )}
-
- {/* Partners */}
- {metadata.partners && metadata.partners.length > 0 && (
-
- )}
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonHeader.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonHeader.tsx
new file mode 100644
index 0000000..55546a6
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonHeader.tsx
@@ -0,0 +1,36 @@
+'use client';
+
+import React from 'react';
+import { motion } from 'framer-motion';
+import { HackathonContent } from '@/content/hackathons/types';
+
+interface HackathonHeaderProps {
+ metadata: HackathonContent['metadata'];
+}
+
+export function HackathonHeader({ metadata }: HackathonHeaderProps) {
+ return (
+
+
+
+ {metadata.name}
+
+ {metadata.theme && (
+
+ {metadata.theme}
+
+ )}
+
+ {metadata.description && (
+
+ {metadata.description}
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonHero.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonHero.tsx
new file mode 100644
index 0000000..4e0b572
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonHero.tsx
@@ -0,0 +1,29 @@
+'use client';
+
+import React from 'react';
+import { motion } from 'framer-motion';
+import Image from 'next/image';
+
+interface HackathonHeroProps {
+ coverImage: string;
+ hackathonName: string;
+}
+
+export function HackathonHero({ coverImage, hackathonName }: HackathonHeroProps) {
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonPartners.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonPartners.tsx
new file mode 100644
index 0000000..9dd0cf2
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonPartners.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { HackathonContent } from '@/content/hackathons/types';
+import { BiBuilding } from 'react-icons/bi';
+
+interface HackathonPartnersProps {
+ partners: HackathonContent['metadata']['partners'];
+}
+
+export function HackathonPartners({ partners }: HackathonPartnersProps) {
+ if (!partners || partners.length === 0) {
+ return (
+
+
+
+
+
No Sponsors Yet
+
+ Sponsor information will appear here once partnerships are announced.
+
+
+ );
+ }
+
+ return (
+
+
+
+
Event Sponsors
+
+ {partners.length} sponsor{partners.length !== 1 ? 's' : ''}
+
+
+
+ Kami berterima kasih kepada sponsor-sponsor berikut yang telah mendukung hackathon ini, tanpa dukungan mereka, acara ini tidak akan mungkin terlaksana.
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonQuickInfo.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonQuickInfo.tsx
new file mode 100644
index 0000000..6b78167
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonQuickInfo.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { HackathonContent } from '@/content/hackathons/types';
+
+interface HackathonQuickInfoProps {
+ metadata: HackathonContent['metadata'];
+}
+
+export function HackathonQuickInfo({ metadata }: HackathonQuickInfoProps) {
+ return (
+
+
Quick Info
+
+ {metadata.prize && (
+
+ Prize Pool:
+ {metadata.prize}
+
+ )}
+ {metadata.minTeamSize && metadata.maxTeamSize && (
+
+ Team Size:
+
+ {metadata.minTeamSize === metadata.maxTeamSize
+ ? `${metadata.minTeamSize} person${metadata.minTeamSize > 1 ? 's' : ''}`
+ : `${metadata.minTeamSize}-${metadata.maxTeamSize} people`
+ }
+
+
+ )}
+ {metadata.submissionsCount && (
+
+ Submissions:
+ {metadata.submissionsCount}
+
+ )}
+ {metadata.partnersCount && (
+
+ Partners:
+ {metadata.partnersCount}
+
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonRequirements.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonRequirements.tsx
new file mode 100644
index 0000000..85c5a77
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonRequirements.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { HackathonContent } from '@/content/hackathons/types';
+
+interface HackathonRequirementsProps {
+ requirements: HackathonContent['metadata']['requirements'];
+}
+
+export function HackathonRequirements({ requirements }: HackathonRequirementsProps) {
+ if (!requirements || requirements.length === 0) {
+ return null;
+ }
+
+ return (
+
+
Requirements
+
+ {requirements.map((req) => (
+
+
+
+
{req.name}
+
+ {req.description}
+
+
+
+ ))}
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonSidebar.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonSidebar.tsx
new file mode 100644
index 0000000..7371539
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonSidebar.tsx
@@ -0,0 +1,36 @@
+'use client';
+
+import React from 'react';
+import { motion } from 'framer-motion';
+import { HackathonContent } from '@/content/hackathons/types';
+import { HackathonHeader } from './HackathonHeader';
+import { HackathonQuickInfo } from './HackathonQuickInfo';
+import { HackathonRequirements } from './HackathonRequirements';
+
+interface HackathonSidebarProps {
+ metadata: HackathonContent['metadata'];
+}
+
+export function HackathonSidebar({ metadata }: HackathonSidebarProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonStatusBar.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonStatusBar.tsx
new file mode 100644
index 0000000..868a3eb
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonStatusBar.tsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import { HackathonContent } from '@/content/hackathons/types';
+import { formatDateRange } from '@/content/hackathons/utils';
+
+interface HackathonStatusBarProps {
+ metadata: HackathonContent['metadata'];
+ progressPercent: number | null;
+ daysLeftText: string | null;
+}
+
+export function HackathonStatusBar({ metadata, progressPercent, daysLeftText }: HackathonStatusBarProps) {
+ // Only show if we have progress and days left, and it's not too far in the past
+ if (!(progressPercent !== null && daysLeftText && parseInt(daysLeftText) > -5)) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+ {metadata.status?.charAt(0).toUpperCase() + metadata.status?.slice(1)}
+
+ {metadata.submissionWindow && (
+
+ {formatDateRange(metadata.submissionWindow)}
+
+ )}
+
+ {daysLeftText && (
+
+ {daysLeftText}
+
+ )}
+
+ {progressPercent !== null && (
+
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonSubmissions.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonSubmissions.tsx
new file mode 100644
index 0000000..1598449
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonSubmissions.tsx
@@ -0,0 +1,107 @@
+'use client';
+
+import React from 'react';
+import { BiLinkExternal, BiLogoGithub, BiImage, BiGroup } from 'react-icons/bi';
+
+interface Submission {
+ team_name: string;
+ project_title: string;
+ description: string;
+ repo_link: string;
+ screenshot?: string;
+ file_name?: string;
+}
+
+interface HackathonSubmissionsProps {
+ submissions?: Submission[];
+ submissionsCount?: number;
+}
+
+export function HackathonSubmissions({ submissions, submissionsCount }: HackathonSubmissionsProps) {
+ if (!submissions || submissions.length === 0) {
+ return (
+
+
+
+
+
No Submissions Yet
+
+ Submissions will appear here once participants start submitting their projects.
+
+ {submissionsCount && (
+
+ Expected submissions: {submissionsCount}
+
+ )}
+
+ );
+ }
+
+ return (
+
+
+
Project Submissions
+
+ {submissions.length} project{submissions.length !== 1 ? 's' : ''}
+
+
+
+
+ {submissions.map((submission, index) => (
+
+
+ {/* Header */}
+
+ {submission.screenshot ? (
+ // eslint-disable-next-line @next/next/no-img-element
+
+ ) : (
+
+ No Screenshot Available
+
+ )}
+
+
+
+
+
+
{submission.project_title}
+
+
+
+ {submission.team_name}
+
+
+
+ {/* Description */}
+
+ {submission.description}
+
+
+ {/* Links */}
+
+
+
+
+ ))}
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonTabs.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonTabs.tsx
new file mode 100644
index 0000000..d95cc3a
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/HackathonTabs.tsx
@@ -0,0 +1,103 @@
+'use client';
+
+import React, { useState } from 'react';
+import { motion } from 'framer-motion';
+import { HackathonContent } from '@/content/hackathons/types';
+import { HackathonPartners } from './HackathonPartners';
+import { HackathonSubmissions } from './HackathonSubmissions';
+import { BiGroup, BiFile, BiInfoCircle } from 'react-icons/bi';
+
+interface Submission {
+ team_name: string;
+ project_title: string;
+ description: string;
+ repo_link: string;
+ screenshot?: string;
+ file_name?: string;
+}
+
+interface HackathonTabsProps {
+ metadata: HackathonContent['metadata'];
+ submissions?: Submission[];
+ contentElement: React.ReactNode;
+}
+
+export function HackathonTabs({ metadata, submissions, contentElement }: HackathonTabsProps) {
+ const [activeTab, setActiveTab] = useState('content');
+
+ const tabs = [
+ {
+ id: 'content',
+ label: 'Details',
+ icon: BiInfoCircle,
+ count: 0 // No count for content tab
+ },
+ {
+ id: 'sponsors',
+ label: 'Sponsors',
+ icon: BiGroup,
+ count: metadata.partners?.length || 0
+ },
+ {
+ id: 'submissions',
+ label: 'Submissions',
+ icon: BiFile,
+ count: submissions?.length || metadata.submissionsCount || 0
+ }
+ ];
+
+ return (
+
+ {/* Tab Navigation */}
+
+
+ {tabs.map((tab) => {
+ const Icon = tab.icon;
+ return (
+ setActiveTab(tab.id)}
+ className={`relative py-4 px-1 cursor-pointer font-medium text-sm flex items-center gap-2 transition-colors`}
+ >
+
+ {tab.label}
+ {tab.count > 0 && (
+
+ {tab.count}
+
+ )}
+ {activeTab === tab.id && (
+
+ )}
+
+ );
+ })}
+
+
+
+ {/* Tab Content */}
+
+
+ {activeTab === 'content' && contentElement}
+ {activeTab === 'sponsors' && (
+
+ )}
+ {activeTab === 'submissions' && (
+
+ )}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/_components/ServerHackathonContent.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/_components/ServerHackathonContent.tsx
new file mode 100644
index 0000000..d41b62b
--- /dev/null
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/_components/ServerHackathonContent.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { MDXRemote } from 'next-mdx-remote/rsc';
+
+interface ServerHackathonContentProps {
+ content: string;
+}
+
+export async function ServerHackathonContent({ content }: ServerHackathonContentProps) {
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/[slug]/page.tsx b/apps/landing/src/app/(public)/hackathon/[slug]/page.tsx
index 7035b42..12d8b9e 100644
--- a/apps/landing/src/app/(public)/hackathon/[slug]/page.tsx
+++ b/apps/landing/src/app/(public)/hackathon/[slug]/page.tsx
@@ -1,7 +1,15 @@
import { notFound } from 'next/navigation';
import { Metadata } from 'next';
import { getHackathonBySlug, getAllHackathonSlugs } from '@/content/hackathons/content';
-import { HackathonDetailPage } from '@/app/(public)/hackathon/[slug]/_components/HackathonDetailPage';
+import { getDaysLeftText, getProgressPercent } from '@/content/hackathons/utils';
+import Link from 'next/link';
+import { Button } from '@components';
+import { BsArrowLeft } from 'react-icons/bs';
+import { HackathonHero } from './_components/HackathonHero';
+import { HackathonStatusBar } from './_components/HackathonStatusBar';
+import { HackathonSidebar } from './_components/HackathonSidebar';
+import { HackathonTabs } from './_components/HackathonTabs';
+import { MDXRemote } from 'next-mdx-remote/rsc';
interface Props {
params: {
@@ -21,7 +29,7 @@ export async function generateStaticParams() {
export async function generateMetadata({ params }: Props): Promise {
const resolvedParams = await params;
const hackathon = await getHackathonBySlug(resolvedParams.slug);
-
+
if (!hackathon) {
return {
title: 'Hackathon Not Found',
@@ -30,7 +38,7 @@ export async function generateMetadata({ params }: Props): Promise {
}
const { metadata } = hackathon;
-
+
return {
title: metadata.seoTitle || `${metadata.name} - IMPHNEN`,
description: metadata.seoDescription || metadata.description,
@@ -58,5 +66,56 @@ export default async function HackathonPage({ params }: Props) {
notFound();
}
- return ;
+ const { metadata, content } = hackathon;
+
+ // Return 404 if no content is found
+ if (!content || content.trim() === '') {
+ notFound();
+ }
+
+ const progressPercent = getProgressPercent(metadata);
+ const daysLeftText = getDaysLeftText(metadata);
+
+ return (
+
+
+
+ Back to Hackathons
+
+
+
+
+
+
+
+
+ {/* Sidebar only */}
+
+
+
+
+ {/* Tabs section */}
+
+
+
+
+ }
+ />
+
+
+
+
+
+ );
}
\ No newline at end of file
diff --git a/apps/landing/src/app/(public)/hackathon/page.tsx b/apps/landing/src/app/(public)/hackathon/page.tsx
index 507d9f4..6bf8875 100644
--- a/apps/landing/src/app/(public)/hackathon/page.tsx
+++ b/apps/landing/src/app/(public)/hackathon/page.tsx
@@ -6,11 +6,11 @@ import { motion } from 'framer-motion';
import Link from 'next/link';
import { HackathonSummary } from '@/content/hackathons/types';
import { hackathonSummaries } from '@/content/hackathons/index';
-import {
- getDaysLeftText,
- getProgressPercent,
- filterHackathons,
- sortHackathons
+import {
+ getDaysLeftText,
+ getProgressPercent,
+ filterHackathons,
+ sortHackathons
} from '@/content/hackathons/utils';
const HackathonTags: React.FC<{ tags?: string[] }> = ({ tags }) => {
@@ -98,34 +98,29 @@ const HackathonCard: React.FC<{ hackathon: HackathonSummary; idx: number }> = ({
};
export default function HackathonsPage() {
- // Use static data instead of API calls
const [filteredItems, setFilteredItems] = React.useState(hackathonSummaries);
const [searchTerm, setSearchTerm] = React.useState('');
const [statusFilter, setStatusFilter] = React.useState('all');
- // Filter and sort hackathons
+
React.useEffect(() => {
let filtered = [...hackathonSummaries];
-
- // Apply search filter
if (searchTerm) {
filtered = filterHackathons(filtered, { search: searchTerm });
}
- // Apply status filter
if (statusFilter !== 'all') {
filtered = filterHackathons(filtered, { status: [statusFilter] });
}
-
- // Sort by registration start date (newest first)
+
filtered = sortHackathons(filtered, 'registrationStart', 'desc');
-
setFilteredItems(filtered);
}, [searchTerm, statusFilter]);
return (
{/* Search and Filter Controls */}
+ {/*
setSearchTerm(e.target.value)}
- className="flex-1 px-4 py-2 border border-muted rounded-md focus:outline-none focus:ring-2 focus:ring-primary"
+ className="flex-1 px-4 py-2 border border-gray-400 rounded-md focus:outline-none focus:ring-2 focus:ring-primary"
/>
Ended
-
- {/* Results summary */}
-
- Showing {filteredItems.length} of {hackathonSummaries.length} hackathons
-
-
+
+ */}
))}
-
+
{/* No results message */}
{filteredItems.length === 0 && (
diff --git a/apps/landing/src/app/layout.tsx b/apps/landing/src/app/layout.tsx
index d5484f1..cc1b7bc 100644
--- a/apps/landing/src/app/layout.tsx
+++ b/apps/landing/src/app/layout.tsx
@@ -2,6 +2,7 @@ import { poppinsFont } from '@/lib/fonts';
import '@/styles/globals.css';
import { cn } from '@utils';
import { type Metadata } from 'next';
+import NextTopLoader from 'nextjs-toploader';
import { Providers } from './_components/providers';
import { Toaster } from './_components/toaster';
@@ -18,6 +19,17 @@ export default function RootLayout({
return (
+
= 6.0.0",
+ "react": ">= 16.0.0",
+ "react-dom": ">= 16.0.0"
+ }
+ },
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@@ -27417,6 +27435,12 @@
"node": ">=8"
}
},
+ "node_modules/nprogress": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
+ "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==",
+ "license": "MIT"
+ },
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -27978,7 +28002,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -29887,7 +29910,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
@@ -29899,7 +29921,6 @@
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/property-information": {
diff --git a/package.json b/package.json
index 6541e13..029b3d5 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"next": "~15.2.4",
"next-mdx-remote": "^5.0.0",
"next-themes": "^0.4.6",
+ "nextjs-toploader": "^3.9.17",
"openapi-fetch": "^0.14.0",
"openapi-react-query": "^0.5.0",
"react": "^19.1.0",