import { useAuth, useCollection, useIdentity } from '@deplixo/sdk'; /* @component-map * App — Main container, tab navigation [app.jsx] * RosterManager — Create and manage class roster [components/RosterManager.jsx] * AttendanceView — Today's attendance status and check-in [components/AttendanceView.jsx] * CheckIn — QR code display and code entry for check-in [components/CheckIn.jsx] * AttendanceHistory — View past attendance records [components/AttendanceHistory.jsx] * @end-component-map */ // DEPLOY_CONFIG: {"triggers": [{"name": "email_teacher_on_3_consecutive_absences", "on": "collection.add", "collection": "attendance", "actions": [{"type": "email", "to": "{{teacher_email}}", "subject": "Student missed 3 consecutive classes", "body": "A student has missed 3 consecutive classes. Please review the attendance record and follow up."}]}]} import { useEffect, useMemo, useState } from 'react'; import { RosterManager } from './components/RosterManager.jsx'; import { AttendanceView } from './components/AttendanceView.jsx'; import { CheckIn } from './components/CheckIn.jsx'; import { AttendanceHistory } from './components/AttendanceHistory.jsx'; function App() { const { user, loading, login, logout } = useAuth(); const { user: identityUser, loading: identityLoading } = useIdentity(); const { items: rosterItems, add: addRosterItem, update: updateRosterItem } = useCollection('rosters', { personal: true }); const { items: profileItems, add: addProfileItem, update: updateProfileItem } = useCollection('appProfiles', { personal: true }); const [activeTab, setActiveTab] = useState('attendance'); const [studentIdentity, setStudentIdentity] = useState(null); const tabs = [ { id: 'attendance', label: 'Today', icon: '📋' }, { id: 'checkin', label: 'Check In', icon: '📱' }, { id: 'roster', label: 'Roster', icon: '👥' }, { id: 'history', label: 'History', icon: '📊' }, ]; const teacherIdentity = useMemo(() => { const source = user || identityUser; if (!source) return null; return { id: source.id || source.email || source.uid || 'teacher', name: source.name || source.displayName || source.email || 'Teacher', email: source.email || '', avatar: source.avatar || source.photoURL || '', role: 'teacher', provider: source.provider || 'google', }; }, [user, identityUser]); const studentRoster = useMemo(() => { const items = Array.isArray(rosterItems) ? rosterItems : []; return items .map((item) => ({ ...item, id: item.id || item.studentId || item.name, studentId: item.studentId || item.id || '', name: item.name || 'Unnamed Student', })) .sort((a, b) => String(a.name).localeCompare(String(b.name))); }, [rosterItems]); useEffect(() => { if (!teacherIdentity) return; const existing = Array.isArray(profileItems) ? profileItems[0] : null; const profile = { id: existing?.id, ownerId: teacherIdentity.id, teacher: teacherIdentity, role: teacherIdentity.role, updatedAt: new Date().toISOString(), }; if (existing?.id) { updateProfileItem(existing.id, profile); } else if (addProfileItem) { addProfileItem(profile); } }, [teacherIdentity, profileItems, addProfileItem, updateProfileItem]); useEffect(() => { const saved = Array.isArray(profileItems) ? profileItems.find((item) => item?.studentIdentity?.active) : null; if (saved?.studentIdentity) setStudentIdentity(saved.studentIdentity); }, [profileItems]); const handleTeacherSignIn = async () => { try { await login('google'); } catch (error) { console.error('Google sign-in failed:', error); } }; const handleStudentSelect = (student) => { const selected = { id: student.id, studentId: student.studentId || student.id, name: student.name, role: 'student', rosterId: teacherIdentity?.id || 'local-class', active: true, selectedAt: new Date().toISOString(), }; setStudentIdentity(selected); const existing = Array.isArray(profileItems) ? profileItems[0] : null; const nextProfile = { id: existing?.id, ownerId: teacherIdentity?.id || selected.rosterId, teacher: teacherIdentity, studentIdentity: selected, updatedAt: new Date().toISOString(), }; if (existing?.id) { updateProfileItem(existing.id, nextProfile); } else if (addProfileItem) { addProfileItem(nextProfile); } }; const appIdentity = teacherIdentity || studentIdentity || { id: 'guest', name: 'Guest', role: 'guest', }; const sharedProps = { identity: appIdentity, teacherIdentity, studentIdentity, roster: studentRoster, onStudentSelect: handleStudentSelect, isAuthenticated: Boolean(teacherIdentity || studentIdentity), }; if (loading || identityLoading) { return (