Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 34 additions & 31 deletions .idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions app/api/events/[slug]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { NextRequest, NextResponse } from 'next/server';

import connectDB from '@/lib/mongodb';
import Event from '@/database/event.model';

// Define route params type for type safety
type RouteParams = {
params: Promise<{
slug: string;
}>;
};

/**
* GET /api/events/[slug]
* Fetches a single events by its slug
*/
export async function GET(req: NextRequest, { params }: RouteParams): Promise<NextResponse> {
try {
// Connect to database
await connectDB();

// Await and extract slug from params
const { slug } = await params;

// Validate slug parameter
if (!slug || typeof slug !== 'string' || slug.trim() === '') {
return NextResponse.json(
{ message: 'Invalid or missing slug parameter' },
{ status: 400 }
);
}

// Sanitize slug (remove any potential malicious input)
const sanitizedSlug = slug.trim().toLowerCase();

// Query events by slug
const event = await Event.findOne({ slug: sanitizedSlug }).lean();

// Handle events not found
if (!event) {
return NextResponse.json(
{ message: `Event with slug '${sanitizedSlug}' not found` },
{ status: 404 }
);
}

// Return successful response with events data
return NextResponse.json({ message: 'Event fetched successfully', event }, { status: 200 });
} catch (error) {
// Log error for debugging (only in development)
if (process.env.NODE_ENV === 'development') {
console.error('Error fetching events by slug:', error);
}

// Handle specific error types
if (error instanceof Error) {
// Handle database connection errors
if (error.message.includes('MONGODB_URI')) {
return NextResponse.json(
{ message: 'Database configuration error' },
{ status: 500 }
);
}

// Return generic error with error message
return NextResponse.json(
{ message: 'Failed to fetch events', error: error.message },
{ status: 500 }
);
}

// Handle unknown errors
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}
75 changes: 75 additions & 0 deletions app/api/events/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { NextRequest, NextResponse } from 'next/server';
import { v2 as cloudinary } from 'cloudinary';

import connectDB from '@/lib/mongodb';
import Event from '@/database/event.model';

export async function POST(req: NextRequest) {
try {
await connectDB();

const formData = await req.formData();

let event;

try {
event = Object.fromEntries(formData.entries());
} catch {
return NextResponse.json({ message: 'Invalid JSON data format' }, { status: 400 });
}

const file = formData.get('image') as File;

if (!file) return NextResponse.json({ message: 'Image file is required' }, { status: 400 });

const tags = JSON.parse(formData.get('tags') as string);
const agenda = JSON.parse(formData.get('agenda') as string);

const arrayBuffer = await file.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);

const uploadResult = await new Promise((resolve, reject) => {
cloudinary.uploader
.upload_stream({ resource_type: 'image', folder: 'DevEvent' }, (error, results) => {
if (error) return reject(error);

resolve(results);
})
.end(buffer);
});

event.image = (uploadResult as { secure_url: string }).secure_url;

const createdEvent = await Event.create({
...event,
tags: tags,
agenda: agenda,
});

return NextResponse.json(
{ message: 'Event created successfully', event: createdEvent },
{ status: 201 }
);
} catch (e) {
console.error(e);
return NextResponse.json(
{ message: 'Event Creation Failed', error: e instanceof Error ? e.message : 'Unknown' },
{ status: 500 }
);
}
}

export async function GET() {
try {
await connectDB();

const events = await Event.find().sort({ createdAt: -1 });

return NextResponse.json(
{ message: 'Events fetched successfully', events },
{ status: 200 }
);
} catch (e) {
return NextResponse.json({ message: 'Event fetching failed', error: e }, { status: 500 });
}
}
16 changes: 16 additions & 0 deletions app/events/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Suspense } from 'react';
import EventDetails from '@/components/EventDetails';

const EventDetailsPage = async ({ params }: { params: Promise<{ slug: string }> }) => {
const { slug } = await params;

return (
<main>
<Suspense fallback={<div>Loading...</div>}>
<EventDetails slug={slug} />
</Suspense>
</main>
);
};

