diff --git a/Dockerfile b/Dockerfile
index 3a0c29f..b54ace9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
FROM node:22-alpine AS base
# Install pnpm
-RUN corepack enable && corepack prepare pnpm@latest --activate
+RUN corepack enable && corepack prepare pnpm@10 --activate
FROM base AS deps
@@ -75,4 +75,4 @@ EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
-CMD ["npx", "tsx", "src/server.ts"]
+CMD ["npx", "tsx", "src/server.ts"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 551fa77..7250e88 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+Hey
# AskEasy
A real-time classroom Q&A platform built for live lectures at the University of Toronto. Students post questions anonymously or publicly, upvote what matters most, and get answers from instructors — all updating instantly during class. Professors see exactly what the room is confused about, right now.
@@ -16,17 +17,17 @@ AskEasy is built for that moment. It gives every lecture a live Q&A room where t
┌─────────────────────────────────────────────────────────────┐
│ Production │
│ │
-│ Browser ──HTTPS──▶ Apache + mod_shib ──localhost──▶ App │
+│ Browser ──HTTPS──▶ Apache + mod_shib ──localhost──▶ App │
│ │ │
│ ▼ │
│ U of T IdP (SAML) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
-│ Application Layer │
+│ Application Layer │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
-│ │ Node.js Custom Server (server.ts) │ │
+│ │ Node.js Custom Server (server.ts) │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌────────────────────────┐ │ │
│ │ │ Next.js App │ │ Socket.IO Server │ │ │
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 3170f94..d7b8ad1 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -9,6 +9,7 @@ services:
- ./src/server.ts:/app/src/server.ts:ro
- ./src/app/api/auth/session/route.ts:/app/src/app/api/auth/session/route.ts:ro
- ./whitelist.txt:/app/whitelist.txt:ro
+ - ./admin_whitelist.txt:/app/admin_whitelist.txt:ro
- ./uploads:/app/uploads
env_file: .env
environment:
diff --git a/package.json b/package.json
index fea4fae..a4832bb 100644
--- a/package.json
+++ b/package.json
@@ -36,12 +36,6 @@
"prisma": {
"seed": "pnpm run db:seed"
},
- "pnpm": {
- "onlyBuiltDependencies": [
- "@prisma/engines",
- "prisma"
- ]
- },
"dependencies": {
"@embedpdf/core": "^2.3.0",
"@embedpdf/engines": "^2.3.0",
@@ -98,4 +92,4 @@
"vite-tsconfig-paths": "^6.0.3",
"vitest": "^4.0.16"
}
-}
\ No newline at end of file
+}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 8984764..cde4117 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,4 +1,8 @@
packages:
- .
-onlyBuiltDependencies: '["@prisma/engines", "prisma"]'
+nodeLinker: hoisted
+
+onlyBuiltDependencies:
+ - '@prisma/engines'
+ - prisma
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000..f480140
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,4 @@
+User-agent: *
+Allow: /
+
+Sitemap: https://askeasy.utm.utoronto.ca/sitemap.xml
\ No newline at end of file
diff --git a/public/sitemap.xml b/public/sitemap.xml
new file mode 100644
index 0000000..3776883
--- /dev/null
+++ b/public/sitemap.xml
@@ -0,0 +1,15 @@
+
+
+
+ https://askeasy.utm.utoronto.ca
+ 2026-05-24
+ weekly
+ 1.0
+
+
+ https://askeasy.utm.utoronto.ca/classes
+ 2026-05-24
+ weekly
+ 0.8
+
+
diff --git a/src/app/demo-prof/page.tsx b/src/app/demo-prof/page.tsx
deleted file mode 100644
index ae94ff8..0000000
--- a/src/app/demo-prof/page.tsx
+++ /dev/null
@@ -1,562 +0,0 @@
-"use client";
-
-import { useState } from "react";
-import {
- BookOpen,
- Calendar,
- Radio,
- Settings,
- Square,
- PanelRightClose,
- Users,
- GraduationCap,
- Search,
- Download,
- X,
-} from "lucide-react";
-
-import type { Question, Comment } from "@/utils/types";
-import QuestionPost from "@/app/room/classChat/post/QuestionPost";
-import CommentPost from "@/app/room/classChat/post/CommentPost";
-import FilterTabs from "@/app/room/classChat/FilterTabs";
-import ChatInput from "@/app/room/classChat/ChatInput";
-
-// ---------------------------------------------------------------------------
-// Mock data
-// ---------------------------------------------------------------------------
-
-const MOCK_QUESTIONS: Question[] = [
- {
- id: "q1",
- type: "question",
- user: { username: "Sarah Chen", pfp: "", role: "STUDENT" },
- timestamp: "2:14 PM",
- content:
- "Can you go over that last example one more time? I didn't quite follow the recursion step.",
- upvotes: 12,
- isResolved: false,
- isAnonymous: false,
- replies: [
- {
- id: "a1",
- type: "comment",
- user: { username: "Prof. Williams", pfp: "", role: "PROFESSOR" },
- timestamp: "2:16 PM",
- content:
- "Sure! The key idea is that each recursive call reduces the problem size by half. I'll draw it out on the board in a moment.",
- upvotes: 5,
- isAnonymous: false,
- },
- ],
- },
- {
- id: "q2",
- type: "question",
- user: { username: "Alex Kim", pfp: "", role: "STUDENT" },
- timestamp: "2:18 PM",
- content: "How does binary search handle edge cases like an empty array?",
- upvotes: 24,
- isResolved: true,
- isAnonymous: false,
- replies: [
- {
- id: "a2",
- type: "comment",
- user: { username: "Jordan Lee", pfp: "", role: "TA" },
- timestamp: "2:20 PM",
- content:
- "Great question! When the array is empty, the base case triggers immediately since low > high, and we return -1.",
- upvotes: 8,
- isAnonymous: false,
- },
- ],
- },
- {
- id: "q3",
- type: "question",
- user: null,
- timestamp: "2:22 PM",
- content: "Is this topic going to be on the midterm?",
- upvotes: 7,
- isResolved: false,
- isAnonymous: true,
- replies: [],
- },
- {
- id: "q4",
- type: "question",
- user: { username: "Marcus Johnson", pfp: "", role: "STUDENT" },
- timestamp: "2:25 PM",
- content: "What's the time complexity of merge sort vs quicksort in the worst case?",
- upvotes: 15,
- isResolved: false,
- isAnonymous: false,
- replies: [
- {
- id: "a3",
- type: "comment",
- user: { username: "Jordan Lee", pfp: "", role: "TA" },
- timestamp: "2:27 PM",
- content:
- "Merge sort is always O(n log n), while quicksort degrades to O(n²) when the pivot is poorly chosen.",
- upvotes: 11,
- isAnonymous: false,
- },
- ],
- },
-];
-
-// ---------------------------------------------------------------------------
-// Shared: Mock nav bar
-// ---------------------------------------------------------------------------
-
-function MockNav() {
- return (
-
-
- AskEasy
-
- My Lectures
-
-
-
- JW
-
-
- );
-}
-
-// ---------------------------------------------------------------------------
-// Shared: Chat header
-// ---------------------------------------------------------------------------
-
-function MockChatHeader({
- title,
- answerMode,
-}: {
- title: string;
- answerMode?: "all" | "instructors_only";
-}) {
- const mode = answerMode ?? "instructors_only";
- return (
-
- );
-}
-
-// ---------------------------------------------------------------------------
-// Shared: Question list
-// ---------------------------------------------------------------------------
-
-function MockQuestionList({
- questions,
- commentView,
-}: {
- questions: Question[];
- commentView: string;
-}) {
- return (
-
- {questions
- .filter((q) => {
- if (commentView === "unresolved") return !q.isResolved;
- if (commentView === "resolved") return q.isResolved;
- return true;
- })
- .map((q) => (
- {}}
- onResolve={() => {}}
- onDelete={() => {}}
- onSubmitAnswer={() => {}}
- renderReply={(reply) => (
- {}}
- onDelete={() => {}}
- />
- )}
- />
- ))}
-
- );
-}
-
-// ---------------------------------------------------------------------------
-// Shared: Mock PDF slide
-// ---------------------------------------------------------------------------
-
-function MockSlide() {
- return (
-
-
-
-
- Lecture 5: Recursion & Binary Search
-
-
CSC108 — Introduction to Computer Science
-
-
-
-
Binary Search Algorithm
-
- -
- Works on sorted arrays only
-
- - Repeatedly divides the search interval in half
- - Compare target with the middle element
- -
- Time complexity:{" "}
-
- O(log n)
-
-
-
-
-
-
# Python
-
- def{" "}
- binary_search(arr, target):
-
-
- low, high = 0,{" "}
- len(arr) -{" "}
- 1
-
-
- while low <= high:
-
-
- mid = (low + high) // 2
-
-
- if arr[mid] == target:{" "}
- return mid
-
-
- elif arr[mid] < target: low = mid +{" "}
- 1
-
-
- else: high = mid -{" "}
- 1
-
-
- return -
- 1
-
-
-
-
- Prof. Williams
- 5 / 24
-
-
-
- );
-}
-
-// ===================================================================
-// SCENE 1: "Go Live & Share Slides" — Prof dashboard
-// ===================================================================
-
-function Scene_GoLive() {
- const courses = [
- { id: "1", name: "CSC108", full: "Introduction to Computer Science", semester: "Winter 2026" },
- { id: "2", name: "CSC148", full: "Data Structures", semester: "Winter 2026" },
- {
- id: "3",
- name: "CSC236",
- full: "Introduction to the Theory of Computation",
- semester: "Winter 2026",
- },
- ];
-
- return (
-
-
-
-
-
-
- My Lectures
-
-
Manage your lectures and start live sessions.
-
-
-
-
- {courses.map((course, i) => (
-
-
-
-
- {course.name}
-
-
{course.full}
-
-
-
- {course.semester}
-
-
-
-
-
-
- ))}
-
-
-
- );
-}
-
-// ===================================================================
-// SCENE 2: "Engage With Your Class" — Live split view
-// ===================================================================
-
-function Scene_Interactive() {
- const [commentView, setCommentView] = useState<"all" | "unresolved" | "resolved">("all");
- const [isAnonymous, setIsAnonymous] = useState(false);
-
- return (
-
- {/* Slide panel */}
-
-
-
-
-
- Live
-
-
-
- 47
-
-
-
-
5 / 24
-
-
-
-
-
-
-
-
- {/* Chat panel */}
-
-
-
-
{}}
- isAnonymous={isAnonymous}
- onAnonymousChange={setIsAnonymous}
- />
-
-
- );
-}
-
-// ===================================================================
-// SCENE 3: "Manage & Export" — End session modal
-// ===================================================================
-
-function Scene_ManageExport() {
- const [commentView, setCommentView] = useState<"all" | "unresolved" | "resolved">("all");
- const [isAnonymous, setIsAnonymous] = useState(false);
-
- return (
-
- {/* Session view behind the modal */}
-
-
-
-
-
-
- Live
-
-
-
- 47
-
-
-
-
-
-
-
-
-
-
{}}
- isAnonymous={isAnonymous}
- onAnonymousChange={setIsAnonymous}
- />
-
-
-
- {/* Modal overlay */}
-
-
-
-
End Session
-
- Would you like to download the chat history for{" "}
- CSC108 Live Session before ending?
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-// ---------------------------------------------------------------------------
-// Main page
-// ---------------------------------------------------------------------------
-
-type Scene = 1 | 2 | 3;
-const SCENE_LABELS: Record = {
- 1: "1. Go Live",
- 2: "2. Engage With Your Class",
- 3: "3. Manage & Export",
-};
-
-export default function DemoProfPage() {
- const [scene, setScene] = useState(1);
-
- return (
-
- {/* Minimal scene switcher — hide this before screenshotting */}
-
-
- Prof Onboarding:
-
- {([1, 2, 3] as Scene[]).map((s) => (
-
- ))}
-
-
- {/* Scene — fills remaining height */}
-
- {scene === 1 && }
- {scene === 2 && }
- {scene === 3 && }
-
-
- );
-}
diff --git a/src/app/demo/page.tsx b/src/app/demo/page.tsx
deleted file mode 100644
index bc1bdcd..0000000
--- a/src/app/demo/page.tsx
+++ /dev/null
@@ -1,758 +0,0 @@
-"use client";
-
-import { useState } from "react";
-import {
- Monitor,
- BookOpen,
- Calendar,
- ArrowRight,
- Radio,
- Settings,
- Square,
- PanelRightClose,
- Users,
- GraduationCap,
- Search,
- Download,
- X,
-} from "lucide-react";
-
-import type { Question, Comment } from "@/utils/types";
-import QuestionPost from "@/app/room/classChat/post/QuestionPost";
-import CommentPost from "@/app/room/classChat/post/CommentPost";
-import FilterTabs from "@/app/room/classChat/FilterTabs";
-import ChatInput from "@/app/room/classChat/ChatInput";
-
-// ---------------------------------------------------------------------------
-// Mock data
-// ---------------------------------------------------------------------------
-
-const MOCK_QUESTIONS: Question[] = [
- {
- id: "q1",
- type: "question",
- user: { username: "Sarah Chen", pfp: "", role: "STUDENT" },
- timestamp: "2:14 PM",
- content:
- "Can you go over that last example one more time? I didn't quite follow the recursion step.",
- upvotes: 12,
- isResolved: false,
- isAnonymous: false,
- replies: [
- {
- id: "a1",
- type: "comment",
- user: { username: "Prof. Williams", pfp: "", role: "PROFESSOR" },
- timestamp: "2:16 PM",
- content:
- "Sure! The key idea is that each recursive call reduces the problem size by half. I'll draw it out on the board in a moment.",
- upvotes: 5,
- isAnonymous: false,
- },
- ],
- },
- {
- id: "q2",
- type: "question",
- user: { username: "Alex Kim", pfp: "", role: "STUDENT" },
- timestamp: "2:18 PM",
- content: "How does binary search handle edge cases like an empty array?",
- upvotes: 24,
- isResolved: true,
- isAnonymous: false,
- replies: [
- {
- id: "a2",
- type: "comment",
- user: { username: "Jordan Lee", pfp: "", role: "TA" },
- timestamp: "2:20 PM",
- content:
- "Great question! When the array is empty, the base case triggers immediately since low > high, and we return -1. This is why checking the base case first is so important.",
- upvotes: 8,
- isAnonymous: false,
- },
- ],
- },
- {
- id: "q3",
- type: "question",
- user: null,
- timestamp: "2:22 PM",
- content: "Is this topic going to be on the midterm?",
- upvotes: 7,
- isResolved: false,
- isAnonymous: true,
- replies: [],
- },
- {
- id: "q4",
- type: "question",
- user: { username: "Marcus Johnson", pfp: "", role: "STUDENT" },
- timestamp: "2:25 PM",
- content: "What's the time complexity of merge sort vs quicksort in the worst case?",
- upvotes: 15,
- isResolved: false,
- isAnonymous: false,
- replies: [
- {
- id: "a3",
- type: "comment",
- user: { username: "Jordan Lee", pfp: "", role: "TA" },
- timestamp: "2:27 PM",
- content:
- "Merge sort is always O(n log n) in the worst case, while quicksort degrades to O(n\u00B2) when the pivot is poorly chosen (e.g. already sorted data with first-element pivot).",
- upvotes: 11,
- isAnonymous: false,
- },
- ],
- },
- {
- id: "q5",
- type: "question",
- user: { username: "Emily Park", pfp: "", role: "STUDENT" },
- timestamp: "2:30 PM",
- content: "Could you show a visual example of how the call stack looks during recursion?",
- upvotes: 9,
- isResolved: false,
- isAnonymous: false,
- replies: [],
- },
-];
-
-// ---------------------------------------------------------------------------
-// Shared: Chat header (inline — avoids context deps)
-// ---------------------------------------------------------------------------
-
-function MockChatHeader({
- title,
- role,
- answerMode,
-}: {
- title: string;
- role: "STUDENT" | "PROFESSOR";
- answerMode?: "all" | "instructors_only";
-}) {
- const mode = answerMode ?? "instructors_only";
- return (
-
- );
-}
-
-// ---------------------------------------------------------------------------
-// Shared: Question list
-// ---------------------------------------------------------------------------
-
-function MockQuestionList({
- questions,
- commentView,
- role,
-}: {
- questions: Question[];
- commentView: string;
- role: "STUDENT" | "PROFESSOR";
-}) {
- return (
-
- {questions
- .filter((q) => {
- if (commentView === "unresolved") return !q.isResolved;
- if (commentView === "resolved") return q.isResolved;
- return true;
- })
- .map((q) => (
- {}}
- onResolve={role === "PROFESSOR" ? () => {} : undefined}
- onDelete={role === "PROFESSOR" ? () => {} : undefined}
- onSubmitAnswer={() => {}}
- renderReply={(reply) => (
- {}}
- onDelete={role === "PROFESSOR" ? () => {} : undefined}
- />
- )}
- />
- ))}
-
- );
-}
-
-// ---------------------------------------------------------------------------
-// Shared: Mock PDF slide
-// ---------------------------------------------------------------------------
-
-function MockSlide() {
- return (
-
-
-
-
- Lecture 5: Recursion & Binary Search
-
-
CSC108 — Introduction to Computer Science
-
-
-
-
Binary Search Algorithm
-
- -
- Works on sorted arrays only
-
- - Repeatedly divides the search interval in half
- - Compare target with the middle element
- -
- Time complexity:{" "}
-
- O(log n)
-
-
-
-
-
-
# Python
-
- def{" "}
- binary_search(arr, target):
-
-
- low, high = 0,{" "}
- len(arr) -{" "}
- 1
-
-
- while low <= high:
-
-
- mid = (low + high) // 2
-
-
- if arr[mid] == target:
-
-
- return mid
-
-
- elif arr[mid] < target:
-
-
- low = mid + 1
-
-
- else:
-
-
- high = mid - 1
-
-
- return -
- 1
-
-
-
-
Key Insight
-
- Each recursive call{" "}
- reduces the problem size by half
- . For an array of 1,000,000 elements, we need at most ~20 comparisons.
-
-
-
-
- Prof. Williams
- 5 / 24
-
-
-
- );
-}
-
-// ===================================================================
-// SCENE 1: "Welcome to AskEasy" — Student dashboard, 1 live lecture
-// ===================================================================
-
-function Scene_JoinClass() {
- const courses = [
- { id: "1", name: "CSC108", semester: "Winter 2026", isActive: true },
- { id: "2", name: "MAT137", semester: "Winter 2026", isActive: false },
- { id: "3", name: "CSC148", semester: "Winter 2026", isActive: false },
- ];
-
- return (
-
-
-
-
My Lectures
-
- Select a live lecture to join its session. If the lecture is not live, just wait!
-
-
-
-
- {courses.map((course) => (
-
-
-
- {course.isActive ? (
-
- ) : (
-
- )}
-
- {course.isActive && (
-
-
- LIVE
-
- )}
-
-
-
- {course.name}
-
-
-
-
- {course.semester}
- {course.isActive && (
-
- )}
-
-
- ))}
-
-
- );
-}
-
-// ===================================================================
-// SCENE 2: "Ask & Get Answers Live" — Q&A with questions + TA/prof replies
-// ===================================================================
-
-function Scene_AskQuestions() {
- const [commentView, setCommentView] = useState<"all" | "unresolved" | "resolved">("all");
- const [isAnonymous, setIsAnonymous] = useState(false);
-
- return (
-
-
-
-
{}} isAnonymous={isAnonymous} onAnonymousChange={setIsAnonymous} />
-
- );
-}
-
-// ===================================================================
-// SCENE 3: "Upvote & Track Questions" — upvotes, resolved, filters
-// ===================================================================
-
-function Scene_Upvote() {
- const [commentView, setCommentView] = useState<"all" | "unresolved" | "resolved">("all");
- const [isAnonymous, setIsAnonymous] = useState(false);
-
- // Show all 5 questions — mix of resolved and unresolved, various upvote counts
- return (
-
-
-
-
{}} isAnonymous={isAnonymous} onAnonymousChange={setIsAnonymous} />
-
- );
-}
-
-// ===================================================================
-// SCENE 4: "Stay Anonymous" — anonymous toggle ON, anonymous question visible
-// ===================================================================
-
-function Scene_Anonymous() {
- const [commentView, setCommentView] = useState<"all" | "unresolved" | "resolved">("all");
-
- // Show the chat input area with anonymous mode ON prominently
- return (
-
-
-
- {/* Anonymous mode ON */}
-
{}} isAnonymous={true} onAnonymousChange={() => {}} />
-
- );
-}
-
-// ===================================================================
-// SCENE 5: "Go Live & Share Slides" — Prof dashboard + split view
-// ===================================================================
-
-function Scene_GoLive() {
- const courses = [
- { id: "1", name: "CSC108", semester: "Winter 2026", isActive: false },
- { id: "2", name: "CSC148", semester: "Winter 2026", isActive: false },
- { id: "3", name: "CSC236", semester: "Winter 2026", isActive: false },
- ];
-
- return (
-
-
-
-
My Lectures
-
Manage your lectures and start live sessions.
-
-
-
- {courses.map((course) => (
-
-
-
-
- {course.name}
-
-
-
-
- {course.semester}
-
-
-
-
-
-
- ))}
-
-
- );
-}
-
-// ===================================================================
-// SCENE 6: "Engage With Your Class" — Prof chat with answer mode toggle
-// ===================================================================
-
-function Scene_Interactive() {
- const [commentView, setCommentView] = useState<"all" | "unresolved" | "resolved">("all");
- const [isAnonymous, setIsAnonymous] = useState(false);
-
- return (
-
- {/* Slide side */}
-
-
-
-
-
- Live
-
-
-
- 47
-
-
-
-
5 / 24
-
-
-
-
-
-
- {/* Chat side — professor view with answer mode */}
-
-
-
-
{}}
- isAnonymous={isAnonymous}
- onAnonymousChange={setIsAnonymous}
- />
-
-
- );
-}
-
-// ===================================================================
-// SCENE 7: "Manage & Export" — End session modal + chat behind it
-// ===================================================================
-
-function Scene_ManageExport() {
- const [commentView, setCommentView] = useState<"all" | "unresolved" | "resolved">("all");
- const [isAnonymous, setIsAnonymous] = useState(false);
-
- return (
-
- {/* Chat behind modal (dimmed) */}
-
-
-
-
-
-
- Live
-
-
-
- 47
-
-
-
-
-
-
-
-
-
-
{}}
- isAnonymous={isAnonymous}
- onAnonymousChange={setIsAnonymous}
- />
-
-
-
- {/* End session modal overlay */}
-
-
-
-
End Session
-
- Would you like to download the chat history for{" "}
- CSC108 Live Session before ending?
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-// ---------------------------------------------------------------------------
-// Scenes config
-// ---------------------------------------------------------------------------
-
-type Scene =
- | "s1-join"
- | "s2-ask"
- | "s3-upvote"
- | "s4-anonymous"
- | "p1-golive"
- | "p2-interactive"
- | "p3-manage";
-
-const SCENES: { key: Scene; label: string; group: string }[] = [
- { key: "s1-join", label: "1. Join Class", group: "Student" },
- { key: "s2-ask", label: "2. Ask & Answer", group: "Student" },
- { key: "s3-upvote", label: "3. Upvote & Track", group: "Student" },
- { key: "s4-anonymous", label: "4. Anonymous", group: "Student" },
- { key: "p1-golive", label: "1. Go Live", group: "Professor" },
- { key: "p2-interactive", label: "2. Engage", group: "Professor" },
- { key: "p3-manage", label: "3. Manage & Export", group: "Professor" },
-];
-
-// ---------------------------------------------------------------------------
-// Main Demo Page
-// ---------------------------------------------------------------------------
-
-export default function DemoPage() {
- const [scene, setScene] = useState("s1-join");
-
- return (
-
- {/* Scene selector bar */}
-
-
-
- Student:
-
- {SCENES.filter((s) => s.group === "Student").map((s) => (
-
- ))}
-
-
- Prof:
-
- {SCENES.filter((s) => s.group === "Professor").map((s) => (
-
- ))}
-
-
-
- {/* Scene content */}
-
- {scene === "s1-join" && }
- {scene === "s2-ask" && }
- {scene === "s3-upvote" && }
- {scene === "s4-anonymous" && }
- {scene === "p1-golive" && }
- {scene === "p2-interactive" && }
- {scene === "p3-manage" && }
-
-
- );
-}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 87e371b..8b4f41a 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -15,6 +15,26 @@ const geistMono = Geist_Mono({
export const metadata: Metadata = {
title: "AskEasy",
description: "Real-time classroom Q&A and lecture platform",
+ keywords: [
+ "classroom",
+ "Q&A",
+ "questions",
+ "answers",
+ "lecture",
+ "interactive learning",
+ "student engagement",
+ "real-time",
+ ],
+ authors: [{ name: "AskEasy Team" }],
+ robots: {
+ index: true,
+ follow: true,
+ nocache: false,
+ googleBot: {
+ index: true,
+ follow: true,
+ },
+ },
icons: {
icon: "/icon.png",
apple: "/icon.png",
diff --git a/src/app/room/classChat/ChatHeader.tsx b/src/app/room/classChat/ChatHeader.tsx
index 6513aa2..003f5bf 100644
--- a/src/app/room/classChat/ChatHeader.tsx
+++ b/src/app/room/classChat/ChatHeader.tsx
@@ -2,20 +2,11 @@
import { Input } from "@/components/ui/input";
import { useContext, useState } from "react";
-import {
- PanelRightClose,
- Users,
- GraduationCap,
- Search,
- X,
- ArrowBigRight,
- UserPlus,
-} from "lucide-react";
+import { PanelRightClose, Users, GraduationCap, Search, X, UserPlus } from "lucide-react";
import ManageTAsModal from "./ManageTAsModal";
import { useMediaQuery } from "@/hooks/use-media-query";
import { SlideUpdateContext } from "../SlideUpdateContext";
import type { Role } from "@/utils/types";
-import Link from "next/link";
interface ChatHeaderProps {
role: Role;
@@ -176,14 +167,6 @@ export default function ChatHeader({
)}
-
- Back
-
-
>
)}