A TypeScript-based tool for assigning primary school kids to workshops during project week, built on Val.town.
Target Users: Primary school (60-70 kids, ages 5-11)
Scope: 2-day workshop week, 3 time slots per day, 8-10 workshops per slot
Platform: Val.town with SQLite database and server-side JSX
- Server-side architecture: Val.town's JSX renders on server, returns static HTML
- No client-side React: Limited to form submissions and page navigation
- TypeScript throughout: All vals written in TypeScript for type safety
- SQLite database: Built-in database with ACID transactions (10MB free, 1GB paid)
- Authentication: Hardcoded admin password with URL-based auth
- Frontend: Server-rendered JSX with Hono framework + progressive enhancement
- Data storage: SQLite with proper foreign keys and constraints
- Interaction pattern: Traditional form submissions instead of SPA interactions
- HTTP Handler Vals: For web interfaces and API endpoints
- Script Vals: For shared utilities, types, and database functions
- SQLite integration: Via
std/sqlite
for persistent data storage - JSX support: Server-side rendering with Preact or React
- NPM packages: Access to TypeScript, Hono, and other libraries
- Instant deployment: Code changes deploy immediately in browser
- Admin authentication and dashboard
- Workshop CRUD operations (create, edit, delete)
- Student management with bulk import
- Database initialization and schema
- Student-facing preference ranking interface
- Admin monitoring of completion status
- Preference data collection and validation
- Fair assignment algorithm implementation
- Admin review and adjustment interface
- Final schedule generation and export
- Runtime: Val.town (Deno-based serverless)
- Language: TypeScript
- Database: SQLite via Val.town std/sqlite
- Frontend: Server-side JSX (Preact/React)
- Framework: Hono for routing and responses
- Authentication: Simple password-based auth
- Deployment: Automatic via Val.town
import { sqlite } from "https://esm.town/v/std/sqlite";
// Example usage:
const data = await sqlite.execute("SELECT datetime();");
console.log(data.rows[0]);
// Import from your own vals
import { Workshop, Kid } from "./workshopTypes";
import { initDatabase } from "./workshopDatabase";
// Import from other users' vals
import { someTool } from "https://esm.town/v/username/valname";
interface Workshop {
id: number;
name: string;
description: string;
age_groups: string;
capacity: number;
day: 'monday' | 'tuesday';
time_slot: 1 | 2 | 3;
}
interface Kid {
id: number;
name: string;
grade: string;
}
interface Preference {
kid_id: number;
workshop_id: number;
rank: 1 | 2 | 3;
day: 'monday' | 'tuesday';
time_slot: 1 | 2 | 3;
}
Limitation: No client-side JavaScript/React hooks
Workaround: Server-side forms with immediate response pages
Limitation: No real-time updates
Workaround: Page refreshes and status polling via forms
Limitation: Traditional web app feel vs modern SPA
Workaround: Fast server responses and progressive enhancement
This project was designed with Claude's assistance to work within Val.town's server-side constraints while maintaining modern TypeScript development practices. The architecture prioritizes simplicity and rapid deployment over complex client-side interactions.
Repository Structure: Each major component becomes a separate Val on Val.town, with shared utilities imported between vals using Val.town's module system.