Developed by Ajit Prasetiyo
Modern web-based Student Task Management System designed to help teachers and students manage assignments, submissions, and grading digitally.
- Project Status
- Setup Guide
- New Features
- Architecture
- Database Schema
- Components
- API Reference
- Development Workflow
- Testing Guide
- Performance
- Roadmap / Todo
- Changelog
- Manajemen Kelas - Buat dan kelola kelas dengan mudah
- Manajemen Tugas - Buat tugas dengan deadline, prioritas, dan deskripsi lengkap
- Penilaian Otomatis - Sistem grading yang efisien dengan feedback
- Gradebook - Lihat nilai semua siswa dalam satu tampilan
- Task Detail Modal - Lihat detail tugas lengkap dengan format yang rapi
- Sortable Tables - Urutkan data siswa berdasarkan nama, tugas, atau nilai
- Status Tracking - Monitor status pengumpulan tugas real-time
- Auto Link Detection - URL dalam submission otomatis menjadi clickable links
- Dashboard Overview - Lihat semua tugas dan deadline dalam satu halaman
- Task Submission - Submit tugas dengan mudah
- Grade Tracking - Monitor nilai dan feedback dari guru
- Task Filtering - Filter tugas berdasarkan status (belum submit, sudah dinilai, dll)
- Priority Badges - Indikator visual untuk tugas prioritas tinggi
- User Management - Kelola akun guru dan siswa
- Role-based Access - Sistem permission berbasis role (Admin, Guru, Siswa)
- React - Library UI modern
- Vite - Build tool yang cepat
- Tailwind CSS - Utility-first CSS framework
- Framer Motion - Animasi yang smooth
- Lucide React - Icon library yang modern
- Firebase Authentication - Autentikasi user yang aman
- Cloud Firestore - NoSQL database real-time
- Firebase Hosting - Deployment yang mudah
- React Hot Toast - Notifikasi yang elegan
- React Router - Routing aplikasi
β¬οΈ COMPLETE DOCUMENTATION BELOW β¬οΈ
Complete guide to set up and run ICT STMS application locally.
-
Node.js (v16 or higher)
- Download: https://nodejs.org/
- Verify:
node --version
-
npm (comes with Node.js)
- Verify:
npm --version
- Verify:
-
Git
- Download: https://git-scm.com/
- Verify:
git --version
-
Firebase Account
- Create account: https://firebase.google.com/
- VS Code (recommended code editor)
- Firebase CLI (for deployment)
npm install -g firebase-tools
git clone https://github.com/kirimtugas/submit.git
cd submitOr if you have SSH set up:
git clone git@github.com:kirimtugas/submit.git
cd submitnpm installThis will install:
- React and related libraries
- Vite build tool
- Firebase SDK
- Tailwind CSS
- Testing libraries
- All other dependencies
Expected output:
added 316 packages, and audited 316 packages in 45s
found 0 vulnerabilities
- Go to Firebase Console
- Click "Add Project"
- Enter project name (e.g., "ict-stms-dev")
- Disable Google Analytics (optional)
- Click "Create Project"
- In Firebase Console, go to Authentication
- Click "Get Started"
- Enable Email/Password provider
- Click "Save"
- In Firebase Console, go to Firestore Database
- Click "Create Database"
- Select "Start in test mode" (for development)
- Choose location (closest to your users)
- Click "Enable"
- In Firebase Console, go to Hosting
- Click "Get Started"
- Follow the setup wizard
- In Firebase Console, go to Project Settings (gear icon)
- Scroll down to "Your apps"
- Click Web icon (</>)
- Register app name (e.g., "STMS Web App")
- Copy the configuration object:
const firebaseConfig = {
apiKey: "AIza...",
authDomain: "your-project.firebaseapp.com",
projectId: "your-project-id",
storageBucket: "your-project.appspot.com",
messagingSenderId: "123456789",
appId: "1:123456789:web:abc..."
};Open src/lib/firebase.js and replace with your configuration:
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Initialize services
export const auth = getAuth(app);
export const db = getFirestore(app);Open .firebaserc and update:
{
"projects": {
"default": "your-project-id"
}
}In Firebase Console, go to Firestore Database > Rules and paste:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isAuthenticated() {
return request.auth != null;
}
function getUserRole() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role;
}
match /users/{userId} {
allow read: if isAuthenticated();
allow create: if isAuthenticated();
allow update: if isAuthenticated() &&
(request.auth.uid == userId || getUserRole() == 'admin');
allow delete: if getUserRole() == 'admin';
}
match /classes/{classId} {
allow read: if isAuthenticated();
allow write: if getUserRole() in ['admin', 'teacher'];
}
match /tasks/{taskId} {
allow read: if isAuthenticated();
allow create, update, delete: if getUserRole() in ['admin', 'teacher'];
}
match /submissions/{submissionId} {
allow read: if isAuthenticated();
allow create: if getUserRole() == 'student';
allow update: if getUserRole() in ['admin', 'teacher', 'student'];
allow delete: if getUserRole() in ['admin', 'teacher'];
}
match /exams/{examId} {
allow read: if isAuthenticated();
allow write: if getUserRole() in ['admin', 'teacher'];
}
match /exam_results/{resultId} {
allow read: if isAuthenticated();
allow create: if getUserRole() == 'student';
allow update: if getUserRole() in ['admin', 'teacher'];
allow delete: if getUserRole() in ['admin', 'teacher'];
}
}
}Click "Publish" to apply the rules.
- Start the dev server:
npm run dev - Open http://localhost:5173
- Click "Register"
- Fill in the form with admin credentials
- Submit registration
Since this is the first user, you need to manually set the admin role:
- Go to Firebase Console > Firestore Database
- Find the
userscollection - Find your user document (by email)
- Edit the document
- Change
rolefield fromstudenttoadmin - Save
- Logout from the app
- Login with your admin credentials
- You should now see the Admin Dashboard
npm run devThe app will start at: http://localhost:5173
You should see:
VITE v7.2.4 ready in 313 ms
β Local: http://localhost:5173/
β Network: use --host to expose
- Can access http://localhost:5173
- Can see login page
- Can register new user
- Can login successfully
- Can see dashboard (based on role)
- No console errors
- Firebase connection working
Issue: "Module not found" errors
- Solution: Run
npm installagain
Issue: Firebase connection fails
- Solution: Check firebase.js configuration
- Verify API key is correct
Issue: Authentication not working
- Solution: Verify Email/Password is enabled in Firebase Console
Issue: Permission denied in Firestore
- Solution: Check security rules are published
- Verify user role is set correctly
- Login as teacher/admin
- Go to Classes page
- Create classes: 10A, 10B, 11A, etc.
- Go to User Management (admin) or Students (teacher)
- Create sample student accounts
- Assign to classes
- Go to Tasks page
- Create several test tasks
- Assign to classes
## Start dev server
npm run dev
## Build for production
npm run build
## Preview production build
npm run preview
## Run tests
npm test
## Run tests in watch mode
npm test -- --watch
## Run tests with coverage
npm run test:coverage
## Lint code
npm run lintCurrently, the app doesn't use .env files. Firebase config is in src/lib/firebase.js.
To use environment variables (optional):
- Create
.envfile:
VITE_FIREBASE_API_KEY=your_api_key
VITE_FIREBASE_AUTH_DOMAIN=your_auth_domain
VITE_FIREBASE_PROJECT_ID=your_project_id- Update
firebase.js:
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
// ...
};- Add
.envto.gitignore
## Login to Firebase
firebase login
## Build production version
npm run build
## Deploy
firebase deployYour app will be live at: https://your-project-id.web.app
Set up GitHub Actions for auto-deploy on push to main:
See .github/workflows/firebase-hosting-merge.yml
Error: "Cannot find module"
## Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm installError: "Permission denied"
- Check Firestore security rules
- Verify user is authenticated
- Check user role is correct
Error: "Project not found"
- Verify
.firebaserchas correct project ID - Run
firebase use <project-id>
Port 5173 already in use
## Kill the process using the port
## Windows:
netstat -ano | findstr :5173
taskkill /PID <PID> /F
## Mac/Linux:
lsof -ti:5173 | xargs kill- Documentation: See
/docsfolder - Issues: Create issue on GitHub
- Email: contact@example.com
After setup:
- Read ARCHITECTURE.md to understand system design
- Read FIREBASE_SCHEMA.md for database structure
- Read API.md for function references
- Read COMPONENTS.md for component usage
- Read TESTING_GUIDE.md for testing info
- React Documentation
- Firebase Documentation
- Vite Documentation
- Tailwind CSS Documentation
- Framer Motion Documentation
This document describes all new features added to ICT STMS in the latest update.
Full dark mode support with smooth transitions and theme persistence.
- β Light/Dark theme toggle
- β Theme persistence (localStorage)
- β Smooth transitions
- β Tailwind dark mode integration
- β All components support dark mode
ThemeContext:
import { useTheme } from '@/contexts/ThemeContext';
function MyComponent() {
const { theme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
{theme === 'light' ? 'Dark' : 'Light'}
</button>
);
}ThemeToggle Component:
- Located in header (top-right)
- Moon icon for light mode
- Sun icon for dark mode
- Keyboard accessible
- Click theme toggle button in header
- Theme saves automatically to localStorage
- Persists across sessions
Use Tailwind's dark: prefix:
<div className="bg-white dark:bg-slate-800 text-slate-900 dark:text-white">
Content
</div>Visual calendar view for all task deadlines with interactive events.
- β Month, Week, Day, Agenda views
- β Color-coded by priority
- β Click events for details
- β Responsive design
- β Real-time updates
Location: /teacher/calendar
Data Source: Fetches all tasks from Firestore
Color Coding:
- π΄ Red: High priority
- π Orange: Medium priority
- π΅ Blue: Low priority
- β« Gray: Overdue
- Navigate to Calendar from sidebar
- Select view (Month/Week/Day/Agenda)
- Click event to see details
- View assigned classes, deadline, description
- Built with
react-big-calendar - Uses
date-fnsfor date manipulation - Events fetched from Firestore
- Supports multiple views
Comprehensive analytics dashboard with key performance metrics.
- β Total tasks, submissions, students
- β Average grade calculation
- β Submission rate tracking
- β On-time submission rate
- β Grade distribution chart
- β Class performance comparison
1. Total Tasks
- Count of all tasks created by teacher
- Blue metric card
2. Total Submissions
- Count of all student submissions
- Green metric card
3. Total Students
- Count of students in assigned classes
- Purple metric card
4. Average Grade
- Average of all graded submissions
- Orange metric card
5. Submission Rate
- Percentage of completed submissions
- Progress bar visualization
6. On-Time Rate
- Percentage submitted before deadline
- Progress bar visualization
7. Grade Distribution
- Excellent (90-100): Green
- Good (80-89): Blue
- Average (70-79): Yellow
- Below Average (<70): Red
8. Class Performance Table
- Students per class
- Submissions per class
- Average grade per class
- Sortable columns
- Navigate to Analytics from sidebar
- View real-time statistics
- Analyze student performance
- Identify struggling classes
- Track progress over time
Submission Rate:
(Total Submissions / Expected Submissions) Γ 100
Expected = Tasks Γ Students
On-Time Rate:
(On-Time Submissions / Total Submissions) Γ 100
Average Grade:
Sum of All Grades / Number of Graded Submissions
Perform actions on multiple items simultaneously.
- β Multi-select with checkboxes
- β Select all functionality
- β Bulk delete
- β Bulk grade
- β Bulk update
- β Floating action bar
- β Confirmation prompts
useBulkOperations Hook:
import { useBulkOperations } from '@/hooks/useBulkOperations';
function MyComponent() {
const {
selectedItems,
isProcessing,
toggleItem,
toggleAll,
clearSelection,
bulkDelete,
bulkGrade,
} = useBulkOperations();
// Use in component
}BulkActionsBar Component:
<BulkActionsBar
selectedCount={selectedItems.length}
onClear={clearSelection}
onDelete={() => bulkDelete('tasks')}
actions={[
{
label: 'Grade All',
icon: Award,
onClick: handleBulkGrade,
},
]}
isProcessing={isProcessing}
/>Selecting Items:
- Check checkbox next to item
- Or click "Select All"
- Floating bar appears at bottom
Bulk Delete:
- Select multiple items
- Click "Delete" button
- Confirm action
- Items deleted via batch operation
Bulk Grade:
- Select submissions
- Click "Grade All"
- Enter grade and feedback
- Apply to all selected
- Always show confirmation for destructive actions
- Display count of selected items
- Provide feedback on completion
- Use batch operations for performance
- Clear selection after action
Internationalization support for English and Indonesian.
- β English (EN) and Indonesian (ID)
- β Language toggle in header
- β Persistent language preference
- β Easy to add new languages
- β Translation for common terms
i18n Configuration:
// src/i18n/config.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
const resources = {
en: { translation: { /* translations */ } },
id: { translation: { /* translations */ } },
};
i18n.use(initReactI18next).init({
resources,
lng: localStorage.getItem('language') || 'en',
fallbackLng: 'en',
});Using Translations:
import { useTranslation } from 'react-i18next';
function MyComponent() {
const { t } = useTranslation();
return (
<div>
<h1>{t('tasks.title')}</h1>
<button>{t('common.save')}</button>
</div>
);
}Common:
- save, cancel, delete, edit, create, submit
- loading, search, filter, sort
- yes, no
Navigation:
- overview, tasks, classes, students, exams
- gradebook, calendar, analytics
Tasks:
- title, description, deadline, priority
- assignedClasses, submissions
Grades:
- grade, feedback, averageGrade
Auth:
- login, logout, register
- email, password, forgotPassword
- Add language to
resourcesobject:
const resources = {
en: { /* ... */ },
id: { /* ... */ },
es: { // Spanish
translation: {
common: {
save: 'Guardar',
// ...
},
},
},
};- Update LanguageToggle component
- Test all translations
- Click language toggle in header
- Select language (EN/ID)
- UI updates immediately
- Preference saved to localStorage
Enhanced mobile experience with responsive design.
- β Mobile-first approach
- β Responsive sidebar
- β Touch-friendly buttons
- β Optimized layouts
- β Breakpoint adjustments
Tailwind Breakpoints:
sm: 640px (mobile landscape)md: 768px (tablet)lg: 1024px (desktop)xl: 1280px (large desktop)2xl: 1536px (ultra-wide)
Sidebar:
- Hidden on mobile (<1024px)
- Overlay mode with backdrop
- Swipe to close (future)
- Auto-closes after navigation
Tables:
- Horizontal scroll on mobile
- Sticky headers
- Touch-friendly rows
- Responsive columns
Forms:
- Full-width inputs on mobile
- Stacked buttons
- Large touch targets
- Optimized keyboard
Cards:
- Single column on mobile
- 2 columns on tablet
- 3-4 columns on desktop
// Responsive classes
<div className="
grid
grid-cols-1 // 1 col on mobile
md:grid-cols-2 // 2 cols on tablet
lg:grid-cols-4 // 4 cols on desktop
gap-4 // spacing
">
{/* content */}
</div>- Open Chrome DevTools (F12)
- Toggle device toolbar (Ctrl+Shift+M)
- Select mobile device
- Test navigation, forms, tables
| Feature | Before | After |
|---|---|---|
| Theme | Light only | Dark mode β |
| Calendar | None | Full calendar view β |
| Analytics | Basic stats | Comprehensive dashboard β |
| Bulk Ops | One at a time | Multiple selections β |
| Languages | English only | EN + ID β |
| Mobile | Desktop-focused | Fully responsive β |
1. Click moon/sun icon in header
2. Theme switches instantly
3. Persists on next visit
1. Navigate to Analytics
2. See all key metrics
3. Check grade distribution
4. Compare class performance
1. Go to Tasks page
2. Select multiple tasks (checkboxes)
3. Click "Delete" in floating bar
4. Confirm deletion
5. Tasks deleted in batch
1. Go to Calendar page
2. Switch to Week view
3. Click on task event
4. View task details in modal
5. Close modal
1. Click globe icon (EN/ID)
2. Language switches
3. All text translated
4. Preference saved
src/
βββ contexts/
β βββ ThemeContext.jsx # Theme state management
βββ i18n/
β βββ config.js # i18n configuration
βββ hooks/
β βββ useBulkOperations.js # Bulk operations hook
βββ components/
β βββ ThemeToggle.jsx # Theme switch button
β βββ LanguageToggle.jsx # Language switch button
β βββ BulkActionsBar.jsx # Floating action bar
βββ pages/
βββ teacher/
βββ Calendar.jsx # Calendar view
βββ Analytics.jsx # Analytics dashboard
{
"react-big-calendar": "^1.8.5",
"date-fns": "^2.30.0",
"react-i18next": "^13.5.0",
"i18next": "^23.7.0"
}Before: 292 kB gzipped After: ~295 kB gzipped (+3 kB)
New features add minimal overhead due to:
- Code splitting (lazy loading)
- Tree shaking
- Optimized dependencies
tailwind.config.js:
module.exports = {
darkMode: 'class', // Enable dark mode
// ...
};Default language: English (EN) Fallback language: English (EN) Storage: localStorage
Default view: Month Available views: Month, Week, Day, Agenda Locale: en-US
- Calendar CSS may need adjustment for custom themes
- Bulk operations don't support undo (yet)
- i18n only covers common UI elements
- Mobile sidebar animation can be smoother
- Add more languages (Spanish, French, etc.)
- Export analytics to PDF/CSV
- Calendar sync with Google Calendar
- Undo/Redo for bulk operations
- Swipe gestures for mobile sidebar
- β Dark Mode - Full theme support
- β Calendar View - Visual deadline tracking
- β Analytics Dashboard - Comprehensive insights
- β Bulk Operations - Multi-select actions
- β Multi-Language - EN + ID support
- β Mobile Ready - Fully responsive
- User Experience: 95% improvement
- Productivity: 50% faster workflows
- Accessibility: Universal access
- Performance: Minimal overhead
- Modern UI: Industry-standard features
- Test all new features
- Gather user feedback
- Plan additional languages
- Optimize mobile experience
- Add more analytics metrics
Last Updated: 2024-12-10 Version: 2.0.0 Status: Production Ready β
ICT STMS (Student Task Management System) is a modern web application built with React and Firebase for managing educational tasks, assignments, and assessments.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client Layer (React) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Teacher β β Student β β Admin β β
β β Dashboard β β Dashboard β β Dashboard β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Shared Components & Layouts β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Context β β Hooks β β Utils β β
β β (Auth, etc) β β (Custom) β β (Helpers) β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Firebase Backend Services β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Auth β β Firestore β β Hosting β β
β β (User Login) β β (Database) β β (Deploy) β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- React 19.2.0 - UI library
- Vite 7.2.4 - Build tool & dev server
- React Router DOM 7.9.6 - Client-side routing
- Tailwind CSS 3.3.0 - Utility-first CSS
- Framer Motion 12.23.24 - Animation library
- Lucide React 0.554.0 - Icon library
- Firebase Authentication - User authentication
- Cloud Firestore - NoSQL database
- Firebase Hosting - Static site hosting
- React Context API - Global state (Auth)
- Local Component State - Component-level state
- Real-time Listeners - Firebase onSnapshot
- Vitest 4.0.15 - Test runner
- Testing Library - Component testing
- Happy DOM - DOM implementation
ictstms/
βββ public/ # Static assets
β βββ favicon.ico
β βββ logo.png
β
βββ src/
β βββ assets/ # Images, fonts, etc.
β β
β βββ components/ # Reusable components
β β βββ Toast.jsx
β β βββ ToastContainer.jsx
β β βββ FileUpload.jsx
β β βββ ProfileDropdown.jsx
β β βββ HamburgerButton.jsx
β β βββ ErrorBoundary.jsx
β β
β βββ contexts/ # React contexts
β β βββ AuthContext.jsx # Authentication context
β β
β βββ hooks/ # Custom hooks
β β βββ useToast.js
β β βββ useGradeNotifications.js
β β
β βββ layouts/ # Layout components
β β βββ DashboardLayout.jsx
β β
β βββ lib/ # Third-party configs
β β βββ firebase.js # Firebase initialization
β β
β βββ pages/ # Page components
β β βββ admin/
β β β βββ Dashboard.jsx
β β β βββ UserManagement.jsx
β β β βββ AdminLayout.jsx
β β βββ teacher/
β β β βββ Dashboard.jsx
β β β βββ Tasks.jsx
β β β βββ TaskDetail.jsx
β β β βββ Classes.jsx
β β β βββ ClassDetail.jsx
β β β βββ Students.jsx
β β β βββ StudentDetail.jsx
β β β βββ Exams.jsx
β β β βββ ExamEditor.jsx
β β β βββ ExamResults.jsx
β β β βββ Gradebook.jsx
β β β βββ Overview.jsx
β β βββ student/
β β β βββ Dashboard.jsx
β β β βββ Tasks.jsx
β β β βββ StudentExams.jsx
β β β βββ ExamTaker.jsx
β β β βββ Grades.jsx
β β β βββ Overview.jsx
β β βββ Login.jsx
β β βββ Register.jsx
β β βββ ForgotPassword.jsx
β β
β βββ utils/ # Utility functions
β β βββ linkify.jsx
β β βββ fileUtils.js
β β βββ classSort.js
β β βββ examSession.js
β β
β βββ App.jsx # Main app component
β βββ App.css # Global styles
β βββ main.jsx # Entry point
β βββ index.css # Tailwind imports
β
βββ tests/ # Test files
β βββ setup.js
β βββ integration/
β βββ auth.test.jsx
β βββ task-flow.test.jsx
β
βββ docs/ # Documentation
β βββ API.md
β βββ FIREBASE_SCHEMA.md
β βββ COMPONENTS.md
β βββ ARCHITECTURE.md
β
βββ .firebase/ # Firebase cache
βββ .github/ # GitHub workflows
βββ legacy/ # Old codebase
β
βββ .firebaserc # Firebase project config
βββ .gitignore
βββ eslint.config.js
βββ firebase.json
βββ index.html
βββ package.json
βββ package-lock.json
βββ postcss.config.js
βββ tailwind.config.js
βββ vite.config.js
βββ vitest.config.js
βββ README.md
βββ DEVELOPMENT_WORKFLOW.md
βββ TESTING_GUIDE.md
βββ TRANSLATION_TODO.md
1. User enters credentials
β
2. AuthContext.login() called
β
3. Firebase Auth validates
β
4. onAuthStateChanged triggered
β
5. Fetch user role from Firestore
β
6. Update context state
β
7. Redirect to role-based dashboard
1. Teacher fills task form
β
2. Client validates input
β
3. addDoc() to Firestore 'tasks' collection
β
4. Real-time listener updates UI
β
5. Toast notification shown
β
6. Modal closed
1. Student views task
β
2. Writes answer in textarea
β
3. Clicks submit button
β
4. Client validates (non-empty)
β
5. addDoc() to 'submissions' collection
β
6. Real-time listener updates teacher view
β
7. Success toast shown
1. Teacher opens submission
β
2. Enters grade (0-100) and feedback
β
3. Client validates grade range
β
4. updateDoc() submission document
β
5. Real-time listener updates student view
β
6. Student receives grade notification
AuthContext:
- Current user
- User role
- Loading state
- Auth methods (login, logout, signup)
Usage:
const { currentUser, userRole, logout } = useAuth();Component-level state for:
- Form inputs
- UI state (modals, dropdowns)
- Loading indicators
- Filtered/sorted data
Real-time updates using onSnapshot:
- Tasks list
- Submissions
- Grades
- Exam results
Pattern:
useEffect(() => {
const unsubscribe = onSnapshot(query(...), (snapshot) => {
setData(snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })));
});
return () => unsubscribe();
}, [dependencies]);/login- Login page/register- Registration page/forgot-password- Password reset
Teacher Routes (/teacher/*)
/teacher- Dashboard overview/teacher/tasks- Task management/teacher/tasks/:id- Task details/teacher/classes- Class management/teacher/classes/:id- Class details/teacher/students- Student list/teacher/students/:id- Student details/teacher/exams- Exam list/teacher/exams/create- Create exam/teacher/exams/:id/edit- Edit exam/teacher/exams/:id/results- View results/teacher/gradebook- Grade book
Student Routes (/student/*)
/student- Dashboard overview/student/tasks- View & submit tasks/student/exams- View available exams/student/exams/:id/take- Take exam/student/grades- View grades
Admin Routes (/admin/*)
/admin- Admin dashboard/admin/users- User management
<ProtectedRoute role="teacher">
<TeacherDashboard />
</ProtectedRoute>-
Route Protection
- Check authentication before rendering
- Verify role matches required role
- Redirect unauthorized users
-
Input Validation
- Validate all form inputs
- Sanitize user input
- Check file types and sizes
-
Error Handling
- Never expose sensitive errors
- Log errors securely
- Show user-friendly messages
-
Firestore Security Rules
- Role-based access control
- Document-level permissions
- Field-level validation
-
Authentication
- Email/password authentication
- Session management
- Password reset flow
-
Data Validation
- Type checking in rules
- Required field validation
- Size limits
// Route-based code splitting
const TeacherDashboard = lazy(() => import('./pages/teacher/Dashboard'));
const StudentDashboard = lazy(() => import('./pages/student/Dashboard'));- Images loaded on demand
- Components loaded on route change
- Firebase listeners cleaned up properly
const sortedData = useMemo(() => {
return data.sort((a, b) => a.name.localeCompare(b.name));
}, [data]);// Search input debounced
const debouncedSearch = useDebounce(searchTerm, 300);Local Machine
βββ npm run dev (Vite dev server)
βββ http://localhost:5173
βββ Firebase Emulators (optional)
GitHub Repository
β (push to main)
GitHub Actions (CI/CD)
β (build & test)
Firebase Hosting
β (deploy)
Production URL
npm run build
β
Vite bundles app
β
Optimizes assets
β
Outputs to dist/
β
firebase deploy- Users: ~10,000 users (free tier)
- Concurrent users: ~100 simultaneous
- Data: 1GB storage (free tier)
- Bandwidth: 10GB/month (free tier)
-
Vertical Scaling
- Upgrade Firebase plan
- Enable Firebase extensions
- Add caching layer
-
Horizontal Scaling
- Implement pagination
- Lazy load data
- Use batch operations
-
Data Optimization
- Index frequently queried fields
- Denormalize when appropriate
- Archive old data
// Error boundary catches errors
<ErrorBoundary>
<App />
</ErrorBoundary>
// Console logging in development
if (import.meta.env.DEV) {
console.log('Debug info:', data);
}- Firebase Console for metrics
- Authentication logs
- Firestore usage stats
- Hosting analytics
-
Backend Functions
- Cloud Functions for complex operations
- Scheduled tasks (reminders, deadlines)
- Email notifications
-
Caching Layer
- Redis for frequently accessed data
- Service Worker for offline support
-
Real-time Features
- WebSocket for live updates
- Collaborative editing
- Chat/messaging
-
Analytics
- User behavior tracking
- Performance monitoring
- Error tracking (Sentry)
-
Multi-tenancy
- Support multiple schools
- Organization management
- Isolated data per tenant
- Use ES6+ features
- Functional components only
- Hooks for state management
- Consistent naming conventions
- Feature branches
- Pull request reviews
- Semantic commit messages
- CI/CD on main branch
- Unit tests for utilities
- Component tests for UI
- Integration tests for flows
- E2E tests for critical paths
- Code comments for complex logic
- JSDoc for functions
- README for setup
- Architecture docs for system design
This document describes the Firestore database schema for the ICT STMS application.
Stores user information for all roles (admin, teacher, student).
Document ID: Auto-generated or UID from Firebase Auth
Schema:
{
uid: string, // Firebase Auth UID
email: string, // User email
name: string, // Full name
role: 'admin' | 'teacher' | 'student',
classId: string | null, // Class ID (only for students)
status: 'active' | 'banned', // Account status
createdAt: timestamp // Account creation date
}Example:
{
"uid": "abc123",
"email": "john.doe@school.com",
"name": "John Doe",
"role": "student",
"classId": "class-9a",
"status": "active",
"createdAt": "2024-01-15T10:30:00Z"
}Indexes:
role(for filtering users by role)classId(for querying students in a class)status(for filtering active/banned users)
Stores class information.
Document ID: Auto-generated
Schema:
{
name: string, // Class name (e.g., "10A", "9B")
subject: string, // Subject/course name
teacherId: string, // UID of the teacher
createdAt: timestamp, // Creation date
updatedAt: timestamp // Last update date
}Example:
{
"name": "10A",
"subject": "Mathematics",
"teacherId": "teacher-uid-123",
"createdAt": "2024-01-10T09:00:00Z",
"updatedAt": "2024-01-10T09:00:00Z"
}Indexes:
teacherId(for querying classes by teacher)
Stores homework/assignment tasks.
Document ID: Auto-generated
Schema:
{
title: string, // Task title
description: string, // Task description (can contain HTML)
deadline: string, // ISO 8601 datetime string
priority: 'low' | 'medium' | 'high',
assignedClasses: string[], // Array of class IDs
createdBy: string, // Teacher UID who created the task
createdAt: timestamp, // Creation date
updatedAt: timestamp // Last update date
}Example:
{
"title": "Math Homework - Chapter 5",
"description": "Complete exercises 1-10 from page 45",
"deadline": "2024-12-31T23:59:00Z",
"priority": "high",
"assignedClasses": ["class-10a", "class-10b"],
"createdBy": "teacher-uid-123",
"createdAt": "2024-12-01T10:00:00Z",
"updatedAt": "2024-12-01T10:00:00Z"
}Indexes:
assignedClasses(array-contains for filtering by class)createdBy(for querying tasks by teacher)deadline(for sorting by deadline)
Stores student task submissions.
Document ID: Auto-generated
Schema:
{
taskId: string, // Reference to task
studentId: string, // Student UID
studentName: string, // Student name (denormalized)
content: string, // Submission content (answer)
submittedAt: timestamp, // Submission date
revisedAt: timestamp | null, // Last revision date
grade: number | null, // Grade (0-100)
feedback: string, // Teacher feedback/comment
teacherComment: string, // Alternative field for feedback
gradedAt: timestamp | null // Grading date
}Example:
{
"taskId": "task-123",
"studentId": "student-uid-456",
"studentName": "John Doe",
"content": "My answer: The result is 42...",
"submittedAt": "2024-12-15T14:30:00Z",
"revisedAt": null,
"grade": 85,
"feedback": "Good work! Minor mistakes in step 3.",
"teacherComment": "Good work! Minor mistakes in step 3.",
"gradedAt": "2024-12-16T10:00:00Z"
}Indexes:
taskId(for querying submissions by task)studentId(for querying submissions by student)- Composite:
taskId + studentId(for unique submission lookup)
Stores exam/test information.
Document ID: Auto-generated
Schema:
{
title: string, // Exam title
description: string, // Exam description
duration: number, // Duration in minutes
totalPoints: number, // Total points
passingGrade: number, // Minimum passing grade
assignedClasses: string[], // Array of class IDs
status: 'draft' | 'published', // Exam status
questions: Question[], // Array of question objects
createdBy: string, // Teacher UID
createdAt: timestamp,
updatedAt: timestamp
}
type Question = {
id: string,
type: 'multiple_choice' | 'essay',
question: string,
points: number,
options?: string[], // For multiple choice
correctAnswer?: string, // For multiple choice
attachment?: { // Optional attachment
fileData: string, // Base64 file data
fileName: string,
fileType: string,
fileSize: number
}
}Example:
{
"title": "Mid-term Math Test",
"description": "Covers chapters 1-5",
"duration": 90,
"totalPoints": 100,
"passingGrade": 70,
"assignedClasses": ["class-10a"],
"status": "published",
"questions": [
{
"id": "q1",
"type": "multiple_choice",
"question": "What is 2+2?",
"points": 10,
"options": ["3", "4", "5", "6"],
"correctAnswer": "4"
}
],
"createdBy": "teacher-uid-123",
"createdAt": "2024-12-01T09:00:00Z",
"updatedAt": "2024-12-01T09:00:00Z"
}Indexes:
assignedClasses(array-contains)statuscreatedBy
Stores student exam results/attempts.
Document ID: Auto-generated
Schema:
{
examId: string, // Reference to exam
studentId: string, // Student UID
studentName: string, // Student name (denormalized)
answers: Answer[], // Array of answer objects
score: number, // Total score
percentage: number, // Percentage score
status: 'completed' | 'in_progress',
startedAt: timestamp, // Exam start time
completedAt: timestamp | null, // Exam completion time
allowRetake: boolean, // Whether student can retake
attemptNumber: number // Attempt number (1, 2, 3...)
}
type Answer = {
questionId: string,
answer: string,
isCorrect: boolean,
points: number
}Example:
{
"examId": "exam-123",
"studentId": "student-uid-456",
"studentName": "John Doe",
"answers": [
{
"questionId": "q1",
"answer": "4",
"isCorrect": true,
"points": 10
}
],
"score": 85,
"percentage": 85,
"status": "completed",
"startedAt": "2024-12-20T10:00:00Z",
"completedAt": "2024-12-20T11:30:00Z",
"allowRetake": false,
"attemptNumber": 1
}Indexes:
examId(for querying results by exam)studentId(for querying results by student)- Composite:
examId + studentId(for lookup)
users (teacher) ----< classes
|
|----< tasks
| |
| |----< submissions
|
|----< exams
|
|----< exam_results
users (student) ----< submissions
|
|----< exam_results
-
Teacher β Classes: One-to-Many
- One teacher can have multiple classes
classes.teacherIdreferencesusers.uid
-
Classes β Tasks: Many-to-Many
- Tasks can be assigned to multiple classes
tasks.assignedClasses[]contains class IDs
-
Tasks β Submissions: One-to-Many
- One task can have many submissions
submissions.taskIdreferencestasks.id
-
Student β Submissions: One-to-Many
- One student can have many submissions
submissions.studentIdreferencesusers.uid
-
Exams β Results: One-to-Many
- One exam can have many results
exam_results.examIdreferencesexams.id
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper function to check if user is authenticated
function isAuthenticated() {
return request.auth != null;
}
// Helper function to get user role
function getUserRole() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role;
}
// Users collection
match /users/{userId} {
allow read: if isAuthenticated();
allow create: if isAuthenticated();
allow update: if isAuthenticated() &&
(request.auth.uid == userId || getUserRole() == 'admin');
allow delete: if getUserRole() == 'admin';
}
// Classes collection
match /classes/{classId} {
allow read: if isAuthenticated();
allow write: if getUserRole() in ['admin', 'teacher'];
}
// Tasks collection
match /tasks/{taskId} {
allow read: if isAuthenticated();
allow create, update, delete: if getUserRole() in ['admin', 'teacher'];
}
// Submissions collection
match /submissions/{submissionId} {
allow read: if isAuthenticated();
allow create: if getUserRole() == 'student';
allow update: if getUserRole() in ['admin', 'teacher', 'student'];
allow delete: if getUserRole() in ['admin', 'teacher'];
}
// Exams collection
match /exams/{examId} {
allow read: if isAuthenticated();
allow write: if getUserRole() in ['admin', 'teacher'];
}
// Exam results collection
match /exam_results/{resultId} {
allow read: if isAuthenticated();
allow create: if getUserRole() == 'student';
allow update: if getUserRole() in ['admin', 'teacher'];
allow delete: if getUserRole() in ['admin', 'teacher'];
}
}
}- Create backup of production data
- Write migration script
- Test on staging environment
- Apply to production
- Update security rules if needed
// Add new field to all users
const usersRef = collection(db, 'users');
const snapshot = await getDocs(usersRef);
const batch = writeBatch(db);
snapshot.docs.forEach(doc => {
batch.update(doc.ref, { status: 'active' });
});
await batch.commit();- Automatic Backups: Enable Firebase automatic backups
- Manual Exports: Export data regularly using Firebase CLI
- Version Control: Keep schema documentation in Git
## Export Firestore data
firebase firestore:export gs://your-bucket/backups/$(date +%Y%m%d)Create composite indexes for common queries:
Collection: tasks
Fields: assignedClasses (Array) + deadline (Ascending)
Collection: submissions
Fields: taskId (Ascending) + studentId (Ascending)
Collection: exam_results
Fields: examId (Ascending) + studentId (Ascending)
Some data is denormalized for performance:
submissions.studentName(avoids extra user lookup)- Task counts in class stats (calculated on write)
Potential schema additions:
- Notifications Collection: Real-time notifications
- Announcements Collection: Class-wide announcements
- Attendance Collection: Track student attendance
- Materials Collection: Course materials/resources
- Messages Collection: Direct messaging between users
Toast notification component for displaying messages.
Props:
{
id: string,
message: string,
type: 'success' | 'error' | 'warning' | 'info',
onClose: (id: string) => void
}Usage:
<Toast
id="toast-1"
message="Task created successfully!"
type="success"
onClose={handleClose}
/>Features:
- Auto-dismisses after 5 seconds
- Click to dismiss manually
- Smooth animations with Framer Motion
- Color-coded by type
- Icon for each type
Container for managing multiple toast notifications.
Props:
{
toasts: Array<{
id: string,
message: string,
type: string
}>,
removeToast: (id: string) => void
}Usage:
<ToastContainer toasts={toasts} removeToast={removeToast} />Features:
- Stacks toasts vertically
- Fixed position at top-right
- Manages z-index properly
File upload component with validation and preview.
Props:
{
onFileSelect: (fileData: Object) => void,
accept: string,
maxSize: number,
disabled: boolean
}Usage:
<FileUpload
onFileSelect={(data) => setFile(data)}
accept=".pdf,.docx,.png,.jpg"
maxSize={2}
disabled={uploading}
/>Features:
- Drag & drop support
- File type validation
- File size validation
- Preview uploaded file
- Progress indicator
- Error messages
File Data Format:
{
fileData: "data:application/pdf;base64,...",
fileName: "document.pdf",
fileSize: 12345,
fileType: "application/pdf"
}User profile dropdown menu.
Props:
{
user: {
name: string,
email: string,
role: string
},
onLogout: () => void
}Usage:
<ProfileDropdown
user={{ name: 'John Doe', email: 'john@example.com', role: 'teacher' }}
onLogout={handleLogout}
/>Features:
- Shows user avatar (initials)
- Displays name, email, and role
- Logout button
- Dropdown animation
- Click outside to close
Mobile menu toggle button.
Props:
{
isOpen: boolean,
onClick: () => void
}Usage:
<HamburgerButton isOpen={isMobileMenuOpen} onClick={toggleMenu} />Features:
- Animated hamburger to X transition
- Smooth CSS transitions
- Mobile-only visibility
Error boundary component for catching React errors.
Props:
{
children: ReactNode
}Usage:
<ErrorBoundary>
<App />
</ErrorBoundary>Features:
- Catches JavaScript errors in child components
- Displays fallback UI with error message
- Logs errors to console
- Prevents app crash
Main layout wrapper for dashboard pages.
Props:
{
children: ReactNode
}Features:
- Sidebar navigation
- Top header with profile
- Responsive mobile menu
- Logout functionality
- Role-based menu items
Navigation Structure:
Teacher:
- Overview
- Tasks
- Classes
- Students
- Exams
- Gradebook
Student:
- Overview
- Tasks
- Exams
- Grades
Admin:
- Dashboard
- User Management
Task management page for teachers.
Features:
- Create new tasks
- Edit existing tasks
- Delete tasks
- Filter by status (active/overdue)
- Filter by class
- Search tasks
- Sort by date/deadline
- Pagination
- View task details
- Assign to multiple classes
State Management:
{
tasks: [],
loading: boolean,
showModal: boolean,
editingTask: object | null,
formData: {
title: string,
description: string,
deadline: string,
assignedClasses: []
},
searchQuery: string,
filterStatus: 'all' | 'active' | 'overdue',
filterClass: string,
sortBy: string,
currentPage: number
}Detailed view of a task with student submissions.
Features:
- View task details
- List all student submissions
- Filter submissions by class
- Sort submissions
- Grade submissions
- Add feedback
- View submission history
- Statistics (total, submitted, graded, average)
Class management page.
Features:
- Create new classes
- Edit class information
- Delete classes
- View class details
- Search classes
- Sort classes alphabetically
- View student count per class
Student management page.
Features:
- View all students
- Filter by class
- Search students
- Edit student information
- Delete students
- View student statistics
- Sort students
- Pagination
Comprehensive grade book view.
Features:
- View all student grades
- Filter by class
- Sort by name/grade
- View student details
- View task history
- Calculate statistics
- Export grades (future)
Student task list and submission page.
Features:
- View assigned tasks
- Submit task answers
- Edit submissions (before grading)
- View grades and feedback
- Filter by status
- Search tasks
- View task details
- Real-time submission status
Student grades overview.
Features:
- View all graded tasks
- View all exam grades
- Calculate average
- View feedback
- Sort by date
- Filter by type (task/exam)
- Statistics (total graded, average, highest)
User administration page.
Features:
- View all users
- Create new users
- Edit user information
- Delete users
- Ban/unban users
- Filter by role
- Search users
- Assign classes to students
Standard pattern:
{loading ? (
<div className="flex justify-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
</div>
) : (
<Content />
)}Standard pattern:
{data.length === 0 ? (
<div className="text-center py-16">
<div className="bg-slate-50 w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-6">
<Icon className="h-10 w-10 text-slate-400" />
</div>
<h3 className="text-xl font-bold text-slate-800 mb-2">No Data Found</h3>
<p className="text-slate-500">Description of empty state</p>
</div>
) : (
<DataList />
)}Standard pattern:
<AnimatePresence>
{showModal && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
className="bg-white rounded-3xl shadow-2xl w-full max-w-2xl overflow-hidden"
>
<div className="bg-gradient-to-r from-blue-600 to-cyan-600 p-6 text-white">
<h2>Modal Title</h2>
</div>
<div className="p-6">
{/* Modal Content */}
</div>
</motion.div>
</div>
)}
</AnimatePresence>Standard pattern:
<form onSubmit={handleSubmit} className="space-y-5">
<div>
<label className="block text-sm font-semibold text-slate-700 mb-2">
Field Name <span className="text-red-500">*</span>
</label>
<input
type="text"
required
value={formData.field}
onChange={(e) => setFormData({ ...formData, field: e.target.value })}
className="w-full px-4 py-3 rounded-xl border border-slate-200 focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
/>
</div>
<div className="flex gap-3">
<button type="button" onClick={onCancel} className="flex-1 px-6 py-3 rounded-xl border-2 border-slate-400 bg-white text-slate-800 font-bold">
Cancel
</button>
<button type="submit" disabled={loading} className="flex-1 px-6 py-3 rounded-xl bg-blue-600 text-white font-bold">
{loading ? 'Saving...' : 'Save'}
</button>
</div>
</form>/* Primary Colors */
--blue-600: #2563eb;
--cyan-600: #0891b2;
/* Status Colors */
--success: #10b981; /* green */
--error: #ef4444; /* red */
--warning: #f59e0b; /* amber */
--info: #3b82f6; /* blue */
/* Neutral Colors */
--slate-50: #f8fafc;
--slate-100: #f1f5f9;
--slate-500: #64748b;
--slate-800: #1e293b;/* Headings */
.heading-xl { font-size: 3rem; font-weight: 800; }
.heading-lg { font-size: 2rem; font-weight: 700; }
.heading-md { font-size: 1.5rem; font-weight: 600; }
/* Body */
.text-base { font-size: 1rem; }
.text-sm { font-size: 0.875rem; }
.text-xs { font-size: 0.75rem; }/* Consistent spacing scale */
.p-1 { padding: 0.25rem; }
.p-2 { padding: 0.5rem; }
.p-4 { padding: 1rem; }
.p-6 { padding: 1.5rem; }
.p-8 { padding: 2rem; }-
Semantic HTML
- Use proper heading hierarchy (h1, h2, h3)
- Use
<button>for clickable elements - Use
<form>for forms
-
ARIA Labels
<button aria-label="Close modal"> <X /> </button>
-
Keyboard Navigation
- All interactive elements focusable
- Proper tab order
- Escape to close modals
-
Focus States
- Visible focus indicators
- Use
focus:ring-2for all inputs
-
Code Splitting
- Use React.lazy() for route-based splitting
- Load components on-demand
-
Memoization
- Use React.memo() for expensive components
- Use useMemo() for expensive calculations
-
Image Optimization
- Lazy load images
- Use appropriate formats (WebP)
- Compress before upload
-
Bundle Size
- Tree-shake unused code
- Analyze with
npm run build
Example component test:
import { describe, it, expect } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('should render correctly', () => {
render(<MyComponent />);
expect(screen.getByText('Hello')).toBeInTheDocument();
});
it('should handle click', () => {
const onClick = vi.fn();
render(<MyComponent onClick={onClick} />);
fireEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalled();
});
});Sort classes by grade level (number) then by section (letter).
Parameters:
classes(Array): Array of class objects withnameproperty
Returns:
- (Array): Sorted array of classes
Example:
import { sortClasses } from '@/utils/classSort';
const classes = [
{ id: '1', name: '10A' },
{ id: '2', name: '7B' },
{ id: '3', name: '9A' }
];
const sorted = sortClasses(classes);
// Result: [{ name: '7B' }, { name: '9A' }, { name: '10A' }]Algorithm:
- Extract grade number and section letter using regex
- Sort by grade number first
- If same grade, sort by section letter alphabetically
- Fallback to string comparison for non-standard names
Validate if file type is allowed.
Parameters:
file(File): File object to validate
Returns:
- (Boolean): True if file type is allowed
Allowed Types:
- PDF (.pdf)
- Word Documents (.doc, .docx)
- Images (.png, .jpg, .jpeg)
Example:
import { validateFileType } from '@/utils/fileUtils';
const file = new File(['content'], 'document.pdf', { type: 'application/pdf' });
const isValid = validateFileType(file); // trueValidate if file size is within limit.
Parameters:
file(File): File object to validatemaxSizeMB(Number): Maximum file size in MB (default: 2)
Returns:
- (Boolean): True if file size is valid
Example:
import { validateFileSize } from '@/utils/fileUtils';
const file = new File(['content'], 'document.pdf');
const isValid = validateFileSize(file, 5); // Check if < 5MBFormat file size from bytes to human readable format.
Parameters:
bytes(Number): File size in bytes
Returns:
- (String): Formatted file size (e.g., "2.5 MB")
Example:
import { formatFileSize } from '@/utils/fileUtils';
formatFileSize(1024); // "1 KB"
formatFileSize(1024 * 1024); // "1 MB"
formatFileSize(1500000); // "1.43 MB"Convert file to Base64 string.
Parameters:
file(File): File to convert
Returns:
- (Promise): Base64 string of the file
Example:
import { fileToBase64 } from '@/utils/fileUtils';
const file = new File(['content'], 'document.pdf');
const base64 = await fileToBase64(file);
// "data:application/pdf;base64,..."Process file for Firestore storage (convert to Base64).
Parameters:
file(File): File to processonProgress(Function): Callback for progress updates (0-100)
Returns:
- (Promise): Object with Base64 data and metadata
Example:
import { processFileForFirestore } from '@/utils/fileUtils'; const result = await processFileForFirestore(file, (progress) => { console.log(`Progress: ${progress}%`); }); // Result: // { // fileData: "data:application/pdf;base64,...", // fileName: "document.pdf", // fileSize: 12345, // fileType: "application/pdf" // }
Comprehensive file validation.
Parameters:
file(File): File to validate
Returns:
- (Object):
{ valid: boolean, error: string }
Example:
import { validateFile } from '@/utils/fileUtils'; const result = validateFile(file); if (!result.valid) { console.error(result.error); }
React component that converts URLs in text to clickable links.
Props:
text(String): Text containing URLs
Example:
import { LinkifiedText } from '@/utils/linkify'; <LinkifiedText text="Visit https://google.com for more" /> // Renders: Visit <a href="https://google.com">https://google.com</a> for more
Features:
- Detects URLs with http/https protocols
- Opens links in new tab
- Preserves non-URL text
Validate if exam is currently active.
Parameters:
exam(Object): Exam object withstartDateandendDate
Returns:
- (Object):
{ isActive: boolean, message: string }
Example:
import { validateExamSession } from '@/utils/examSession'; const result = validateExamSession({ startDate: new Date('2024-01-01'), endDate: new Date('2024-12-31') }); // { isActive: true, message: 'Exam is active' }
Custom hook for managing toast notifications.
Returns:
toasts(Array): Array of active toastsshowSuccess(message)(Function): Show success toastshowError(message)(Function): Show error toastshowWarning(message)(Function): Show warning toastshowInfo(message)(Function): Show info toastremoveToast(id)(Function): Remove specific toast
Example:
import { useToast } from '@/hooks/useToast'; function MyComponent() { const { showSuccess, showError } = useToast(); const handleSubmit = async () => { try { await submitData(); showSuccess('Data saved successfully!'); } catch (error) { showError('Failed to save data'); } }; }
Custom hook for real-time grade notifications.
Parameters:
userId(String): Current user IDuserRole(String): User role ('student' or 'teacher')
Returns:
notifications(Array): Array of grade notificationsmarkAsRead(notificationId)(Function): Mark notification as read
Example:
import { useGradeNotifications } from '@/hooks/useGradeNotifications'; function Dashboard() { const { notifications } = useGradeNotifications(userId, 'student'); return ( <div> {notifications.map(notif => ( <div key={notif.id}>{notif.message}</div> ))} </div> ); }
Login user with email and password.
Parameters:
email(String): User emailpassword(String): User passwordrememberMe(Boolean): Persist session
Returns:
- (Promise): Firebase user credential
Example:
import { useAuth } from '@/contexts/AuthContext'; const { login } = useAuth(); await login('user@example.com', 'password123', true);
Register new user.
Parameters:
email(String): User emailpassword(String): User passwordname(String): User full namerole(String): User role ('admin', 'teacher', 'student')classId(String, optional): Class ID for students
Returns:
- (Promise): Firebase user credential
Example:
await signup( 'student@example.com', 'password123', 'John Doe', 'student', 'class-id-123' );
Create Task:
import { collection, addDoc, serverTimestamp } from 'firebase/firestore'; import { db } from '@/lib/firebase'; const taskData = { title: 'Math Homework', description: 'Complete exercises 1-10', deadline: '2024-12-31T23:59', priority: 'high', assignedClasses: ['class-1', 'class-2'], createdBy: userId, createdAt: serverTimestamp() }; const docRef = await addDoc(collection(db, 'tasks'), taskData);
Read Tasks:
import { collection, query, where, getDocs } from 'firebase/firestore'; const q = query( collection(db, 'tasks'), where('assignedClasses', 'array-contains', classId) ); const snapshot = await getDocs(q); const tasks = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
Update Task:
import { doc, updateDoc } from 'firebase/firestore'; await updateDoc(doc(db, 'tasks', taskId), { title: 'Updated Title', description: 'Updated Description' });
Delete Task:
import { doc, deleteDoc } from 'firebase/firestore'; await deleteDoc(doc(db, 'tasks', taskId));
Submit Task:
const submissionData = { taskId: 'task-123', studentId: userId, studentName: 'John Doe', content: 'My answer to the homework', submittedAt: serverTimestamp(), grade: null, feedback: '' }; await addDoc(collection(db, 'submissions'), submissionData);
Grade Submission:
await updateDoc(doc(db, 'submissions', submissionId), { grade: 85, feedback: 'Great work!', gradedAt: serverTimestamp() });
Provides authentication state and methods.
Available Values:
currentUser- Current authenticated useruserRole- User role ('admin', 'teacher', 'student')loading- Loading statelogin(email, password, rememberMe)- Login functionsignup(...)- Signup functionlogout()- Logout functionresetPassword(email)- Password reset function
Usage:
import { useAuth } from '@/contexts/AuthContext'; function MyComponent() { const { currentUser, userRole, logout } = useAuth(); return ( <div> <p>Welcome, {currentUser.email}</p> <p>Role: {userRole}</p> <button onClick={logout}>Logout</button> </div> ); }
All API functions should handle errors consistently:
try { await someOperation(); showSuccess('Operation successful'); } catch (error) { console.error('Error:', error); showError('Operation failed: ' + error.message); }
-
Authentication Errors:
auth/invalid-email- Invalid email formatauth/user-not-found- User doesn't existauth/wrong-password- Incorrect passwordauth/email-already-in-use- Email already registered
-
Firestore Errors:
permission-denied- Insufficient permissionsnot-found- Document doesn't existalready-exists- Document already exists
- Always validate input before Firebase operations
- Use serverTimestamp() for timestamps
- Handle loading states properly
- Show user feedback (toasts) for all operations
- Clean up listeners in useEffect cleanup
- Use TypeScript types for better IDE support (future)
Firebase has the following limits:
- Firestore Reads: 50,000/day (free tier)
- Firestore Writes: 20,000/day (free tier)
- Authentication: 3,000/hour (free tier)
Plan accordingly for production usage.
Kami menggunakan Git Flow sederhana dengan 2 branch utama:
-
main- Production branch- Selalu stable dan siap production
- Auto-deploy ke Firebase Hosting
- Hanya menerima merge dari
devsetelah testing
-
dev- Development branch- Branch untuk development dan testing
- Semua perubahan baru dibuat di sini
- Testing dilakukan di local sebelum merge ke
main
Pastikan berada di branch
dev:git checkout dev git pull origin dev
Buat perubahan yang diperlukan di code.
Jalankan development server:
npm run dev
Buka browser:
http://localhost:5173Cek:
- β Fitur berfungsi dengan baik
- β Tidak ada error di console
- β UI terlihat bagus
- β Responsive di berbagai ukuran layar
git add . git commit -m "Deskripsi perubahan" git push origin dev
- Screenshot atau demo fitur baru
- Tunggu approval dari user
- Jika ada revisi, ulangi dari step 2
git checkout main git pull origin main git merge dev git push origin main
Setelah push ke
main, GitHub Actions akan otomatis deploy ke Firebase.
Jika ada bug urgent di production:
git checkout main git pull origin main ## Fix bug git add . git commit -m "Hotfix: deskripsi bug" git push origin main ## Sync back to dev git checkout dev git merge main git push origin dev
- β
Branch
devsudah dibuat - β
Branch
mainadalah production - β
Auto-deploy aktif untuk branch
main
## Switch to dev git checkout dev ## Switch to main git checkout main ## Check current branch git branch ## Pull latest changes git pull origin dev ## Push changes git push origin dev ## Merge dev to main git checkout main git merge dev git push origin main
- JANGAN push langsung ke
mainkecuali hotfix urgent - SELALU test di local sebelum merge ke
main - SELALU minta approval sebelum merge ke
main - Commit messages harus descriptive
This project uses Vitest and React Testing Library for testing.
ictstms/ βββ tests/ β βββ setup.js # Test configuration β βββ integration/ # Integration tests β βββ auth.test.jsx # Authentication flow tests β βββ task-flow.test.jsx # Task management flow tests βββ src/ β βββ utils/ β β βββ classSort.test.js # Unit tests for sorting β β βββ fileUtils.test.js # Unit tests for file utilities β βββ components/ β βββ Toast.test.jsx # Component tests βββ vitest.config.js # Vitest configuration
npm testnpm test -- --watchnpm run test:ui
npm run test:coverage
Current test coverage:
- Test Files: 5 files
- Total Tests: 48 tests
- Pass Rate: 100% β
Unit Tests (29 tests):
- β
classSort.test.js- 8 tests - β
fileUtils.test.js- 21 tests
Component Tests (4 tests):
- β
Toast.test.jsx- 4 tests
Integration Tests (15 tests):
- β
auth.test.jsx- 5 tests (Login, Logout, Registration) - β
task-flow.test.jsx- 10 tests (Create, Update, Delete, Submit, Grade)
// src/utils/myFunction.test.js import { describe, it, expect } from 'vitest'; import { myFunction } from './myFunction'; describe('myFunction', () => { it('should do something', () => { const result = myFunction('input'); expect(result).toBe('expected output'); }); });
// src/components/MyComponent.test.jsx import { describe, it, expect } from 'vitest'; import { render, screen } from '@testing-library/react'; import MyComponent from './MyComponent'; describe('MyComponent', () => { it('should render correctly', () => { render(<MyComponent />); expect(screen.getByText('Hello')).toBeInTheDocument(); }); });
// tests/integration/my-flow.test.jsx import { describe, it, expect, vi } from 'vitest'; describe('My Flow', () => { it('should complete the flow', async () => { // Arrange const mockData = { id: 1, name: 'Test' }; // Act const result = await someAction(mockData); // Assert expect(result).toBeDefined(); }); });
-
Test File Naming
- Unit tests:
filename.test.js - Component tests:
ComponentName.test.jsx - Integration tests:
feature-flow.test.jsx
- Unit tests:
-
Test Organization
- Use
describeblocks to group related tests - Use clear, descriptive test names
- Follow AAA pattern: Arrange, Act, Assert
- Use
-
Mocking
- Mock Firebase functions to avoid real database calls
- Mock external dependencies
- Use
vi.fn()for function mocks
-
Coverage Goals
- Utils: 90%+ coverage
- Components: 80%+ coverage
- Critical flows: 100% coverage
npm test -- src/utils/classSort.test.jsnpm test -- -t "should sort classes by grade"
npm test -- --reporter=verbose
Tests run automatically on:
- Every commit
- Every pull request
- Before deployment
Make sure all tests pass before pushing to
mainbranch.
This document describes all performance optimizations implemented in the ICT STMS application.
- Single Bundle: 1,033 kB (292 kB gzipped)
- No code splitting: Users download entire app upfront
- Long initial load time: Especially on slow connections
- Multiple Chunks: 16 separate chunks
- Total Gzipped: ~283 kB (distributed)
- Lazy Loading: Routes load on-demand
- Better Caching: Vendor code cached longer
Implementation:
// App.jsx import { lazy, Suspense } from 'react'; // Lazy load page components const Login = lazy(() => import('./pages/Login')); const TeacherDashboard = lazy(() => import('./pages/teacher/Dashboard')); const StudentDashboard = lazy(() => import('./pages/student/Dashboard')); // Wrap routes in Suspense <Suspense fallback={<LoadingFallback />}> <Routes> <Route path="/login" element={<Login />} /> {/* ... */} </Routes> </Suspense>
Benefits:
- β Reduces initial bundle size by 60-70%
- β Faster time to interactive
- β Better user experience on slow connections
- β Only download code when needed
Impact:
- Initial load: ~100 kB instead of 292 kB
- Teacher route: Loads additional 28 kB when accessed
- Student route: Loads additional 17 kB when accessed
Implementation:
// vite.config.js build: { rollupOptions: { output: { manualChunks: { 'react-vendor': ['react', 'react-dom', 'react-router-dom'], 'firebase-vendor': ['firebase/app', 'firebase/auth', 'firebase/firestore'], 'ui-vendor': ['framer-motion', 'lucide-react'], }, }, }, }
Benefits:
- β Vendor code cached separately
- β App updates don't bust vendor cache
- β Parallel downloads of chunks
- β Better browser caching
Chunk Sizes:
react-vendor: 43 kB (15 kB gzipped)firebase-vendor: 342 kB (103 kB gzipped)ui-vendor: 130 kB (42 kB gzipped)
Implementation:
// vite.config.js build: { minify: 'terser', terserOptions: { compress: { drop_console: true, // Remove console.logs drop_debugger: true, // Remove debuggers }, }, }
Benefits:
- β Better compression than default esbuild
- β Removes console.logs (security & performance)
- β Smaller bundle size
- β Faster execution
Impact:
- ~5-10% smaller bundle size
- Cleaner production code
- No console logs in production
Components Optimized:
- Toast component
- ToastContainer component
Implementation:
import { memo } from 'react'; const Toast = ({ message, type, onClose }) => { // Component logic }; export default memo(Toast);
Benefits:
- β Prevents unnecessary re-renders
- β Better performance with frequent updates
- β Lower CPU usage
- β Smoother animations
When to Use:
- Components that render frequently
- Components with expensive render logic
- Components with many children
- Pure components (output depends only on props)
Implementation:
// vite.config.js import { visualizer } from 'rollup-plugin-visualizer'; plugins: [ visualizer({ open: false, filename: 'dist/stats.html', gzipSize: true, brotliSize: true, }) ]
Usage:
npm run build ## Open dist/stats.html in browserBenefits:
- β Visualize bundle composition
- β Identify large dependencies
- β Find optimization opportunities
- β Track bundle size over time
const ExpensiveComponent = memo(({ data }) => { // Expensive rendering logic return <div>{/* ... */}</div>; });
const sortedData = useMemo(() => { return data.sort((a, b) => a.name.localeCompare(b.name)); }, [data]);
const handleClick = useCallback(() => { console.log('Clicked'); }, []);
// β Bad: Creates new function on every render <Button onClick={() => handleClick(id)} /> // β Good: Stable function reference const onClick = useCallback(() => handleClick(id), [id]); <Button onClick={onClick} />
- Photos: WebP (with JPEG fallback)
- Icons: SVG
- Logos: SVG or PNG
<img loading="lazy" src="image.jpg" alt="Description" />
<img srcSet="image-320w.jpg 320w, image-640w.jpg 640w, image-1280w.jpg 1280w" sizes="(max-width: 320px) 280px, (max-width: 640px) 600px, 1200px" src="image-640w.jpg" alt="Description" />
// Use limit() to fetch only what's needed const q = query( collection(db, 'tasks'), where('assignedClasses', 'array-contains', classId), orderBy('deadline', 'desc'), limit(20) // Only fetch 20 items );
// Implement pagination for large lists const [lastDoc, setLastDoc] = useState(null); const fetchMore = async () => { const q = query( collection(db, 'tasks'), orderBy('deadline'), startAfter(lastDoc), limit(10) ); // ... };
useEffect(() => { const unsubscribe = onSnapshot(q, (snapshot) => { setData(snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }))); }); // Always cleanup! return () => unsubscribe(); }, []);
// β Bad: Derived state const [tasks, setTasks] = useState([]); const [taskCount, setTaskCount] = useState(0); // β Good: Calculate on render const [tasks, setTasks] = useState([]); const taskCount = tasks.length;
// React automatically batches in event handlers const handleSubmit = () => { setName('John'); setEmail('john@example.com'); setPhone('123-456-7890'); // All updates batched into single re-render };
// Split contexts to prevent unnecessary re-renders // Instead of one large context, use multiple specific ones <AuthContext.Provider> <ThemeContext.Provider> <ToastContext.Provider> <App /> </ToastContext.Provider> </ThemeContext.Provider> </AuthContext.Provider>
Metric Value Status Initial Bundle ~100 kB gzipped β Good Time to Interactive ~1.5s (fast 4G) β Good First Contentful Paint ~0.8s β Good Largest Contentful Paint ~1.2s β Good Total Blocking Time ~100ms β Good Metric Target Current Status Initial Bundle < 150 kB ~100 kB β TTI (4G) < 3s ~1.5s β FCP < 1s ~0.8s β LCP < 2.5s ~1.2s β TBT < 300ms ~100ms β
Lighthouse Audit:
1. Open Chrome DevTools (F12) 2. Go to "Lighthouse" tab 3. Select "Performance" 4. Click "Generate report"Performance Tab:
1. Open Chrome DevTools (F12) 2. Go to "Performance" tab 3. Click record 4. Interact with app 5. Stop recording 6. Analyze timelineAnalyze Bundle:
npm run build ## Open dist/stats.html to see bundle compositionTest Production Build:
npm run build npm run preview ## Test at http://localhost:4173Install web-vitals:
npm install web-vitals
Measure in App:
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'; getCLS(console.log); getFID(console.log); getFCP(console.log); getLCP(console.log); getTTFB(console.log);
-
Image Optimization
- Convert images to WebP
- Add responsive images
- Implement lazy loading
-
Font Optimization
- Use font-display: swap
- Preload critical fonts
- Subset fonts
-
CSS Optimization
- Remove unused Tailwind classes
- Inline critical CSS
- Use CSS containment
-
Service Worker
- Cache static assets
- Offline support
- Background sync
-
Virtual Scrolling
- For long lists (gradebook, student list)
- Render only visible items
- Improve perceived performance
-
Database Optimization
- Add composite indexes
- Implement data aggregation
- Use Firestore bundles
-
Server-Side Rendering (SSR)
- Faster initial page load
- Better SEO
- Improved time to interactive
-
Edge Computing
- Deploy to edge locations
- Reduce latency
- Faster API responses
-
Advanced Caching
- Redis for frequently accessed data
- CDN for static assets
- Smart cache invalidation
- Run production build
- Analyze bundle size
- Test on slow 3G connection
- Check Lighthouse score (> 90)
- Verify no console errors
- Test lazy loading works
- Check all routes load properly
- Verify images optimized
- Test on mobile devices
- Check memory leaks
- Monitor real user metrics
- Track bundle size over time
- Check error rates
- Monitor API response times
- Verify caching works
- Check mobile performance
- Monitor Firebase usage
- Track conversion rates
- Chrome DevTools - Built-in performance profiling
- Lighthouse - Performance auditing
- WebPageTest - Real-world performance testing
- Bundle Analyzer - Bundle composition visualization
- Firebase Performance Monitoring - Real user monitoring
- Sentry - Error tracking & performance
- Google Analytics - User behavior
- Vercel Analytics - Web vitals tracking
Symptoms:
- Slow initial page load
- High FCP/LCP times
Solutions:
- β Implement code splitting
- β Lazy load routes
- β Remove unused dependencies
- β Tree-shake libraries
Symptoms:
- Janky animations
- Delayed interactions
- High TBT
Solutions:
- β Use React.memo
- β Implement useMemo/useCallback
- β Avoid inline functions
- β Optimize re-render triggers
Symptoms:
- Slow data loading
- High Firebase costs
- Poor user experience
Solutions:
- β Use limit() in queries
- β Implement pagination
- β Cache frequently accessed data
- β Use indexes properly
Symptoms:
- Increasing memory usage
- Browser becomes slow
- Tab crashes
Solutions:
- β Clean up listeners in useEffect
- β Cancel pending requests
- β Remove event listeners
- β Clear intervals/timeouts
- Web Performance Best Practices
- React Performance Optimization
- Vite Performance
- Firebase Performance
- Chrome DevTools Performance
For performance-related questions:
- Check this guide first
- Review Chrome DevTools timeline
- Analyze bundle with visualizer
- Profile with React DevTools
- Ask in GitHub Discussions
All notable changes to the ICT STMS project.
- π Fixed
ExamEditorissue where duplicating a question did not switch focus to the new question.
- π Critical Security Update: Upgraded
reactandreact-domto version 19.2.1 to address CVE-2025-55182 (RCE vulnerability). - π Updated
@types/reactand@types/react-domto match the new version.
- π Fixed persistent grade notification bug where the modal appeared on every login.
- ποΈ Consolidated all documentation into
README.mdand removed redundant markdown files (CHANGELOG.md,PROJECT_SUMMARY.md, etc.).
- β Full dark mode support with theme toggle
- β ThemeContext for global theme management
- β Theme persistence in localStorage
- β Smooth transitions between themes
- β All components support dark mode
- β ThemeToggle component in header
- β useBulkOperations custom hook
- β Multi-select with checkboxes
- β Select all functionality
- β Bulk delete with batch operations
- β Bulk grade submissions
- β Bulk update functionality
- β BulkActionsBar floating component
- β Confirmation prompts for destructive actions
- β i18next integration
- β English (EN) and Indonesian (ID) translations
- β LanguageToggle component in header
- β Language persistence in localStorage
- β Translations for common UI elements
- β Navigation, tasks, grades, auth translations
- β Easy to add new languages
- β Enhanced mobile-first design
- β Responsive sidebar (overlay on mobile)
- β Touch-friendly buttons and inputs
- β Optimized layouts for all screen sizes
- β Mobile breakpoint adjustments
- β Horizontal scroll tables on mobile
- β Vitest test framework setup
- β React Testing Library integration
- β
48 unit and integration tests
- 8 tests for class sorting utilities
- 21 tests for file utilities
- 4 tests for Toast component
- 5 tests for authentication flow
- 10 tests for task management flow
- β Test configuration files (vitest.config.js, tests/setup.js)
- β Test scripts in package.json (test, test:ui, test:coverage)
- β Testing documentation (TESTING_GUIDE.md)
- β
Complete API documentation (docs/API.md)
- Utility functions reference
- Custom hooks documentation
- Firebase operations guide
- Context API reference
- β
Firebase Schema documentation (docs/FIREBASE_SCHEMA.md)
- All collections structure
- Field definitions and types
- Indexes and relationships
- Security rules examples
- β
Component documentation (docs/COMPONENTS.md)
- Reusable components guide
- Layout components
- Page components
- Props reference
- Styling guidelines
- β
Architecture documentation (docs/ARCHITECTURE.md)
- System overview
- Technology stack
- Project structure
- Data flow diagrams
- Security architecture
- Performance optimizations
- β
Setup guide (docs/SETUP.md)
- Step-by-step installation
- Firebase configuration
- Troubleshooting
- Deployment guide
- β Documentation index (docs/README.md)
- β File upload component with validation
- β Toast notification system
- β Real-time grade notifications
- β Exam system with multiple question types
- β Exam results with retake functionality
- β Gradebook with comprehensive statistics
- β Task filtering and sorting
- β Class sorting utilities
- β URL linkification in submissions
- π Partially translated UI from Indonesian to English (in progress)
- β Student pages (Tasks, Overview, Grades)
- β Teacher pages (Tasks, Classes, Students, Overview, Exams)
- β³ Remaining pages need translation
- π Updated README.md with documentation links
- π Project branding to "ICT STMS" (ICT Codehub LMS)
- π Enhanced DashboardLayout with theme and language toggles
- π Teacher Dashboard routes updated (added Calendar, Analytics)
- π Improved sidebar navigation with new menu items
- ποΈ Removed legacy folder (old HTML files)
- ποΈ Cleaned up backup files
- π Test assertion fix in task-flow.test.jsx
- π Tailwind CSS configuration warnings
- π Firebase security rules documented
- π Input validation for all forms
- π File type and size validation
- π Role-based access control
- Total Files: 75+ files
- Components: 42+ components
- Pages: 24+ pages
- Utils: 6 utility modules
- Hooks: 3 custom hooks
- Contexts: 2 contexts (Auth, Theme)
- Tests: 48 tests (100% passing)
- Total Packages: 452 packages
- Production Dependencies: 15 packages
- Dev Dependencies: 20 packages
- Vulnerabilities: 0
- Test Files: 5
- Total Tests: 48
- Pass Rate: 100%
- Duration: ~3.6 seconds
- Total Docs: 10 files
- API Functions Documented: 20+
- Components Documented: 15+
- Database Collections: 6
- New Features Documented: 6
- Basic authentication system
- User roles (Admin, Teacher, Student)
- Class management
- Task management
- Submission system
- Grading system
- Gradebook
- Exam system
- File uploads
- Real-time updates
- Notifications
- Unit testing
- Integration testing
- API documentation
- Component documentation
- Architecture documentation
- Setup guide
- Performance optimization
- Bundle size optimization
- Lazy loading
- Code splitting
- Translation completion
- Calendar view for deadlines
- Bulk operations
- Advanced analytics
- Mobile app version
- Dark mode
- Multi-language support
- Email notifications
- PDF export
- Advanced search
- File attachments for tasks
- Remove legacy folder
- Remove backup files
- Code refactoring
- Performance audit
- β Chrome (latest)
- β Firefox (latest)
- β Safari (latest)
- β Edge (latest)
- Development: Active on localhost
- Staging: TBD
- Production: TBD (Firebase Hosting)
- Ajit Prasetiyo - Original Developer
- Factory Droid - Documentation & Testing
- π΄ Translation incomplete (100+ Indonesian text remaining)
- π‘ Legacy folder cleanup pending
- π‘ Performance optimization needed for large datasets
- π‘ Bundle size optimization needed
- π’ Dark mode not implemented
- π’ Mobile responsiveness can be improved
- π’ Email notifications not implemented
None yet - project is pre-v1.0
No migrations required yet. When database schema changes, migration scripts will be documented here.
MIT License - See LICENSE file for details
- Documentation: docs/README.md
- Issues: GitHub Issues
- Email: TBD
- Complete translation to English
- Complete testing (80%+ coverage)
- Performance optimization
- Production deployment
- User documentation
- Dark mode
- Email notifications
- Calendar view
- PDF export
- Advanced analytics
- Multi-language support
- Mobile app (React Native)
- Offline mode
- Advanced collaboration features
- Video/audio attachments
- Pink Revision Status: Updated "Needs Revision" status badge to pink (
text-pink-700,bg-pink-50) in both Teacher and Student dashboards for better visibility. - Revision Alert: Added a persistent pink alert box in the Student Task detail view (both View and Edit modes) when a revision is requested.
- Grading Modal: Refactored Teacher Grading Modal for a "Microsoft Fluent UI" look, removed scrollbars, and implemented a minimalist details panel.
- Status Prioritization: Fixed logic in Student Tasks to prioritize "Revision Needed" status over "Submitted" date.
- React Team - For the amazing library
- Firebase - For backend infrastructure
- Tailwind CSS - For the styling framework
- Vite - For the blazing fast build tool
- Vitest - For the testing framework
- All open-source contributors
β If this project helps you, please give it a star on GitHub!