Initial commit: Sarthi Lab desktop application

This commit is contained in:
2026-03-11 03:59:38 +05:30
commit bb1ec0a584
49 changed files with 15191 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
import { useState, useEffect } from 'react';
import useLabStore from '../store/labStore';
import { studentLogin, getRoomClasses } from '../services/labApi';
export default function StudentLoginScreen() {
const { serverUrl, roomToken, sessionToken, labInfo, setStudent } = useLabStore();
const [rollNumber, setRollNumber] = useState('');
const [className, setClassName] = useState('');
const [classes, setClasses] = useState([]);
const [loadingClasses, setLoadingClasses] = useState(true);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
useEffect(() => {
async function fetchClasses() {
try {
const list = await getRoomClasses(serverUrl, roomToken);
setClasses(list);
if (list.length === 1) setClassName(list[0]);
} catch {
} finally {
setLoadingClasses(false);
}
}
fetchClasses();
}, [serverUrl, roomToken]);
const handleLogin = async (e) => {
e.preventDefault();
setLoading(true);
setError('');
try {
const student = await studentLogin(serverUrl, sessionToken, rollNumber.trim(), className);
setStudent(student);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen bg-mesh flex flex-col items-center justify-center p-6">
<div className="w-full max-w-sm animate-slide-up">
<div className="text-center mb-8">
<div className="w-16 h-16 mx-auto mb-4 rounded-2xl avatar-gradient flex items-center justify-center accent-glow-sm">
<svg className="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
</svg>
</div>
<h2 className="text-2xl font-bold text-white tracking-tight">{labInfo?.lab_name}</h2>
<p className="text-slate-400 text-sm mt-1">{labInfo?.school_name}</p>
<p className="text-slate-500 text-xs mt-3">Enter your details to start learning</p>
</div>
<form onSubmit={handleLogin} className="glass-strong rounded-2xl p-7 space-y-5">
<div>
<label className="block text-xs font-medium text-slate-400 uppercase tracking-wider mb-2">Roll Number</label>
<input
type="text"
value={rollNumber}
onChange={e => setRollNumber(e.target.value)}
className="w-full bg-white/[0.05] border border-white/[0.08] text-white rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--accent)]/40 focus:border-[var(--accent)]/40 transition-all placeholder:text-slate-600"
placeholder="e.g. 42"
required
autoFocus
/>
</div>
<div>
<label className="block text-xs font-medium text-slate-400 uppercase tracking-wider mb-2">Class</label>
{!loadingClasses && classes.length > 0 ? (
<select
value={className}
onChange={e => setClassName(e.target.value)}
className="w-full bg-white/[0.05] border border-white/[0.08] text-white rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--accent)]/40 focus:border-[var(--accent)]/40 transition-all appearance-none"
required
>
<option value="" className="bg-slate-900">Select your class...</option>
{classes.map(c => (
<option key={c} value={c} className="bg-slate-900">{c}</option>
))}
</select>
) : (
<input
type="text"
value={className}
onChange={e => setClassName(e.target.value)}
className="w-full bg-white/[0.05] border border-white/[0.08] text-white rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--accent)]/40 focus:border-[var(--accent)]/40 transition-all placeholder:text-slate-600"
placeholder={loadingClasses ? 'Loading classes...' : 'e.g. Grade 10 - A'}
required
disabled={loadingClasses}
/>
)}
</div>
{error && (
<div className="bg-red-500/10 border border-red-500/20 text-red-400 rounded-xl px-4 py-3 text-sm animate-fade-in">
{error}
</div>
)}
<button
type="submit"
disabled={loading || loadingClasses}
className="w-full accent-gradient accent-glow disabled:opacity-50 text-white font-semibold rounded-xl py-3.5 transition-all"
>
{loading ? (
<span className="flex items-center justify-center gap-2">
<span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
Logging in...
</span>
) : 'Start Learning'}
</button>
</form>
</div>
</div>
);
}