Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
4 views16 pages

Components Services

The document outlines essential React components and services for a web application, including an Authentication Component for user login and registration, a Student Dashboard for displaying lessons and progress, and a Lesson Viewer for lesson content and quizzes. It also details the Authentication Service for handling user authentication via API calls. Each component is designed to manage state, handle user interactions, and provide localized content based on user preferences.

Uploaded by

gokulsrg3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views16 pages

Components Services

The document outlines essential React components and services for a web application, including an Authentication Component for user login and registration, a Student Dashboard for displaying lessons and progress, and a Lesson Viewer for lesson content and quizzes. It also details the Authentication Service for handling user authentication via API calls. Each component is designed to manage state, handle user interactions, and provide localized content based on user preferences.

Uploaded by

gokulsrg3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

Essential React Components & Services

Frontend Components

1. Authentication Component (src/components/auth/Login.js)

import React, { useState } from 'react';


import { useTranslation } from 'react-i18next';
import { AuthService } from '../../services/authService';
import './Login.css';

const Login = ({ onLogin }) => {


const [formData, setFormData] = useState({
username: '',
password: '',
isRegistering: false,
full_name: '',
grade_level: '',
role: 'student'
});
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const { t } = useTranslation();

const handleSubmit = async (e) => {


e.preventDefault();
setLoading(true);
setError('');

try {
let result;
if (formData.isRegistering) {
result = await AuthService.register(formData);
} else {
result = await AuthService.login(formData.username, formData.password);
}

localStorage.setItem('token', result.token);
onLogin(result.user);
} catch (error) {
setError(error.message || t('login_error'));
} finally {
setLoading(false);
}
};

const handleInputChange = (e) => {


setFormData({
...formData,
[e.target.name]: e.target.value
});
};

return (
<div className="login-container">
<div className="login-card">
<h2>{formData.isRegistering ? t('register') : t('login')}</h2>

{error && <div className="error-message">{error}</div>}

<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
placeholder={t('username')}
value={formData.username}
onChange={handleInputChange}
required
/>

<input
type="password"
name="password"
placeholder={t('password')}
value={formData.password}
onChange={handleInputChange}
required
/>

{formData.isRegistering && (
<>
<input
type="text"
name="full_name"
placeholder={t('full_name')}
value={formData.full_name}
onChange={handleInputChange}
required
/>

<select
name="role"
value={formData.role}
onChange={handleInputChange}
>
<option value="student">{t('student')}</option>
<option value="teacher">{t('teacher')}</option>
</select>

{formData.role === 'student' && (


<select
name="grade_level"
value={formData.grade_level}
onChange={handleInputChange}
required
>
<option value="">{t('select_grade')}</option>
{[1,2,3,4,5,6,7,8,9,10].map(grade => (
<option key={grade} value={grade.toString()}>{t('grade')} {grade}</op
))}
</select>
)}
</>
)}

<button type="submit" disabled={loading} className="submit-btn">


{loading ? t('loading') : (formData.isRegistering ? t('register') : t('login'
</button>
</form>

<button
type="button"
onClick={() => setFormData({...formData, isRegistering: !formData.isRegistering
className="toggle-btn"
>
{formData.isRegistering ? t('already_have_account') : t('create_account')}
</button>

{/* Demo Credentials */}


<div className="demo-credentials">
<h4>{t('demo_accounts')}:</h4>
<p><strong>{t('student')}:</strong> student / student123</p>
<p><strong>{t('teacher')}:</strong> teacher / teacher123</p>
</div>
</div>
</div>
);
};

export default Login;

2. Student Dashboard (src/components/student/StudentDashboard.js)

import React, { useState, useEffect } from 'react';


import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { ContentService } from '../../services/contentService';
import { OfflineStorage } from '../../services/offlineStorage';
import VoiceAssistant from './VoiceAssistant';
import ProgressTracker from './ProgressTracker';
import './StudentDashboard.css';

const StudentDashboard = ({ user }) => {


const [lessons, setLessons] = useState([]);
const [progress, setProgress] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedSubject, setSelectedSubject] = useState('all');
const { t, i18n } = useTranslation();

useEffect(() => {
loadDashboardData();
}, []);

const loadDashboardData = async () => {


setLoading(true);
try {
// Try to load from cache first (offline support)
let lessonsData = await OfflineStorage.getLessons();
let progressData = await OfflineStorage.getProgress(user.id);

if (!lessonsData.length) {
// If no cached data, fetch from API
lessonsData = await ContentService.getLessons({ grade_level: user.grade_level });
progressData = await ContentService.getProgress();

// Cache the data


await OfflineStorage.saveLessons(lessonsData);
await OfflineStorage.saveProgress(progressData);
}

setLessons(lessonsData);
setProgress(progressData);
} catch (error) {
console.error('Error loading dashboard:', error);
// Load from offline storage if API fails
const cachedLessons = await OfflineStorage.getLessons();
const cachedProgress = await OfflineStorage.getProgress(user.id);
setLessons(cachedLessons);
setProgress(cachedProgress);
} finally {
setLoading(false);
}
};

const getLocalizedContent = (lesson, field) => {


const lang = i18n.language;
if (lang === 'punjabi' && lesson[`${field}_punjabi`]) {
return lesson[`${field}_punjabi`];
}
if (lang === 'hindi' && lesson[`${field}_hindi`]) {
return lesson[`${field}_hindi`];
}
return lesson[field];
};

const getLessonProgress = (lessonId) => {


const lessonProgress = progress.find(p => p.lesson_id === lessonId);
return lessonProgress ? lessonProgress.completion_percentage : 0;
};

const filteredLessons = selectedSubject === 'all'


? lessons
: lessons.filter(lesson => lesson.subject === selectedSubject);

const subjects = [...new Set(lessons.map(lesson => lesson.subject))];

if (loading) {
return (
<div className="loading-container">
<div className="loading-spinner"></div>
<p>{t('loading_dashboard')}</p>
</div>
);
}

return (
<div className="student-dashboard">
<div className="dashboard-header">
<h2>{t('welcome_message', { name: user.full_name })}</h2>
<div className="dashboard-stats">
<div className="stat-card">
<h3>{lessons.length}</h3>
<p>{t('total_lessons')}</p>
</div>
<div className="stat-card">
<h3>{progress.length}</h3>
<p>{t('lessons_started')}</p>
</div>
<div className="stat-card">
<h3>{progress.filter(p => p.completion_percentage >= 100).length}</h3>
<p>{t('lessons_completed')}</p>
</div>
</div>
</div>

<div className="dashboard-filters">
<select
value={selectedSubject}
onChange={(e) => setSelectedSubject(e.target.value)}
className="subject-filter"
>
<option value="all">{t('all_subjects')}</option>
{subjects.map(subject => (
<option key={subject} value={subject}>{t(subject)}</option>
))}
</select>
</div>

<div className="lessons-grid">
{filteredLessons.map(lesson => (
<div key={lesson.id} className="lesson-card">
<div className="lesson-header">
<h3>{getLocalizedContent(lesson, 'title')}</h3>
<span className="subject-badge">{t(lesson.subject)}</span>
</div>

<p className="lesson-description">
{getLocalizedContent(lesson, 'content')}
</p>

<div className="lesson-progress">
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${getLessonProgress(lesson.id)}%` }}
></div>
</div>
<span>{Math.round(getLessonProgress(lesson.id))}% {t('complete')}</span>
</div>

<Link to={`/lesson/${lesson.id}`} className="start-lesson-btn">


{getLessonProgress(lesson.id) > 0 ? t('continue_lesson') : t('start_lesson'
</Link>
</div>
))}
</div>

{filteredLessons.length === 0 && (


<div className="no-lessons">
<p>{t('no_lessons_available')}</p>
</div>
)}

<ProgressTracker progress={progress} />


<VoiceAssistant />
</div>
);
};

export default StudentDashboard;

3. Lesson Viewer (src/components/student/LessonViewer.js)

import React, { useState, useEffect } from 'react';


import { useParams, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { ContentService } from '../../services/contentService';
import { OfflineStorage } from '../../services/offlineStorage';
import QuizComponent from './QuizComponent';
import './LessonViewer.css';

const LessonViewer = ({ user }) => {


const { id } = useParams();
const navigate = useNavigate();
const [lesson, setLesson] = useState(null);
const [loading, setLoading] = useState(true);
const [currentSection, setCurrentSection] = useState('content');
const [startTime] = useState(Date.now());
const [progress, setProgress] = useState(0);
const { t, i18n } = useTranslation();

useEffect(() => {
loadLesson();
}, [id]);

const loadLesson = async () => {


setLoading(true);
try {
// Try offline first
let lessonData = await OfflineStorage.getLesson(id);

if (!lessonData) {
// Fetch from API if not cached
lessonData = await ContentService.getLesson(id);
await OfflineStorage.saveLesson(lessonData);
}

setLesson(lessonData);
} catch (error) {
console.error('Error loading lesson:', error);
} finally {
setLoading(false);
}
};

const getLocalizedContent = (field) => {


if (!lesson) return '';
const lang = i18n.language;
if (lang === 'punjabi' && lesson[`${field}_punjabi`]) {
return lesson[`${field}_punjabi`];
}
if (lang === 'hindi' && lesson[`${field}_hindi`]) {
return lesson[`${field}_hindi`];
}
return lesson[field];
};

const handleProgressUpdate = async (newProgress, quizScores = null) => {


const timeSpent = Math.floor((Date.now() - startTime) / 1000);

const progressData = {
lesson_id: lesson.id,
completion_percentage: newProgress,
quiz_scores: quizScores,
time_spent: timeSpent
};

try {
// Save online if possible
await ContentService.saveProgress(progressData);
} catch (error) {
// Save offline if online fails
await OfflineStorage.saveProgressOffline(user.id, progressData);
}

setProgress(newProgress);
};

const handleQuizComplete = (scores) => {


const quizCompletion = scores.correct / scores.total * 100;
const totalProgress = Math.max(progress, 50 + (quizCompletion / 2));
handleProgressUpdate(totalProgress, scores);

if (totalProgress >= 100) {


setTimeout(() => {
navigate('/');
}, 2000);
}
};

const markContentComplete = () => {


handleProgressUpdate(Math.max(progress, 50));
setCurrentSection('quiz');
};

if (loading) {
return (
<div className="loading-container">
<div className="loading-spinner"></div>
<p>{t('loading_lesson')}</p>
</div>
);
}

if (!lesson) {
return (
<div className="error-container">
<p>{t('lesson_not_found')}</p>
<button onClick={() => navigate('/')}>{t('go_back')}</button>
</div>
);
}

return (
<div className="lesson-viewer">
<div className="lesson-header">
<button onClick={() => navigate('/')} className="back-btn">
← {t('back_to_dashboard')}
</button>
<h1>{getLocalizedContent('title')}</h1>
<div className="lesson-progress">
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${progress}%` }}
></div>
</div>
<span>{Math.round(progress)}% {t('complete')}</span>
</div>
</div>

<div className="lesson-navigation">
<button
className={`nav-btn ${currentSection === 'content' ? 'active' : ''}`}
onClick={() => setCurrentSection('content')}
>
{t('lesson_content')}
</button>
<button
className={`nav-btn ${currentSection === 'quiz' ? 'active' : ''}`}
onClick={() => setCurrentSection('quiz')}
disabled={progress < 50}
>
{t('quiz')}
</button>
</div>

<div className="lesson-content">
{currentSection === 'content' && (
<div className="content-section">
<div className="lesson-text">
<p>{getLocalizedContent('content')}</p>
</div>

{/* Add multimedia content here */}


{lesson.media_files && (
<div className="media-content">
{/* This would include images, videos, etc. */}
</div>
)}

<div className="content-actions">
<button
onClick={markContentComplete}
className="complete-content-btn"
disabled={progress >= 50}
>
{progress >= 50 ? t('content_completed') : t('mark_complete')}
</button>
</div>
</div>
)}

{currentSection === 'quiz' && lesson.quiz && (


<QuizComponent
quiz={lesson.quiz}
language={i18n.language}
onComplete={handleQuizComplete}
/>
)}

{currentSection === 'quiz' && !lesson.quiz && (


<div className="no-quiz">
<p>{t('no_quiz_available')}</p>
<button onClick={() => handleProgressUpdate(100)}>
{t('complete_lesson')}
</button>
</div>
)}
</div>
</div>
);
};

export default LessonViewer;


4. Services

Authentication Service (src/services/authService.js)

const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:5000/api';

export class AuthService {


static async login(username, password) {
const response = await fetch(`${API_BASE_URL}/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}

return response.json();
}

static async register(userData) {


const response = await fetch(`${API_BASE_URL}/auth/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}

return response.json();
}

static async verifyToken(token) {


// Decode JWT token (simple version)
try {
const payload = JSON.parse(atob(token.split('.')[1]));
if (payload.exp * 1000 < Date.now()) {
throw new Error('Token expired');
}
return payload;
} catch (error) {
throw new Error('Invalid token');
}
}

static logout() {
localStorage.removeItem('token');
}

static getAuthHeaders() {
const token = localStorage.getItem('token');
return {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
};
}
}

Offline Storage Service (src/services/offlineStorage.js)

class OfflineStorageService {
constructor() {
this.dbName = 'RuralLearningDB';
this.version = 1;
this.db = null;
}

async init() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);

request.onerror = () => reject(request.error);


request.onsuccess = () => {
this.db = request.result;
resolve();
};

request.onupgradeneeded = (event) => {


const db = event.target.result;

// Create lessons store


if (!db.objectStoreNames.contains('lessons')) {
const lessonsStore = db.createObjectStore('lessons', { keyPath: 'id' });
lessonsStore.createIndex('subject', 'subject', { unique: false });
lessonsStore.createIndex('grade_level', 'grade_level', { unique: false });
}

// Create progress store


if (!db.objectStoreNames.contains('progress')) {
const progressStore = db.createObjectStore('progress', { keyPath: 'id', autoInc
progressStore.createIndex('user_id', 'user_id', { unique: false });
progressStore.createIndex('lesson_id', 'lesson_id', { unique: false });
}

// Create offline progress store


if (!db.objectStoreNames.contains('offline_progress')) {
const offlineProgressStore = db.createObjectStore('offline_progress', { keyPath
offlineProgressStore.createIndex('user_id', 'user_id', { unique: false });
offlineProgressStore.createIndex('synced', 'synced', { unique: false });
}
};
});
}

async saveLessons(lessons) {
const transaction = this.db.transaction(['lessons'], 'readwrite');
const store = transaction.objectStore('lessons');

for (const lesson of lessons) {


await store.put(lesson);
}
}

async getLessons(filters = {}) {


const transaction = this.db.transaction(['lessons'], 'readonly');
const store = transaction.objectStore('lessons');

return new Promise((resolve, reject) => {


const request = store.getAll();
request.onsuccess = () => {
let lessons = request.result;

// Apply filters
if (filters.grade_level) {
lessons = lessons.filter(lesson => lesson.grade_level === filters.grade_level);
}
if (filters.subject) {
lessons = lessons.filter(lesson => lesson.subject === filters.subject);
}

resolve(lessons);
};
request.onerror = () => reject(request.error);
});
}

async getLesson(id) {
const transaction = this.db.transaction(['lessons'], 'readonly');
const store = transaction.objectStore('lessons');

return new Promise((resolve, reject) => {


const request = store.get(parseInt(id));
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}

async saveLesson(lesson) {
const transaction = this.db.transaction(['lessons'], 'readwrite');
const store = transaction.objectStore('lessons');
return store.put(lesson);
}

async saveProgress(progressData) {
const transaction = this.db.transaction(['progress'], 'readwrite');
const store = transaction.objectStore('progress');
for (const progress of progressData) {
await store.put(progress);
}
}

async getProgress(userId) {
const transaction = this.db.transaction(['progress'], 'readonly');
const store = transaction.objectStore('progress');
const index = store.index('user_id');

return new Promise((resolve, reject) => {


const request = index.getAll(userId);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}

async saveProgressOffline(userId, progressData) {


const transaction = this.db.transaction(['offline_progress'], 'readwrite');
const store = transaction.objectStore('offline_progress');

const offlineProgress = {
...progressData,
user_id: userId,
synced: false,
timestamp: Date.now()
};

return store.add(offlineProgress);
}

async getOfflineProgress() {
const transaction = this.db.transaction(['offline_progress'], 'readonly');
const store = transaction.objectStore('offline_progress');
const index = store.index('synced');

return new Promise((resolve, reject) => {


const request = index.getAll(false);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}

async markProgressSynced(progressIds) {
const transaction = this.db.transaction(['offline_progress'], 'readwrite');
const store = transaction.objectStore('offline_progress');

for (const id of progressIds) {


const progress = await store.get(id);
if (progress) {
progress.synced = true;
await store.put(progress);
}
}
}
}
export const OfflineStorage = new OfflineStorageService();

5. Language Configuration (src/i18n/i18n.js)

import i18n from 'i18next';


import { initReactI18next } from 'react-i18next';

// Translation resources
const resources = {
english: {
translation: {
app_title: "Rural Learning Platform",
login: "Login",
register: "Register",
username: "Username",
password: "Password",
full_name: "Full Name",
student: "Student",
teacher: "Teacher",
grade: "Grade",
select_grade: "Select Grade",
welcome_message: "Welcome, {{name}}!",
total_lessons: "Total Lessons",
lessons_started: "Lessons Started",
lessons_completed: "Lessons Completed",
all_subjects: "All Subjects",
mathematics: "Mathematics",
language: "Language",
science: "Science",
start_lesson: "Start Lesson",
continue_lesson: "Continue Lesson",
complete: "Complete",
loading: "Loading...",
offline: "Offline",
online: "Online",
sync_pending: "Sync Pending",
logout: "Logout"
}
},
punjabi: {
translation: {
app_title: "ਪੇਂਡੂ ਸਿੱਖਿਆ ਪਲੇਟਫਾਰਮ",
login: "ਲਾਗਇਨ",
register: "ਰਜਿਸਟਰ",
username: "ਯੂਜ਼ਰ ਨਾਮ",
password: "ਪਾਸਵਰਡ",
full_name: "ਪੂਰਾ ਨਾਮ",
student: "ਵਿਦਿਆਰਥੀ",
teacher: "ਅਧਿਆਪਕ",
grade: "ਜਮਾਤ",
select_grade: "ਜਮਾਤ ਚੁਣੋ",
welcome_message: "ਜੀ ਆਇਆਂ ਨੂੰ, {{name}}!",
total_lessons: "ਕੁੱਲ ਪਾਠ",
lessons_started: "ਸ਼ੁਰੂ ਕੀਤੇ ਪਾਠ",
lessons_completed: "ਪੂਰੇ ਕੀਤੇ ਪਾਠ",
all_subjects: "ਸਾਰੇ ਵਿਸ਼ੇ",
mathematics: "ਗਣਿਤ",
language: "ਭਾਸ਼ਾ",
science: "ਵਿਗਿਆਨ",
start_lesson: "ਪਾਠ ਸ਼ੁਰੂ ਕਰੋ",
continue_lesson: "ਪਾਠ ਜਾਰੀ ਰੱਖੋ",
complete: "ਪੂਰਾ",
loading: "ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ...",
offline: "ਔਫਲਾਈਨ",
online: "ਔਨਲਾਈਨ",
sync_pending: "ਸਿੰਕ ਬਾਕੀ ਹੈ",
logout: "ਲਾਗਆਊਟ"
}
},
hindi: {
translation: {
app_title: "ग्रामीण शिक्षा प्लेटफॉर्म",
login: "लॉगिन",
register: "रजिस्टर",
username: "उपयोगकर्ता नाम",
password: "पासवर्ड",
full_name: "पूरा नाम",
student: "छात्र",
teacher: "अध्यापक",
grade: "कक्षा",
select_grade: "कक्षा चुनें",
welcome_message: "स्वागत है, {{name}}!",
total_lessons: "कु ल पाठ",
lessons_started: "शुरू किए गए पाठ",
lessons_completed: "पूरे किए गए पाठ",
all_subjects: "सभी विषय",
mathematics: "गणित",
language: "भाषा",
science: "विज्ञान",
start_lesson: "पाठ शुरू करें",
continue_lesson: "पाठ जारी रखें",
complete: "पूर्ण",
loading: "लोड हो रहा है...",
offline: "ऑफलाइन",
online: "ऑनलाइन",
sync_pending: "सिंक बकाया है",
logout: "लॉगआउट"
}
}
};

i18n
.use(initReactI18next)
.init({
resources,
lng: 'punjabi', // default language
fallbackLng: 'english',
interpolation: {
escapeValue: false
}
});

export default i18n;

6. Service Worker (public/sw.js)

const CACHE_NAME = 'rural-learning-v1';


const urlsToCache = [
'/',
'/static/js/bundle.js',
'/static/css/main.css',
'/manifest.json'
];

// Install service worker


self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
return cache.addAll(urlsToCache);
})
);
});

// Fetch event
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
// Return cached version or fetch from network
return response || fetch(event.request);
})
);
});

// Background sync for offline progress


self.addEventListener('sync', (event) => {
if (event.tag === 'progress-sync') {
event.waitUntil(syncOfflineProgress());
}
});

async function syncOfflineProgress() {


// This would sync offline progress data when connection is restored
console.log('Syncing offline progress...');
}

You might also like