Recall is a professional, high-performance web application designed for the Computer Science Association (CSA) at RIT Kottayam. It replaces fragmented, ad-hoc feedback collection tools (like Google Forms) with a centralized, persistent system to record, analyze, and archive feedback for all association eventsβincluding workshops, bootcamps, hackathons, and technical talks.
- Project Motivation
- Key Features
- Tech Stack
- Firestore Database Schema
- Architecture & System Flows
- Directory Structure
- Installation & Local Setup
- Firebase Console & Security Rules Configuration
- Deployment
- Maintainability & Handover Guide
The current reliance on individual-led feedback loops (primarily Google Forms) introduces three critical failure points:
- Access Fragmentation: Responses are visible only to the form creator. Sharing access is manual, leading to data siloing and lost insights.
- Institutional Memory Loss: When a new Executive Committee (Excom) takes charge, there is no permanent archive of past events, feedback, or operational issues. Every new committee starts blind.
- Lack of Automated Analytics: Raw spreadsheet data requires manual parsing. There is no automated, recurring visualization of ratings, option selections, or qualitative sentiment.
Recall resolves these issues by establishing a centralized dashboard accessible to all authenticated CSA members, preserving event archives across handovers, and generating real-time analytics with exportable data options.
- π Event Dashboard: Unified dashboard listing all events (workshops, hackathons, bootcamps, etc.) sorted chronologically, displaying active status and total response counts.
- π οΈ Drag-and-Drop Questionnaire Builder: Dynamic form builder supporting four question types:
single_choice(radio),mcq(checkbox),short_text(textarea), andstar_rating. Powered by@hello-pangea/dndwith custom autoscroll easing, slower scroll-rate dampening, and dynamic touch handle pill indicators designed for optimized mobile reordering. - π Auto-Locking Safety: The questionnaire builder automatically locks once an event receives its first response, preventing structural database mismatches.
- π‘οΈ Questionnaire Integrity Protection: Leverages a custom hashing mechanism to generate a signature for each form state. Submissions are rejected if the participant's form state does not match the active server configuration.
- π Live Analytics & Visualization: Interactive analytics using Recharts with optimized tab transitions using React 19
useTransitionand a responsive toolbar loading shimmer state for instantaneous feedback.- Single Choice: Rendered as colorful Pie Charts.
- Multiple Choice (MCQ): Visualized as Horizontal Bar Charts with multi-select labels.
- Star Ratings: Averages and star distributions.
- Short Text: Searchable, scrollable lists sorted by submission time.
- π Dynamic Viewport Layouts: Utilizes a
ResizeObserverdynamic offset tracker to calculate and adjust top padding in the questionnaire builder automatically, ensuring header title cards are fully visible under floating navigation bars across all device viewports. - π₯ Client-Side Excel/CSV Export: Enables administrators to download event response data instantly. Generates clean, structured, and properly-escaped spreadsheet reports directly in the browser as a
.csvfile (fully compatible with Microsoft Excel and Google Sheets) containing respondent details, timestamps, and corresponding answers. - π Hybrid Cookie-Based Auth: Combines client-side Firebase Auth with secure, server-side HTTP-Only session cookies verified by Next.js Middleware.
- π Premium Visual Interface: Built with a curated dark color palette, custom glassmorphism components, and a fluid 3D Three.js particle background (
PixelBlast) that adds a premium feel.
| Layer | Choice | Reason |
|---|---|---|
| Framework | Next.js 16 (App Router) | React Server Components, server-side API routes, and optimized assets. |
| Runtime | React 19 | Standardized DOM rendering and async state handlers. |
| Styling | Tailwind CSS v4 & PostCSS | Rapid utility styling paired with modern CSS variables. |
| Database | Cloud Firestore (NoSQL) | Real-time nested document storage, highly scalable, and flexible. |
| Auth | Firebase Authentication | Secure, managed authentication for internal members. |
| Admin Operations | Firebase Admin SDK | Bypasses standard rules securely in serverless endpoints. |
| Charts | Recharts | Canvas-free, SVG-based declarative charting library. |
| Visual Effects | Three.js & Postprocessing | Renders high-performance interactive 3D particle backgrounds. |
Firestore stores data in a hierarchical collection-document structure. Below is the nested design used in Recall:
events/ [Root Collection]
βββ {eventId}/ [Document: Event Metadata]
β βββ title: string
β βββ description: string | null
β βββ start_date: string (ISO Date YYYY-MM-DD)
β βββ end_date: string (ISO Date YYYY-MM-DD)
β βββ event_type: string ("Workshop" | "Bootcamp" | "Hackathon" | "Technical Talk" | "Other")
β βββ is_published: boolean
β βββ created_at: string (ISO Timestamp)
β β
β βββ questions/ [Subcollection: Form Structure]
β β βββ {questionId}/ [Document: Question Configuration]
β β βββ question_text: string
β β βββ question_type: string ("single_choice" | "mcq" | "short_text" | "star_rating")
β β βββ options: string[] | null
β β βββ order_index: number
β β βββ is_required: boolean
β β
β βββ responses/ [Subcollection: Submissions]
β βββ {responseId}/ [Document: Respondent Header]
β βββ respondent_token: string (Client-side UUID for deduplication)
β βββ respondent_name: string (Optional)
β βββ questionnaire_signature: string
β βββ submitted_at: string (ISO Timestamp)
β β
β βββ answers/ [Subcollection: Submitted Values]
β βββ {questionId}/ (Document ID matches Question Document ID)
β βββ answer_value: string | string[] | number
Note
Nesting answers inside a responses document allows the system to read a full submission in a single database read, while naming answer document IDs after their corresponding questionId optimizes per-question analytics aggregation.
Recall uses a split authentication guard to maintain a secure admin boundary while enabling unauthenticated responses:
sequenceDiagram
participant Browser as Admin Browser
participant Context as AuthContext
participant Server as Next.js API (/session)
participant SDK as Firebase Auth SDK
participant MW as Middleware (Edge)
Browser->>SDK: Login(email, password)
SDK-->>Browser: Return UserCredential & ID Token
Browser->>Context: State Change Detected
Context->>Server: POST /api/session { token }
Server-->>Browser: Set HTTP-Only '__session' Cookie
Note over Browser, MW: Subsequent Navigation to /dashboard
Browser->>MW: Request Page
alt Has Valid Cookie
MW-->>Browser: Render Dashboard (200)
else No Cookie / Invalid
MW-->>Browser: Redirect to /login (307)
end
When a questionnaire is built or updated, it has a cryptographic signature based on its structural layout (question texts, types, order, requirements, options). This signature is validated on response submission to prevent data pollution if an admin changes questions while a user is answering:
- Signature Generation: Normalized fields are hashed using a
djb2implementation:// lib/questionnaire-signature.ts function hashString(value: string) { let hash = 5381; for (let i = 0; i < value.length; i++) { hash = (hash * 33) ^ value.charCodeAt(i); } return (hash >>> 0).toString(36); }
- Validation Route: During
POST /api/submit-response:- Fetch active questions from Firestore.
- Recompute the signature.
- Compare the server-side signature with the payload's
questionnaire_signature. - If they mismatch, return
409 Conflict(Code:QUESTIONNAIRE_CHANGED), forcing the participant browser to reload.
Recall simplifies feedback retrieval by generating and compiling data client-side with zero external cloud dependencies or API authorization overhead. When an administrator clicks the "Save as Excel" button:
sequenceDiagram
participant Admin as Administrator
participant Browser as Browser (Client)
participant DB as Firestore
Admin->>Browser: Click "Save as Excel"
Browser->>DB: Fetch Responses & Answers for Event
Browser->>Browser: Construct CSV Headers (Timestamp, Name, Token, Questions...)
Browser->>Browser: Iterate responses & map answers to columns
Browser->>Browser: Sanitize content & escape double quotes
Browser->>Browser: Generate CSV Blob with UTF-8 encoding
Browser->>Browser: Trigger file download: recall-[event-title]-responses.csv
Browser-->>Admin: Download completed
The exported spreadsheet file matches standard tabular structures:
- Columns: Map out Respondent metadata (Submitted Timestamp, Respondent Name, Token) followed by each question defined in the questionnaire.
- Rows: A separate entry for each submitted response.
- Cell Values: Raw text strings, numbers, or semi-colon-separated lists (for multiple choice questions).
recall/
βββ app/ # Next.js App Router Pages & API Routes
β βββ (portal)/ # Administrative Authenticated Layout
β β βββ dashboard/ # Event list & creation entry point
β β βββ events/ # Event details, builders, and statistics
β β βββ new/ # Event metadata creator
β β βββ [event-id]/
β β βββ builder/ # Drag-and-drop questionnaire editor
β β βββ responses/ # Analytics charts and exports
β βββ api/ # Backend Endpoints
β β βββ export-responses-to-sheets/ # Unused legacy endpoint (Excel downloads are client-side)
β β βββ ping/ # Keeping database active (cron target)
β β βββ session/ # HTTP-Only cookie injector/remover
β β βββ submit-response/ # Validated public response receiver
β βββ login/ # Administrator login panel
β βββ reset-password/ # Re-authentication password gate
β βββ respond/[event-id]/ # Public participant response form
β βββ globals.css # Global styles and Tailwind layout
β βββ layout.tsx # Root layout & providers (Auth, Theme)
β βββ middleware.ts # Edge protection middleware
βββ components/ # React Components
β βββ ui/ # Core design system components
β β βββ Button.tsx # Customized primary/secondary action handlers
β β βββ Card.tsx # Content containers with custom glassmorphism
β β βββ Input.tsx / Select.tsx # Pre-styled visual inputs
β β βββ NavBar.tsx # Site headers and navigation controls
β β βββ PixelBlast.tsx # Three.js 3D dynamic particle background
β β βββ Spinner.tsx # Pre-styled visual loaders
β βββ AnalyticsCharts.tsx # Recharts visualization selector
β βββ ParticipantForm.tsx # Public questionnaire renderer & validator
β βββ ParticipantLinkShare.tsx # Copy-to-clipboard invite links
β βββ QuestionnaireBuilder.tsx # Form layout configurator
βββ lib/ # Core Utilities & Library Wrappers
β βββ auth.ts # Firebase Auth abstraction layer
β βββ AuthContext.tsx # Client context listener
β βββ db-admin.ts # Server-side Firestore operations (Firebase Admin)
β βββ db.ts # Client-side Firestore operations (Firestore SDK)
β βββ firebase-admin.ts # Firebase Admin SDK initializer
β βββ firebase.ts # Client Firebase SDK initializer
β βββ questionnaire-signature.ts # Hashing helper
β βββ types.ts # Global TypeScript type contracts
- Node.js v22.x
- npm v10.x
- A Firebase Project (with Firestore and Email/Password Sign-In enabled)
git clone https://github.com/kevinjose06/Recall.git
cd Recall
npm installCreate a .env.local file in the root directory:
# Client Firebase Keys (Publicly accessible in client bundles)
NEXT_PUBLIC_FIREBASE_API_KEY=your-api-key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project-id.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your-project-id.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your-sender-id
NEXT_PUBLIC_FIREBASE_APP_ID=your-app-id
# Server-Side Firebase Admin Secret (Keep private)
# Escape double quotes inside the private key string or paste the service account JSON on a single line
FIREBASE_SERVICE_ACCOUNT_JSON={"type":"service_account","project_id":"your-project-id","private_key_id":"...","private_key":"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n","client_email":"firebase-adminsdk-...@your-project-id.iam.gserviceaccount.com",...}npm run devOpen http://localhost:3000 with your browser.
To keep the platform's backend secure while allowing unauthenticated form submissions, configure Cloud Firestore with the following security schema in the Rules tab:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper: Checks if the request is from an authenticated administrator
function isAdmin() {
return request.auth != null;
}
match /events/{eventId} {
// Events can only be read or written by administrators
allow read, write: if isAdmin();
match /questions/{questionId} {
// Form questions are only manageable by administrators
allow read, write: if isAdmin();
}
match /responses/{responseId} {
// Anyone can submit a response container (required for public forms)
allow create: if true;
// Only administrators can view submitted responses
allow read: if isAdmin();
match /answers/{questionId} {
// Anyone can submit individual answer records
allow create: if true;
// Only administrators can read individual answer records
allow read: if isAdmin();
}
}
}
}
}Recall is fully compatible with Vercel.
- Link your repository to Vercel.
- Add all variables in the
.env.localfile to the Environment Variables tab in your Vercel project settings. - Trigger a production build.
Although Cloud Firestore has no inactivity pause, any external components or microservices (e.g. Supabase instances integrated in adjacent projects) might sleep. A keepalive route is available at /api/ping.
You can configure a periodic health check via cron-job.org targeting https://<your-vercel-domain>/api/ping every 5 days to ensure adjacent services stay active.
To ensure future Executive Committees can take over operations seamlessly, follow this protocol:
- π Password Rotation:
- Administrative accounts use a shared login (
csa@ritkerala.ac.inor similar). - Rotation must occur once per year during the leadership transition.
- The outgoing Tech Lead must update the password directly via the Firebase Console (Authentication -> Users -> Reset Password).
- The incoming Tech Lead should change the password in-app at
/reset-passwordafter proving knowledge of the current credentials. - Distribute the new password securely via official college emails (never via messaging applications).
- Administrative accounts use a shared login (
- π Firebase Admin SDK Permissions:
- Ensure that the service account JSON defined in the
FIREBASE_SERVICE_ACCOUNT_JSONenvironment variable has the necessary permissions to access your Firestore database (e.g., Firebase Admin SDK Administrator Service Agent or Editor role on the project).
- Ensure that the service account JSON defined in the
Created and maintained by the Computer Science Association (CSA), RIT Kottayam.