Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ next-env.d.ts
.env.local
.vscode/
tsconfig.tsbuildinfo
playwright-report/.last-run.json
116 changes: 116 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

BSL Platform is a Next.js 15 web application for Big Strategy Labs, an invite-only startup accelerator. It manages startup applications, events, team leadership profiles, and provides an admin dashboard for reviewing applications.

## Tech Stack

- **Framework:** Next.js 15 (App Router)
- **Database:** MongoDB with Prisma ORM
- **Authentication:** NextAuth v5 (Google OAuth, JWT sessions)
- **Styling:** Tailwind CSS + Shadcn/Radix UI components
- **Validation:** Zod

## Commands

```bash
npm run dev # Start development server (port 3000)
npm run build # Production build
npm run lint # Run ESLint
npm run lint-fix # Fix lint issues and format with Prettier
npm run lint-check # Check lint and formatting without fixing
npm run format # Format all files with Prettier
npm run test # Run Playwright E2E tests
npm run seed # Seed user roles (edit prisma/seed.ts first)
npx prisma generate # Generate Prisma client after schema changes
npx prisma db push # Push schema changes to MongoDB
```

## Architecture

### App Router Structure

```
app/
├── api/
│ ├── auth/[...nextauth]/ # NextAuth handler
│ ├── applications/startup/ # POST startup applications
│ ├── events/ # GET public events
│ └── admin/ # Admin API (applications, events CRUD)
├── apply/ # Application forms (startup, org, team)
├── admin/ # Admin dashboard and application review
├── leaders/ # Team leadership profiles
├── events/ # Public events page
└── about/ # About/mission page
```

### Key Directories

- **`components/ui/`** - Shadcn/Radix primitives (Button, Input, Card, etc.)
- **`components/layout/`** - PublicLayout wrapper with navbar/footer
- **`components/admin/`** - AdminLayout with sidebar
- **`lib/`** - Prisma client singleton, Mongoose connection, utilities
- **`services/`** - Data access layer (demo products, mock applications)

### Database (Prisma/MongoDB)

Three main models in `prisma/schema.prisma`:
- **User** - Auth with RBAC roles (USER, REVIEWER, AMBASSADOR, SUPER_ADMIN)
- **Application** - Form submissions with JSON payload, status tracking
- **Event** - Admin-managed events with creator relationship

### Authentication Flow

- NextAuth configured in `auth.ts` with Google provider
- Session provider wraps app in root `layout.tsx`
- Auth button component at `components/auth/AuthButton.tsx`
- RBAC enforced in API routes via requireRole() in lib/auth-helpers.ts

### Seeding User Roles

Edit `prisma/seed.ts` to assign roles for testing:
```ts
const SEED_USERS = [
{ email: "your-email@gmail.com", role: "SUPER_ADMIN", name: "Your Name" },
];
```
Then run `npm run seed`. Safe to re-run (uses upsert).

### Path Aliases

Use `@/*` to import from project root (configured in tsconfig.json).

```typescript
import { prisma } from "@/lib/prisma";
import { Button } from "@/components/ui/button";
```

## Environment Variables

Required in `.env` (see `.env.example`):
```
DATABASE_URL=mongodb://localhost:27017/test-db
AUTH_SECRET= # openssl rand -base64 32
AUTH_GOOGLE_ID= # Google Cloud Console
AUTH_GOOGLE_SECRET=
```

## Code Patterns

- Client components use `"use client"` directive
- API routes return `NextResponse.json()` with appropriate status codes
- Prisma client is singleton-cached in `lib/prisma.ts` to prevent hot-reload leaks
- Tailwind classes merged with `cn()` utility from `lib/utils.ts`
- Forms use client-side state with POST to API routes

## Testing

Playwright E2E tests in `tests/e2e/`. The config auto-starts dev server on port 3000.