export default EventDetailsPage;
83 changes: 36 additions & 47 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,54 @@
import type { Metadata } from 'next';
import { Geist, Martian_Mono, Schibsted_Grotesk } from 'next/font/google';
import './globals.css';
import { ReactNode } from 'react';
import { cn } from '@/lib/utils';
import LightRays from '@/components/LightRays';
import Navbar from '@/components/Navbar';

const geist = Geist({ subsets: ['latin'], variable: '--font-sans' });
import type { Metadata } from "next";
import { Schibsted_Grotesk, Martian_Mono } from "next/font/google";
import "./globals.css";
import LightRays from "@/components/LightRays";
import Navbar from "@/components/Navbar";

const schibstedGrotesk = Schibsted_Grotesk({
variable: '--font-schibsted-grotesk',
subsets: ['latin'],
variable: "--font-schibsted-grotesk",
subsets: ["latin"],
});

const martianMono = Martian_Mono({
variable: '--font-martian-mono',
subsets: ['latin'],
variable: "--font-martian-mono",
subsets: ["latin"],
});

export const metadata: Metadata = {
title: 'DevEvent',
description: "The Hub for Every Dev Event You Must'nt Miss",
title: "DevEvent",
description: "The Hub for Every Dev Event You Mustn't Miss",
};

export default function RootLayout({
children,
}: Readonly<{
children: ReactNode;
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html
lang='en'
className={cn(
'h-full',
'antialiased',
schibstedGrotesk.variable,
martianMono.variable,
'font-sans',
geist.variable
)}
style={{ scrollBehavior: 'smooth' }}
<html lang="en">
<body
className={`${schibstedGrotesk.variable} ${martianMono.variable} min-h-screen antialiased`}
>
<body className='min-h-screen flex flex-col'>
<Navbar />
<Navbar />

<div className={'absolute inset-0 top-0 z-[-1] min-h-screen'}>
<LightRays
raysOrigin='top-center-offset'
raysColor='#5dfeca'
raysSpeed={0.5}
lightSpread={0.9}
rayLength={1.4}
followMouse={true}
mouseInfluence={0.02}
noiseAmount={0.0}
distortion={0.01}
/>
</div>
<div className="absolute inset-0 top-0 z-[-1] min-h-screen">
<LightRays
raysOrigin="top-center-offset"
raysColor="#5dfeca"
raysSpeed={0.5}
lightSpread={0.9}
rayLength={1.4}
followMouse={true}
mouseInfluence={0.02}
noiseAmount={0.0}
distortion={0.01}
/>
</div>

<main>{children}</main>
</body>
<main>
{children}
</main>
</body>
</html>
);
}
}
20 changes: 12 additions & 8 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import ExploreBtn from '@/components/ExploreBtn';
import EventCard from '@/components/EventCard';
import { events } from '@/lib/constants';
import { getEvents } from '@/lib/actions/event.actions';

export const dynamic = 'force-dynamic';

const Page = async () => {
const events = await getEvents();

const Page = () => {
return (
<section>
<h1 className={'text-center'}>
The Hub for Every Dev <br /> Event You Can&#39;t Miss
<h1 className='text-center'>
The Hub for Every Dev <br /> Event You Cannot Miss
</h1>
<p className={'text-center mt-5'}>
<p className='text-center mt-5'>
Hackathons, Meetups, and Conferences, All in One Place
</p>

<ExploreBtn />

<div className={'mt-20 space-y-7'}>
<div className='mt-20 space-y-7'>
<h3>Featured Events</h3>

<ul className={'events'} id={'events'}>
<ul className='events' id='events'>
{events.map((event) => (
<li style={{listStyleType: 'none'}} key={event.title}>
<li key={event.slug} className='list-none'>
<EventCard {...event} />
</li>
))}
Expand Down
Loading
Loading