Skip to content

Commit 03e74c9

Browse files
authored
Merge pull request #13 from dcsil/rapid-mvp
Complete all code features for A2.
2 parents 165cb6d + 9a531f1 commit 03e74c9

31 files changed

Lines changed: 7700 additions & 1 deletion

.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
.env
31+
32+
# vercel
33+
.vercel
34+
35+
# typescript
36+
*.tsbuildinfo
37+
next-env.d.ts
38+
39+
# database
40+
*.db
41+
*.db-journal

README.md

Lines changed: 234 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,234 @@
1-
# the-painters-code
1+
# Classroom Presentation Randomizer
2+
3+
A command center dashboard for university instructors to manage high-stakes student presentations with randomization, timing, and grading capabilities.
4+
5+
## Features
6+
7+
### Setup Phase
8+
- **Session Management**: Create sessions with configurable presentation and Q&A durations
9+
- **Roster Import**:
10+
- Bulk CSV import (Team Name, Member1, Member2, ...)
11+
- Manual team entry
12+
- Duplicate team name prevention
13+
- **Rubric Builder**:
14+
- Create custom grading criteria with max scores and weights
15+
- Save and load rubric templates
16+
- Rubric locks after first presentation starts (ensures fairness)
17+
18+
### Presentation Phase
19+
- **Random Team Selection**: Fair randomization from eligible pool
20+
- **Smart Timer System**:
21+
- Manual start (never auto-starts)
22+
- Visual warnings at 2-minute mark
23+
- Overtime tracking (counts into negative with clear indication)
24+
- Separate presentation and Q&A timers
25+
- Browser crash recovery (auto-resumes from saved state)
26+
- **Flexible Controls**:
27+
- Skip/Defer absent teams (returns them to pool)
28+
- Emergency Stop with resume or restart options
29+
- Stop & Grade for early finishes
30+
- **Grading Interface**:
31+
- Score entry with validation (0 to max score)
32+
- Auto-calculated total scores
33+
- Public feedback (student-visible) and private notes
34+
- Cannot submit invalid scores
35+
36+
### History & Management
37+
- **View Completed Presentations**: See all graded teams with scores and feedback
38+
- **Edit Grades**: Modify scores/notes for any completed presentation
39+
- **Audit Trail**: Timestamps logged when grades are edited
40+
- **CSV Export**: Download all grades with team info and feedback
41+
42+
### Persistence
43+
- **SQLite Database**: All data persists locally
44+
- **Browser Crash Recovery**: Automatically resume active presentations with exact timer state
45+
- **Single Device**: Designed for use on one computer during sessions
46+
47+
## Tech Stack
48+
49+
- **Frontend**: Next.js 15 (React) with TypeScript
50+
- **Backend**: Next.js API Routes (Node.js)
51+
- **Database**: SQLite with better-sqlite3
52+
- **Styling**: Tailwind CSS
53+
- **Authentication**: JWT with httpOnly cookies
54+
55+
## Getting Started
56+
57+
### Prerequisites
58+
- Node.js 18+
59+
- pnpm (or npm/yarn)
60+
61+
### Installation
62+
63+
1. Clone the repository:
64+
```bash
65+
git clone <repo-url>
66+
cd the-painters-code
67+
```
68+
69+
2. Install dependencies:
70+
```bash
71+
pnpm install
72+
```
73+
74+
3. Set up environment variables:
75+
```bash
76+
# .env.local is already created with a default JWT secret
77+
# For production, change JWT_SECRET to a secure random string
78+
```
79+
80+
4. Run the development server:
81+
```bash
82+
pnpm dev
83+
```
84+
85+
5. Open [http://localhost:3000](http://localhost:3000) in your browser
86+
87+
### First Time Setup
88+
89+
1. Navigate to the signup page and create an account
90+
2. Login with your credentials
91+
3. Create a new session with:
92+
- Session name (e.g., "CSC491 Week 5")
93+
- Presentation duration (minutes)
94+
- Q&A duration (minutes)
95+
4. Add teams using bulk CSV or manual entry
96+
5. Create rubric criteria or load from a saved template
97+
6. Click "Start Presentation Session"
98+
99+
## Usage Guide
100+
101+
### Adding Teams (Bulk CSV)
102+
Format each line as: `Team Name, Member1, Member2, Member3, ...`
103+
104+
Example:
105+
```
106+
Team Alpha, Alice Smith, Bob Jones
107+
Team Beta, Carol White, Dave Brown, Eve Green
108+
Team Gamma, Frank Miller
109+
```
110+
111+
### During Presentations
112+
113+
1. **Pick Next Team**: Randomly selects from eligible pool
114+
2. **Start Presentation**: Manually start the timer
115+
3. **Monitor Timer**:
116+
- Green = plenty of time
117+
- Yellow = 2 minutes warning
118+
- Red = overtime
119+
4. **Controls**:
120+
- **Switch to Q&A**: End presentation phase, start Q&A
121+
- **Stop & Grade**: For early finishes or end of Q&A
122+
- **Emergency Stop**: Pause with option to resume or defer
123+
5. **Grade**: Fill in scores, add feedback, submit
124+
6. **Repeat**: Pick next team when ready
125+
126+
### Skip/Defer Scenarios
127+
128+
- **Team Absent**: Click team, then use defer to return them to pool
129+
- **Technical Issues**: Emergency Stop → Defer (they can go later)
130+
- **Student Emergency**: Emergency Stop → option to restart or defer
131+
132+
### Editing Grades
133+
134+
1. Go to "View History"
135+
2. Find the presentation
136+
3. Click "Edit Grades"
137+
4. Modify scores/feedback
138+
5. Save (creates audit trail entry)
139+
140+
### Exporting Data
141+
142+
1. Go to "View History"
143+
2. Click "Export to CSV"
144+
3. Downloads file with all team scores and feedback
145+
146+
## Project Structure
147+
148+
```
149+
the-painters-code/
150+
├── app/
151+
│ ├── api/ # API routes
152+
│ │ ├── auth/ # Authentication endpoints
153+
│ │ ├── session/ # Session management
154+
│ │ ├── teams/ # Team operations
155+
│ │ ├── rubric/ # Rubric and templates
156+
│ │ ├── presentations/# Presentation state
157+
│ │ ├── grades/ # Grading operations
158+
│ │ └── export/ # CSV export
159+
│ ├── dashboard/ # Main dashboard page
160+
│ ├── login/ # Login page
161+
│ ├── signup/ # Signup page
162+
│ ├── layout.tsx # Root layout
163+
│ ├── page.tsx # Home (redirects)
164+
│ └── globals.css # Global styles
165+
├── components/
166+
│ ├── Dashboard.tsx # Main dashboard container
167+
│ ├── SetupPhase.tsx # Session/roster/rubric setup
168+
│ ├── PresentationPhase.tsx # Randomizer, timer, grading
169+
│ └── HistoryView.tsx # Completed presentations
170+
├── lib/
171+
│ ├── db.ts # SQLite connection and schema
172+
│ └── auth.ts # JWT utilities
173+
├── types/
174+
│ └── index.ts # TypeScript type definitions
175+
└── classroom.db # SQLite database (auto-created)
176+
```
177+
178+
## Database Schema
179+
180+
### Tables
181+
- **users**: Instructor accounts
182+
- **sessions**: Presentation sessions with timer configs
183+
- **teams**: Team rosters with members
184+
- **rubric_templates**: Saved rubric configurations
185+
- **rubric_criteria**: Session-specific grading criteria
186+
- **presentations**: Presentation records with timer state
187+
- **grades**: Individual criterion scores
188+
- **feedback**: Public and private feedback
189+
- **grade_audit**: Edit history for grades
190+
- **session_state**: Recovery state for browser crashes
191+
192+
## Development
193+
194+
### Build for Production
195+
```bash
196+
pnpm build
197+
pnpm start
198+
```
199+
200+
### Type Checking
201+
```bash
202+
pnpm tsc --noEmit
203+
```
204+
205+
### Linting
206+
```bash
207+
pnpm lint
208+
```
209+
210+
## Security Notes
211+
212+
- JWT tokens stored in httpOnly cookies (XSS protection)
213+
- Passwords hashed with bcryptjs
214+
- Database uses foreign keys and constraints
215+
- Input validation on all API endpoints
216+
- Change JWT_SECRET for production deployments
217+
218+
## Future Enhancements (Not in MVP)
219+
220+
- Multi-instructor support with isolated data
221+
- Multiple concurrent sessions
222+
- Real-time presentation view for projection
223+
- Student-facing grade viewing portal
224+
- Advanced analytics and reporting
225+
- Email notifications
226+
- Mobile responsive improvements
227+
228+
## License
229+
230+
MIT
231+
232+
## Support
233+
234+
For issues or questions, please open an issue on GitHub.

app/api/auth/login/route.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { NextResponse } from 'next/server';
2+
import bcrypt from 'bcryptjs';
3+
import db from '@/lib/db';
4+
import { createToken, setAuthCookie } from '@/lib/auth';
5+
import type { User } from '@/types';
6+
7+
export async function POST(request: Request) {
8+
try {
9+
const { email, password } = await request.json();
10+
11+
// Validation
12+
if (!email || !password) {
13+
return NextResponse.json(
14+
{ error: 'Email and password are required' },
15+
{ status: 400 }
16+
);
17+
}
18+
19+
// Find user
20+
const user = db
21+
.prepare('SELECT * FROM users WHERE email = ?')
22+
.get(email) as User | undefined;
23+
24+
if (!user) {
25+
return NextResponse.json(
26+
{ error: 'Invalid credentials' },
27+
{ status: 401 }
28+
);
29+
}
30+
31+
// Verify password
32+
const isValidPassword = await bcrypt.compare(password, user.password);
33+
34+
if (!isValidPassword) {
35+
return NextResponse.json(
36+
{ error: 'Invalid credentials' },
37+
{ status: 401 }
38+
);
39+
}
40+
41+
// Create token
42+
const token = await createToken({ userId: user.id, email: user.email });
43+
await setAuthCookie(token);
44+
45+
return NextResponse.json(
46+
{ message: 'Login successful', userId: user.id },
47+
{ status: 200 }
48+
);
49+
} catch (error) {
50+
console.error('Login error:', error);
51+
return NextResponse.json(
52+
{ error: 'Internal server error' },
53+
{ status: 500 }
54+
);
55+
}
56+
}

app/api/auth/logout/route.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { NextResponse } from 'next/server';
2+
import { clearAuthCookie } from '@/lib/auth';
3+
4+
export async function POST() {
5+
try {
6+
await clearAuthCookie();
7+
return NextResponse.json({ message: 'Logout successful' }, { status: 200 });
8+
} catch (error) {
9+
console.error('Logout error:', error);
10+
return NextResponse.json(
11+
{ error: 'Internal server error' },
12+
{ status: 500 }
13+
);
14+
}
15+
}

0 commit comments

Comments
 (0)