I got an new assignment to complete in 2 day i will share it here please throughly guide me how to complete it step by step
ffirst cover high level design, folder structure, purpose of each file, minimalist and simple approach
dont worry about scaling for now, I will do that just focus on system design and data flow
if its too big break it into pices but cover every detail dont miss basics
Design link https://www.figma.com/design/uhinheFgWssbxvlI7wtf59/Intervue-Assigm ent--Poll-system?node-id=0-1&t=Y5GfjIgQte8VUTgA-1 Task is to create a live polling system with 2 personas
I'll help you build this live polling system step by step! Let me break this down into a comprehensive guide.
polling-system/
├── client/ # React Frontend
│ ├── src/
│ │ ├── components/
│ │ │ ├── Teacher/
│ │ │ │ ├── TeacherDashboard.jsx
│ │ │ │ ├── CreatePoll.jsx
│ │ │ │ ├── LiveResults.jsx
│ │ │ │ └── PollControls.jsx
│ │ │ ├── Student/
│ │ │ │ ├── StudentInterface.jsx
│ │ │ │ ├── NameEntry.jsx
│ │ │ │ ├── AnswerQuestion.jsx
│ │ │ │ └── ResultsView.jsx
│ │ │ ├── Common/
│ │ │ │ ├── Timer.jsx
│ │ │ │ ├── PollResults.jsx
│ │ │ │ └── Layout.jsx
│ │ │ └── Chat/
│ │ │ └── ChatPopup.jsx
│ │ ├── hooks/
│ │ │ ├── useSocket.js
│ │ │ ├── useTimer.js
│ │ │ └── useLocalStorage.js
│ │ ├── store/ # Redux store (optional)
│ │ │ ├── pollSlice.js
│ │ │ ├── studentSlice.js
│ │ │ └── store.js
│ │ ├── utils/
│ │ │ ├── socketEvents.js
│ │ │ └── constants.js
│ │ ├── App.jsx
│ │ └── main.jsx
│ ├── package.json
│ └── vite.config.js
├── server/ # Express Backend
│ ├── src/
│ │ ├── controllers/
│ │ │ ├── pollController.js
│ │ │ └── studentController.js
│ │ ├── managers/
│ │ │ ├── PollManager.js
│ │ │ ├── SessionManager.js
│ │ │ └── TimerManager.js
│ │ ├── models/
│ │ │ ├── Poll.js
│ │ │ └── Student.js
│ │ ├── socket/
│ │ │ ├── socketHandlers.js
│ │ │ └── socketEvents.js
│ │ ├── utils/
│ │ │ └── constants.js
│ │ ├── app.js
│ │ └── server.js
│ ├── package.json
│ └── .env
└── README.md
// server/src/app.js
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const cors = require('cors');
const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
cors: {
origin: "http://localhost:5173",
methods: ["GET", "POST"]
}
});
app.use(cors());
app.use(express.json());
module.exports = { app, server, io };
// server/src/managers/PollManager.js
class PollManager {
constructor() {
this.currentPoll = null;
this.pollHistory = [];
this.answers = new Map(); // studentId -> answer
}
createPoll(question, options, timeLimit = 60) {
this.currentPoll = {
id: Date.now(),
question,
options,
timeLimit,
startTime: Date.now(),
status: 'active'
};
this.answers.clear();
return this.currentPoll;
}
submitAnswer(studentId, answer) {
if (!this.currentPoll || this.currentPoll.status !== 'active') {
return false;
}
this.answers.set(studentId, answer);
return true;
}
getResults() {
const results = {};
this.currentPoll?.options.forEach(option => {
results[option] = 0;
});
this.answers.forEach(answer => {
if (results.hasOwnProperty(answer)) {
results[answer]++;
}
});
return {
question: this.currentPoll?.question,
results,
totalAnswers: this.answers.size
};
}
canCreateNewPoll(totalStudents) {
return !this.currentPoll ||
this.currentPoll.status === 'completed' ||
this.answers.size === totalStudents;
}
endPoll() {
if (this.currentPoll) {
this.currentPoll.status = 'completed';
this.pollHistory.push({
...this.currentPoll,
results: this.getResults(),
endTime: Date.now()
});
}
}
}
// server/src/socket/socketHandlers.js
const PollManager = require('../managers/PollManager');
const SessionManager = require('../managers/SessionManager');
const pollManager = new PollManager();
const sessionManager = new SessionManager();
function handleConnection(io, socket) {
console.log('User connected:', socket.id);
// Teacher joins
socket.on('join-as-teacher', () => {
socket.join('teachers');
socket.emit('poll-state', {
currentPoll: pollManager.currentPoll,
results: pollManager.getResults(),
canCreatePoll: pollManager.canCreateNewPoll(sessionManager.getStudentCount())
});
});
// Student joins
socket.on('join-as-student', (studentName) => {
const student = sessionManager.addStudent(socket.id, studentName);
socket.join('students');
socket.emit('joined-successfully', student);
// Notify teachers about student count
io.to('teachers').emit('student-count-updated', sessionManager.getStudentCount());
// Send current poll if active
if (pollManager.currentPoll && pollManager.currentPoll.status === 'active') {
socket.emit('new-question', pollManager.currentPoll);
}
});
// Teacher creates poll
socket.on('create-poll', (pollData) => {
if (pollManager.canCreateNewPoll(sessionManager.getStudentCount())) {
const poll = pollManager.createPoll(
pollData.question,
pollData.options,
pollData.timeLimit
);
// Broadcast to all students
io.to('students').emit('new-question', poll);
// Update teachers
io.to('teachers').emit('poll-created', poll);
// Start timer
setTimeout(() => {
pollManager.endPoll();
const results = pollManager.getResults();
io.emit('poll-ended', results);
}, poll.timeLimit * 1000);
}
});
// Student submits answer
socket.on('submit-answer', (answer) => {
const student = sessionManager.getStudent(socket.id);
if (student && pollManager.submitAnswer(student.id, answer)) {
// Send results to this student
socket.emit('answer-submitted', pollManager.getResults());
// Update live results for teachers
io.to('teachers').emit('live-results-update', pollManager.getResults());
// Check if all students answered
if (pollManager.answers.size === sessionManager.getStudentCount()) {
pollManager.endPoll();
io.emit('poll-ended', pollManager.getResults());
}
}
});
socket.on('disconnect', () => {
sessionManager.removeStudent(socket.id);
io.to('teachers').emit('student-count-updated', sessionManager.getStudentCount());
});
}
module.exports = handleConnection;
// client/src/hooks/useSocket.js
import { useEffect, useRef } from 'react';
import io from 'socket.io-client';
export const useSocket = (serverUrl = 'http://localhost:3001') => {
const socketRef = useRef(null);
useEffect(() => {
socketRef.current = io(serverUrl);
return () => {
if (socketRef.current) {
socketRef.current.disconnect();
}
};
}, [serverUrl]);
return socketRef.current;
};
// client/src/App.jsx
import { useState, useEffect } from 'react';
import { useSocket } from './hooks/useSocket';
import TeacherDashboard from './components/Teacher/TeacherDashboard';
import StudentInterface from './components/Student/StudentInterface';
function App() {
const [userType, setUserType] = useState(null);
const socket = useSocket();
const handleRoleSelection = (role) => {
setUserType(role);
};
if (!userType) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<div className="bg-white p-8 rounded-lg shadow-md">
<h1 className="text-2xl font-bold mb-6 text-center">Live Polling System</h1>
<div className="space-y-4">
<button
onClick={() => handleRoleSelection('teacher')}
className="w-full bg-blue-500 text-white py-3 px-6 rounded-lg hover:bg-blue-600"
>
Join as Teacher
</button>
<button
onClick={() => handleRoleSelection('student')}
className="w-full bg-green-500 text-white py-3 px-6 rounded-lg hover:bg-green-600"
>
Join as Student
</button>
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-100">
{userType === 'teacher' ? (
<TeacherDashboard socket={socket} />
) : (
<StudentInterface socket={socket} />
)}
</div>
);
}
export default App;
// client/src/components/Teacher/TeacherDashboard.jsx
import { useState, useEffect } from 'react';
import CreatePoll from './CreatePoll';
import LiveResults from './LiveResults';
const TeacherDashboard = ({ socket }) => {
const [currentPoll, setCurrentPoll] = useState(null);
const [results, setResults] = useState(null);
const [studentCount, setStudentCount] = useState(0);
const [canCreatePoll, setCanCreatePoll] = useState(true);
useEffect(() => {
if (!socket) return;
socket.emit('join-as-teacher');
socket.on('poll-state', (state) => {
setCurrentPoll(state.currentPoll);
setResults(state.results);
setCanCreatePoll(state.canCreatePoll);
});
socket.on('student-count-updated', (count) => {
setStudentCount(count);
});
socket.on('live-results-update', (newResults) => {
setResults(newResults);
});
socket.on('poll-ended', (finalResults) => {
setResults(finalResults);
setCanCreatePoll(true);
});
return () => {
socket.off('poll-state');
socket.off('student-count-updated');
socket.off('live-results-update');
socket.off('poll-ended');
};
}, [socket]);
return (
<div className="container mx-auto p-6">
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
<h1 className="text-3xl font-bold mb-4">Teacher Dashboard</h1>
<div className="flex justify-between items-center mb-6">
<div className="text-lg">
Connected Students: <span className="font-bold text-blue-600">{studentCount}</span>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<CreatePoll
socket={socket}
canCreate={canCreatePoll}
studentCount={studentCount}
/>
<LiveResults
currentPoll={currentPoll}
results={results}
/>
</div>
</div>
);
};
export default TeacherDashboard;
// client/src/components/Student/StudentInterface.jsx
import { useState, useEffect } from 'react';
import NameEntry from './NameEntry';
import AnswerQuestion from './AnswerQuestion';
import ResultsView from './ResultsView';
const StudentInterface = ({ socket }) => {
const [studentName, setStudentName] = useState('');
const [isJoined, setIsJoined] = useState(false);
const [currentQuestion, setCurrentQuestion] = useState(null);
const [hasAnswered, setHasAnswered] = useState(false);
const [results, setResults] = useState(null);
useEffect(() => {
// Check localStorage for existing name
const savedName = localStorage.getItem('student-name');
if (savedName) {
setStudentName(savedName);
joinAsStudent(savedName);
}
}, []);
useEffect(() => {
if (!socket) return;
socket.on('joined-successfully', (student) => {
setIsJoined(true);
});
socket.on('new-question', (poll) => {
setCurrentQuestion(poll);
setHasAnswered(false);
setResults(null);
});
socket.on('answer-submitted', (pollResults) => {
setHasAnswered(true);
setResults(pollResults);
});
socket.on('poll-ended', (finalResults) => {
setResults(finalResults);
setCurrentQuestion(null);
});
return () => {
socket.off('joined-successfully');
socket.off('new-question');
socket.off('answer-submitted');
socket.off('poll-ended');
};
}, [socket]);
const joinAsStudent = (name) => {
localStorage.setItem('student-name', name);
setStudentName(name);
socket.emit('join-as-student', name);
};
const submitAnswer = (answer) => {
socket.emit('submit-answer', answer);
};
if (!isJoined) {
return <NameEntry onSubmit={joinAsStudent} />;
}
return (
<div className="container mx-auto p-6">
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
<h1 className="text-2xl font-bold">Welcome, {studentName}!</h1>
</div>
{currentQuestion && !hasAnswered ? (
<AnswerQuestion
question={currentQuestion}
onSubmit={submitAnswer}
/>
) : results ? (
<ResultsView results={results} />
) : (
<div className="bg-white rounded-lg shadow-md p-8 text-center">
<h2 className="text-xl text-gray-600">Waiting for the next question...</h2>
</div>
)}
</div>
);
};
export default StudentInterface;
This structure gives you a solid foundation for building the live polling system. Each component has a clear purpose, and the data flow is designed for real-time synchronization. Would you like me to dive deeper into any specific part or help you implement any particular component?