```bash
npx playwright test # Run all tests
npx playwright test tests/e2e/demo.spec.ts # Run specific test
```
16 changes: 8 additions & 8 deletions app/leaders/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,49 @@ const team = [
name: "Sarah Johnson",
title: "Founder & CEO",
bio: "Former co-founder of TechVentures. Early investor at Google and Amazon.",
image: "woman.jpg",
image: "/woman.jpg",
},
{
name: "Michael Chen",
title: "Investment Director",
bio: "Lead investor at Sequoia Capital. Former partner at Y Combinator.",
image: "men.jpg",
image: "/men.jpg",
},
{
name: "Emily Rodriguez",
title: "Head of Programs",
bio: "Built accelerator programs at Techstars. Former founder of EdTech startup.",
image: "woman.jpg",
image: "/woman.jpg",
},
{
name: "David Park",
title: "Technical Advisor",
bio: "Former CTO at Stripe. Led engineering at Facebook and Airbnb.",
image: "men.jpg",
image: "/men.jpg",
},
{
name: "Alex Thompson",
title: "Operations Manager",
bio: "Scaled operations at Uber. Former consultant at McKinsey.",
image: "men.jpg",
image: "/men.jpg",
},
{
name: "Maria Garcia",
title: "Venture Partner",
bio: "15 years in venture capital. Board member at multiple unicorns.",
image: "woman.jpg",
image: "/woman.jpg",
},
{
name: "Ryan Mitchell",
title: "Community Lead",
bio: "Built communities at Reddit. Former developer relations at GitHub.",
image: "men.jpg",
image: "/men.jpg",
},
{
name: "Sharad Aggarwal",
title: "VC",
bio: "Global head of AI Strategy at Google Cloud.",
image: "men.jpg",
image: "/men.jpg",
},
];

Expand Down
32 changes: 11 additions & 21 deletions components/ui/LeaderCard2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,23 @@ interface LeaderCardProps {
reverse?: boolean;
}

export default function LeaderCard2({ name, title, bio, image, reverse = false }: LeaderCardProps) {
export default function LeaderCard2({ name, title, bio, image }: LeaderCardProps) {
return (
<div
style={{
display: 'flex',
flexDirection: reverse ? 'row-reverse' : 'row',
alignItems: 'center',
backgroundColor: '#d6eef8',
borderRadius: '16px',
overflow: 'visible',
position: 'relative',
minHeight: '180px',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
}}
>
{/* Image — overflows card top/bottom */}
{/* Image */}
<div
style={{
flexShrink: 0,
width: '200px',
height: '230px',
width: '100%',
height: '200px',
position: 'relative',
zIndex: 1,
marginTop: '-24px',
marginBottom: '-24px',
marginLeft: reverse ? '20px' : '-10px',
marginRight: reverse ? '-10px' : '20px',
}}
>
<Image
Expand All @@ -43,20 +34,19 @@ export default function LeaderCard2({ name, title, bio, image, reverse = false }
fill
style={{
objectFit: 'cover',
borderRadius: '14px',
}}
/>
</div>

{/* Text content */}
<div style={{ flex: 1, padding: '28px 32px' }}>
<h3 style={{ fontSize: '22px', fontWeight: '700', margin: '0 0 4px 0', color: '#000' }}>
<div style={{ padding: '20px 24px' }}>
<h3 style={{ fontSize: '20px', fontWeight: '700', margin: '0 0 4px 0', color: '#000' }}>
{name}
</h3>
<p style={{ fontSize: '15px', fontWeight: '400', margin: '0 0 12px 0', color: '#333' }}>
<p style={{ fontSize: '14px', fontWeight: '500', margin: '0 0 10px 0', color: '#555' }}>
{title}
</p>
<p style={{ fontSize: '14px', lineHeight: '1.6', margin: '0', color: '#333' }}>
<p style={{ fontSize: '13px', lineHeight: '1.5', margin: '0', color: '#444' }}>
{bio}
</p>
</div>
Expand Down
Loading