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
7 changes: 5 additions & 2 deletions .github/workflows/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
types: [opened, synchronize, reopened]

permissions:
contents: read

jobs:
test:
name: 🧪 Tests
Expand All @@ -31,5 +34,5 @@ jobs:
working-directory: app
run: bun run typecheck
- name: 🏄 Run the tests
run: echo "No tests configured yet. Add tests as needed."

working-directory: app
run: bun run test
19 changes: 18 additions & 1 deletion app/bun.lock

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

7 changes: 6 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
"typecheck": "tsc --noEmit",
"typecheck:scripts": "tsc --noEmit --project scripts/tsconfig.json",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate"
"db:migrate": "drizzle-kit migrate",
"test": "bun test"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.888.0",
"@aws-sdk/s3-request-presigner": "^3.888.0",
"@chat-adapter/discord": "^4.15.0",
"@chat-adapter/state-redis": "^4.15.0",
"@date-fns/tz": "^1.5.0",
"@hookform/resolvers": "^5.2.2",
"@neondatabase/serverless": "^1.0.1",
"@radix-ui/react-accordion": "^1.2.12",
Expand Down Expand Up @@ -71,6 +73,7 @@
"drizzle-orm": "^0.44.5",
"embla-carousel-react": "^8.6.0",
"heic-convert": "^2.1.0",
"ical.js": "^2.2.1",
"input-otp": "^1.4.2",
"lucide-react": "^0.544.0",
"next": "^16.0.10",
Expand All @@ -94,8 +97,10 @@
"zod": "^4.1.8"
},
"devDependencies": {
"@electric-sql/pglite": "^0.5.8",
"@modelcontextprotocol/sdk": "^1.18.0",
"@tailwindcss/postcss": "^4",
"@types/bun": "^1.4.2",
"@types/canvas-confetti": "^1.9.0",
"@types/node": "^20",
"@types/qrcode": "^1.5.5",
Expand Down
13 changes: 7 additions & 6 deletions app/src/app/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function generateMetadata({
const { slug } = await params;
const event = await getExpandedEventBySlug(slug);

if (!event) {
if (!event || event.isDraft) {
return {
title: "Event not found",
};
Expand Down Expand Up @@ -67,16 +67,17 @@ export default async function EventPage({ params }: PageProps) {
const { slug } = await params;
const event = await getExpandedEventBySlug(slug);

if (!event) {
if (!event || event.isDraft) {
notFound();
}

// Get real attendee count from Luma API
const attendeeCount = event.lumaEventId
? await getEventAttendeeCount(event.lumaEventId)
: 0;
const attendeeCount =
event.lumaEventId && event.attendeeLimit > 0
? await getEventAttendeeCount(event.lumaEventId)
: 0;
const attendeeLimit = event.attendeeLimit;
const isAtCapacity = attendeeCount >= attendeeLimit;
const isAtCapacity = attendeeLimit > 0 && attendeeCount >= attendeeLimit;
const isInPast = isEventInPast(event);

return (
Expand Down
39 changes: 22 additions & 17 deletions app/src/app/api/cron/luma-sync/route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { NextResponse } from "next/server";
import { start } from "workflow/api";
import { revalidatePath } from "next/cache";
import { captureException } from "@sentry/nextjs";
import { mainConfig } from "@/lib/config";
import { syncLumaEventsWorkflow } from "@/workflows/luma-sync";
import { db } from "@/lib/db";
import { syncPublicLumaEvents } from "@/lib/luma/sync";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

const DEFAULT_LIMIT = 10;
const DEFAULT_CALENDAR_HANDLE = "allthingswebcalendar";
export const maxDuration = 60;

function isAuthorized(request: Request, cronSecret: string): boolean {
return request.headers.get("authorization") === `Bearer ${cronSecret}`;
Expand All @@ -31,24 +32,28 @@ export async function GET(request: Request) {
}

try {
const calendarApiId = mainConfig.luma.calendarApiId;
const calendarHandle =
mainConfig.luma.calendarHandle ?? DEFAULT_CALENDAR_HANDLE;

const run = await start(syncLumaEventsWorkflow, [
{
limit: DEFAULT_LIMIT,
calendarApiId,
calendarHandle,
},
]);
const { slugs, ...result } = await syncPublicLumaEvents(
db,
mainConfig.luma.calendarApiId,
);
for (const path of [
"/",
"/api/v1/events",
"/rss",
"/sitemap.xml",
...slugs.map((slug) => `/${slug}`),
]) {
revalidatePath(path);
}
console.info("Luma calendar sync completed", result);

return NextResponse.json({
ok: true,
runId: run.runId,
message: "Luma sync workflow started",
...result,
});
} catch (error) {
captureException(error);
console.error("Luma calendar sync failed", error);
const message = error instanceof Error ? error.message : "Unknown error";

return NextResponse.json({ ok: false, error: message }, { status: 500 });
Expand Down
2 changes: 1 addition & 1 deletion app/src/app/api/v1/events/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export async function GET(
try {
const event = await getExpandedEventById(id);

if (!event) {
if (!event || event.isDraft) {
return NextResponse.json({ error: "Event not found" }, { status: 404 });
}

Expand Down
8 changes: 3 additions & 5 deletions app/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,7 @@ export default async function HomePage() {
<OtherLiveEventsSection events={liveEvents.slice(1)} />
)}

{remainingEvents.length > 0 && (
<OtherUpcomingEventsSection events={remainingEvents} />
)}
<OtherUpcomingEventsSection events={remainingEvents} />

<Section variant="big" className="bg-indigo-600 text-white">
<div className="container">
Expand Down Expand Up @@ -294,7 +292,7 @@ function OtherUpcomingEventsSection({ events }: { events: Event[] }) {
<div className="container">
<div className="flex flex-col items-center space-y-4 text-center mb-8">
<h2 className="text-3xl font-bold tracking-tighter sm:text-4xl md:text-5xl">
Other events
Events on Luma
</h2>
<p className="text-muted-foreground md:text-xl max-w-[700px]">
Discover more upcoming web development events in the Bay Area here
Expand All @@ -311,7 +309,7 @@ function OtherUpcomingEventsSection({ events }: { events: Event[] }) {
</Link>
</Button>
</div>
<EventsCarousel events={events} />
{events.length > 0 && <EventsCarousel events={events} />}
</div>
</Section>
);
Expand Down
24 changes: 18 additions & 6 deletions app/src/components/event-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,14 +213,26 @@ export function AllYouNeedToKnowSection({
<UsersIcon className="h-10 w-10 sm:h-12 sm:w-12 text-primary flex-shrink-0 mt-1 lg:mt-0" />
<div className="min-w-0 flex-1">
<h3 className="text-lg sm:text-xl lg:text-2xl xl:text-3xl font-medium leading-tight">
{!isInPast && attendeeCount < attendeeLimit
? "Spots available"
: !isInPast && attendeeCount >= attendeeLimit
? "At capacity"
: "Event has ended"}
{isInPast
? "Event has ended"
: attendeeLimit <= 0
? "Registration on Luma"
: attendeeCount < attendeeLimit
? "Spots available"
: "At capacity"}
</h3>
<p className="text-sm sm:text-base lg:text-lg xl:text-xl text-muted-foreground mt-1">
{attendeeCount} / {attendeeLimit} guests registered
{attendeeLimit > 0 ? (
<>
{attendeeCount} / {attendeeLimit} guests registered
</>
) : (
<Link
href={event.lumaEventUrl || "https://luma.com/allthingsweb"}
>
View event details on Luma
</Link>
)}
</p>
</div>
</div>
Expand Down
Loading
Loading