Spaces:
Sleeping
Sleeping
| # Component Design — Quran App | |
| This document is the mid-level component map for the entire system. Every component, module, and service is described with its responsibilities, interfaces, dependencies, and internal state. Mermaid diagrams show how components relate to each other within each layer and across layers. | |
| This document is the reference used by per-flow design documents (e.g. `session-end-flow/design.md`). Per-flow documents go deeper into sequence, error handling, and correctness properties for a specific feature. | |
| --- | |
| ## 1. System Component Map | |
| ```mermaid | |
| graph TB | |
| subgraph Browser["Browser — React App"] | |
| subgraph Providers["Context Providers"] | |
| AUTH[AuthContext] | |
| LANG[LanguageContext] | |
| STU[StudentContext] | |
| end | |
| subgraph Pages["Page Components"] | |
| AUTH_PAGES[Auth Pages] | |
| STUDENT_PAGES[Student Pages] | |
| SHEIKH_PAGES[Sheikh Pages] | |
| ADMIN_PAGES[Admin Pages] | |
| SESSION_PAGES[Session Pages] | |
| PRACTICE_PAGES[Practice Pages] | |
| end | |
| subgraph APILayer["API Layer (lib/api/)"] | |
| BOOKING_API[bookingApi] | |
| STUDENT_API[studentApi] | |
| PAYMENT_API[paymentApi] | |
| AI_API[aiApi] | |
| ADMIN_API[adminApi] | |
| SHEIKH_API[sheikhApi] | |
| end | |
| WEBRTC[useWebRTC hook] | |
| end | |
| subgraph Backend["Spring Boot Backend"] | |
| subgraph Security["Security Layer"] | |
| JWT_FILTER[JwtAuthFilter] | |
| SEC_CONFIG[SecurityConfig] | |
| end | |
| subgraph Controllers["Controllers"] | |
| AUTH_CTRL[AuthController] | |
| STU_CTRL[StudentControllers] | |
| SHEIKH_CTRL[SheikhControllers] | |
| ADMIN_CTRL[AdminControllers] | |
| PAY_CTRL[PaymentController] | |
| WS_CTRL[SignalingController] | |
| end | |
| subgraph Services["Services"] | |
| AUTH_SVC[AuthService] | |
| STU_SVC[StudentServices] | |
| SHEIKH_SVC[SheikhServices] | |
| SESSION_SVC[SessionService] | |
| PAY_SVC[PaymentServiceStripe] | |
| SCHEDULER[TaskScheduler] | |
| end | |
| subgraph Repos["Repositories"] | |
| USER_REPO[UserRepository] | |
| SESSION_REPO[SessionRepository] | |
| PAY_REPO[PaymentRepository] | |
| RECIT_REPO[RecitationRepository] | |
| AVAIL_REPO[AvailabilityRepository] | |
| REVIEW_REPO[ReviewRepository] | |
| end | |
| end | |
| subgraph External["External Services"] | |
| STRIPE[Stripe API] | |
| GOOGLE[Google OAuth] | |
| AI_SVC[AI Service] | |
| GMAIL[Gmail SMTP] | |
| DB[(PostgreSQL)] | |
| end | |
| Pages --> Providers | |
| Pages --> APILayer | |
| WEBRTC --> Browser | |
| APILayer -->|REST HTTP| Controllers | |
| WEBRTC -->|WebSocket STOMP| WS_CTRL | |
| JWT_FILTER --> Controllers | |
| Controllers --> Services | |
| Services --> Repos | |
| Repos --> DB | |
| PAY_SVC --> STRIPE | |
| AUTH_SVC --> GOOGLE | |
| AUTH_SVC --> GMAIL | |
| STU_SVC -->|audio + surah| AI_SVC | |
| SCHEDULER --> SESSION_SVC | |
| ``` | |
| --- | |
| ## 2. Frontend Components | |
| ### 2.1 Context Providers | |
| ```mermaid | |
| classDiagram | |
| class AuthContext { | |
| +user: User | null | |
| +loading: boolean | |
| +signIn(email, password): Promise~User~ | |
| +signInWithGoogle(idToken, role): Promise~User~ | |
| +signUp(email, password, ...): Promise~void~ | |
| +signOut(): Promise~void~ | |
| +updateProfile(updates): void | |
| +fetchStreak(): Promise~Streak~ | |
| +fetchPracticeStats(): Promise~RecitationStats~ | |
| } | |
| class LanguageContext { | |
| +language: "ar" | "en" | |
| +isRTL: boolean | |
| +setLanguage(lang): void | |
| } | |
| class StudentContext { | |
| +studentData: StudentData | |
| +reload(): void | |
| } | |
| AuthContext --> LanguageContext : consumed by | |
| StudentContext --> AuthContext : depends on | |
| ``` | |
| **AuthContext** is the most critical context. It: | |
| - Stores the authenticated `User` object in React state and `localStorage` | |
| - On app load, re-calls `GET /api/auth/verify-profile` to restore + validate session | |
| - Exposes all auth operations (signIn, signUp, signOut, Google OAuth) | |
| - JWT token is managed here — stored in `localStorage.authToken` | |
| **LanguageContext** manages Arabic/English switching and RTL/LTR direction. All page components consume it for translations and layout direction. | |
| **StudentContext** caches student-specific dashboard data to avoid re-fetching on every render. | |
| --- | |
| ### 2.2 Route Guard | |
| ```mermaid | |
| flowchart TD | |
| Request[Route Request] --> Loading{loading?} | |
| Loading -->|yes| Spinner[Show Spinner] | |
| Loading -->|no| Auth{user exists?} | |
| Auth -->|no| SignIn[Redirect /signin] | |
| Auth -->|yes| Admin{role = admin?} | |
| Admin -->|yes| Render[Render Page] | |
| Admin -->|no| Profile{profileCompleted?} | |
| Profile -->|no| Complete[Redirect /complete-profile] | |
| Profile -->|yes| Render | |
| ``` | |
| `ProtectedRoute` wraps all authenticated routes. Admin role skips the profile completion check because admin accounts are created manually. | |
| --- | |
| ### 2.3 Auth Pages | |
| ```mermaid | |
| graph LR | |
| LandingPage -->|sign in| SignIn | |
| LandingPage -->|register| Register | |
| SignIn -->|forgot| ForgotPasswordRequest | |
| ForgotPasswordRequest -->|OTP sent| VerifyOtp | |
| VerifyOtp -->|verified| ResetPassword | |
| SignIn -->|google| GoogleAuth | |
| Register -->|success| CompleteProfile | |
| CompleteProfile -->|student| CompleteStudentProfile | |
| CompleteProfile -->|sheikh| CompleteSheikhProfile | |
| CompleteSheikhProfile -->|submitted| InterviewSchedule | |
| ``` | |
| | Component | Route | Responsibility | | |
| |---|---|---| | |
| | `LandingPage` | `/` | Public landing, links to sign in / register | | |
| | `SignIn` | `/signin` | Email + password login form | | |
| | `Register` | `/register` | Registration form (student or sheikh role) | | |
| | `ForgotPasswordRequest` | `/forgot-password` | Email input → triggers OTP email | | |
| | `VerifyOtp` | `/verify-otp` | 6-digit OTP entry | | |
| | `ResetPassword` | `/reset-password` | New password form | | |
| | `GoogleAuth` | `/auth/google` | Handles Google OAuth redirect callback | | |
| | `CompleteProfile` | `/complete-profile` | Role selection (student / sheikh) | | |
| | `CompleteStudentProfile` | `/complete-profile/student` | Quran level, language, accent wizard | | |
| | `CompleteSheikhProfile` | `/complete-profile/sheikh` | Bio, specialization, certificate upload | | |
| | `InterviewSchedule` | `/sheikh/interview` | Pending sheikh sees interview date | | |
| --- | |
| ### 2.4 Dashboard Components | |
| ```mermaid | |
| graph TB | |
| subgraph StudentDashboard | |
| STREAK[Streak Widget] | |
| STATS[Practice Stats] | |
| RECENT_REC[Recent Recitations] | |
| UPCOMING_SES[Upcoming Sessions] | |
| QUICK_ACTIONS[Quick Actions] | |
| end | |
| subgraph SheikhDashboard | |
| PENDING_REQ[Pending Requests] | |
| SHEIKH_STATS[Session Stats] | |
| SHEIKH_STUDENTS[Recent Students] | |
| RATING_WIDGET[Rating Widget] | |
| end | |
| subgraph AdminDashboard | |
| PLATFORM_STATS[Platform Statistics] | |
| USER_OVERVIEW[User Overview] | |
| SESSION_OVERVIEW[Session Overview] | |
| REVENUE[Revenue Stats] | |
| end | |
| StudentDashboard --> studentApi | |
| SheikhDashboard --> sheikhApi | |
| AdminDashboard --> adminApi | |
| ``` | |
| Each dashboard is a single page component that orchestrates multiple widgets. All data is fetched on mount via parallel `Promise.allSettled` calls — individual widget failures do not crash the dashboard. | |
| --- | |
| ### 2.5 Session Components | |
| This is the most complex area. Components are sequenced along the session lifecycle: | |
| ```mermaid | |
| graph LR | |
| MySessions -->|join button active| WaitingRoom | |
| WaitingRoom -->|session time reached| LiveSession | |
| LiveSession -->|sheikh ends| MySessions | |
| LiveSession -->|student leaves| SessionEndWaiting | |
| SessionEndWaiting -->|status = COMPLETED| SessionReport | |
| SessionEndWaiting -->|status = MISSED/CANCELLED| MySessions | |
| SessionReport -->|submit review| MySessions | |
| ``` | |
| | Component | Route | Purpose | Key State | | |
| |---|---|---|---| | |
| | `MySessions` | `/student/sessions` `/sheikh/sessions` | Session list for both roles — tabs for PENDING, UPCOMING, COMPLETED, CANCELLED | `sessions[]`, `focusSession`, `paymentSession` | | |
| | `WaitingRoom` | `/waiting-room/:id` | Countdown until session starts, "Enter Session" enabled at T-5min | `session`, `now` (1s interval) | | |
| | `LiveSession` | `/session/:id` | Full video call UI — camera/mic controls, chat, sheikh notes, role-specific end/leave buttons | `localStream`, `remoteStream`, `isEndingSession`, `sessionNotes` | | |
| | `SessionEndWaiting` | `/session-waiting/:id` | Student waiting screen after leaving — polls status every 5s | `sessionData`, polling interval | | |
| | `SessionReport` | `/session-report/:id` | Post-session summary + review form | `sessionData`, `review` | | |
| | `SheikhInterviewSession` | `/sheikh/interview-session/:id` | Interview call between admin and pending sheikh | Same as LiveSession | | |
| --- | |
| ### 2.6 Practice Components | |
| ```mermaid | |
| sequenceDiagram | |
| participant User | |
| participant RecordingPractice | |
| participant Backend | |
| participant AIService | |
| User->>RecordingPractice: select surah, record/upload audio | |
| RecordingPractice->>Backend: POST /api/student/recitations (multipart) | |
| Backend->>AIService: POST /analyze { audio, surah_number } | |
| AIService-->>Backend: { overall_stats, ayahs[], user_text } | |
| Backend-->>RecordingPractice: RecitationDetailsDto | |
| RecordingPractice->>User: show scores + mistake breakdown | |
| ``` | |
| | Component | Route | Purpose | Key State | | |
| |---|---|---|---| | |
| | `RecordingPractice` | `/student/practice` | Audio recording (MediaRecorder) or file upload, submit to AI, show results | `audioBlob`, `evaluationResult`, `isProcessing` | | |
| | `EvaluationReports` | `/student/evaluations/:id?` | History list of all recitations, detail dialog on click | `recitations[]`, `selected` detail | | |
| --- | |
| ### 2.7 Sheikh Discovery Components | |
| ```mermaid | |
| graph TB | |
| FindSheikh -->|search + filter| SheikhList[Sheikh Cards] | |
| SheikhList -->|click| SheikhProfile | |
| SheikhProfile -->|select date| Calendar[Available Dates] | |
| Calendar -->|select slot| TimeSlots[Time Slot Chips] | |
| TimeSlots -->|book| BookingDialog[Booking Confirmation] | |
| BookingDialog -->|confirm| PaymentModal[Stripe Payment Modal] | |
| PaymentModal -->|success| MySessions | |
| ``` | |
| | Component | Route | Purpose | | |
| |---|---|---| | |
| | `FindSheikh` | `/student/find-sheikh` | Search/filter sheikh list — filter by country, price, rating, language | | |
| | `SheikhProfile` | `/student/sheikh/:id` | Full sheikh profile, availability calendar, booking form, reviews | | |
| `SheikhProfile` is the most complex student-facing component. It manages: sheikh data fetching, calendar availability loading, time slot selection, booking confirmation dialog, and Stripe payment modal — all in one component. | |
| --- | |
| ### 2.8 Admin Components | |
| ```mermaid | |
| graph TB | |
| AdminDashboard --> SheikhApproval | |
| AdminDashboard --> UserManagement | |
| SheikhApproval -->|click sheikh| UserProfileView | |
| UserManagement -->|click user| UserProfileView | |
| SheikhApproval -->|schedule interview| InterviewSession | |
| ``` | |
| | Component | Route | Purpose | | |
| |---|---|---| | |
| | `SheikhApproval` | `/admin/sheikh-approval` | Queue of pending sheikhs — approve, reject, schedule interview | | |
| | `UserManagement` | `/admin/users` | All users list with filters — deactivate accounts | | |
| | `UserProfileView` | `/admin/user/:id` | Full user detail (student or sheikh) | | |
| | `InterviewSession` | `/admin/interview/:id` | Live video call with pending sheikh (same WebRTC infrastructure) | | |
| --- | |
| ### 2.9 Shared Components | |
| ```mermaid | |
| graph LR | |
| subgraph Navbar | |
| Logo --> NavLinks | |
| NavLinks --> RoleIndicator | |
| RoleIndicator --> LanguageSwitcher | |
| LanguageSwitcher --> UserMenu | |
| end | |
| subgraph ProfilePage | |
| BasicInfo[Basic Info Form] | |
| PhotoUpload[Profile Photo] | |
| PasswordChange[Change Password] | |
| end | |
| ``` | |
| - **`Navbar`** — top navigation. Links change based on role. Shows current language toggle (AR/EN) and user avatar menu. | |
| - **`RoleSwitcher`** — dev tool for testing different roles. Not shown in production. | |
| - **`ProfilePage`** — shared profile editing for all roles. Route: `/profile`. | |
| - **`ErrorBoundary`** — wraps dashboards. Prevents a single widget crash from taking down the full page. | |
| - **`ui/`** — 50+ shadcn/Radix UI primitive components (Button, Card, Dialog, etc.). No business logic. | |
| --- | |
| ### 2.10 API Layer | |
| Each file in `lib/api/` is responsible for one domain. All files follow the same pattern: a shared `request()` helper that injects the JWT token, handles 401 token expiry, and normalizes errors. | |
| ```mermaid | |
| classDiagram | |
| class bookingApi { | |
| +fetchAvailableDates(sheikhId, from, days) | |
| +fetchAvailableSlotsForDay(sheikhId, date) | |
| +bookSession(request) | |
| +fetchMySessions(status?) | |
| +fetchSheikhSessions(status?) | |
| +acceptSessionRequest(sessionId) | |
| +rejectSessionRequest(sessionId) | |
| +fetchSessionReview(sessionId, role) | |
| +submitSessionReview(sessionId, review) | |
| } | |
| class studentApi { | |
| +submitRecitationAudio(dto) | |
| +fetchRecentRecitations() | |
| +fetchRecitationDetail(id) | |
| +fetchRecitationStats() | |
| +fetchStreak() | |
| +fetchPracticeStats() | |
| +fetchDashboardData() | |
| } | |
| class paymentApi { | |
| +initiateCardPayment(request) | |
| +confirmPaymentWithBackend(paymentIntentId) | |
| } | |
| class aiApi { | |
| +analyzeRecitation(audio, surahNumber) | |
| +checkAiHealth() | |
| +mapAiResponseToDto(aiResponse) | |
| } | |
| class adminApi { | |
| +fetchPendingSheikhs() | |
| +approveSheikh(id) | |
| +rejectSheikh(id, reason) | |
| +scheduleInterview(id, dateTime) | |
| +fetchAllUsers(filters) | |
| +deactivateUser(id) | |
| } | |
| ``` | |
| --- | |
| ### 2.11 useWebRTC Hook | |
| ```mermaid | |
| sequenceDiagram | |
| participant Component as LiveSession.tsx | |
| participant Hook as useWebRTC | |
| participant STOMP as STOMP Client | |
| participant PC as RTCPeerConnection | |
| Component->>Hook: useWebRTC(sessionId, userId) | |
| Hook->>STOMP: connect to /ws | |
| Hook->>PC: getUserMedia() → localStream | |
| STOMP-->>Hook: connected | |
| Hook->>STOMP: publish join signal | |
| STOMP-->>Hook: receive join from peer | |
| Hook->>PC: createOffer() | |
| Hook->>STOMP: publish offer | |
| STOMP-->>Hook: receive answer | |
| Hook->>PC: setRemoteDescription(answer) | |
| Note over PC: ICE negotiation | |
| PC-->>Hook: remoteStream available | |
| Hook-->>Component: { localStream, remoteStream, isConnected, messages } | |
| ``` | |
| **Exposed interface:** | |
| ```typescript | |
| { | |
| localStream: MediaStream | null | |
| remoteStream: MediaStream | null | |
| isConnected: boolean | |
| connectionQuality: 'good' | 'fair' | 'poor' | 'unknown' | |
| messages: ChatMessage[] | |
| sendChatMessage(text: string): void | |
| toggleVideo(): void | |
| toggleAudio(): void | |
| } | |
| ``` | |
| Connection quality is measured every 3 seconds via `RTCPeerConnection.getStats()` on the `candidate-pair` report. RTT < 150ms = good, < 400ms = fair, > 400ms = poor. | |
| --- | |
| ## 3. Backend Components | |
| ### 3.1 Security Layer | |
| ```mermaid | |
| sequenceDiagram | |
| participant Client | |
| participant JwtFilter as JwtAuthFilter | |
| participant UDS as UserDetailsService | |
| participant Chain as SecurityFilterChain | |
| participant Controller | |
| Client->>JwtFilter: HTTP Request + Bearer token | |
| JwtFilter->>JwtFilter: extract JWT from Authorization header | |
| JwtFilter->>JwtFilter: jwtService.extractUsername(jwt) | |
| JwtFilter->>UDS: loadUserByUsername(email) | |
| UDS->>JwtFilter: CustomUserDetails | |
| JwtFilter->>JwtFilter: jwtService.isTokenValid(jwt, userDetails) | |
| JwtFilter->>Chain: set SecurityContext authentication | |
| Chain->>Chain: check role-based access rule | |
| Chain->>Controller: forward if authorized | |
| Controller-->>Client: response | |
| ``` | |
| **`JwtAuthenticationFilter`** | |
| - Runs on every request (`OncePerRequestFilter`) | |
| - Extracts `Bearer` token from `Authorization` header | |
| - Validates expiry + signature via `JwtService` | |
| - Sets `SecurityContextHolder` so downstream code can call `authentication.getPrincipal()` | |
| **`CustomUserDetails`** | |
| - Wraps the `User` JPA entity | |
| - Exposes `getUserId()` — used by every controller to identify the caller | |
| - `isEnabled()` returns `user.status == ALIVE` — deactivated users are rejected at auth time | |
| **`SecurityConfig`** | |
| - Stateless sessions (`SessionCreationPolicy.STATELESS`) | |
| - CORS: allows `http://localhost:*` (must be restricted for production) | |
| - Role rules defined as path-prefix matchers (see architecture.md §5 for full table) | |
| --- | |
| ### 3.2 Auth Module (`dashboard/common`) | |
| ```mermaid | |
| classDiagram | |
| class AuthController { | |
| +registerStudent(request) | |
| +registerSheikh(request) | |
| +login(request) | |
| +googleAuth(request) | |
| +forgotPassword(request) | |
| +verifyOtp(request) | |
| +resetPassword(request) | |
| +logout() | |
| +verifyProfile() | |
| +completeGoogleProfile(request) | |
| +completeGoogleSheikhProfile(request) | |
| } | |
| class AuthService { | |
| +registerStudent(request): AuthResponse | |
| +registerSheikh(request): AuthResponse | |
| +login(request): AuthResponse | |
| +googleAuth(request): AuthResponse | |
| +forgotPassword(request): void | |
| +verifyOtp(request): void | |
| +resetPasswordWithOtp(request): void | |
| +logout(userId): void | |
| +verifyProfile(userId): ProfileCheckDto | |
| +completeGoogleProfile(email, request): void | |
| +completeGoogleSheikhProfile(email, request): void | |
| } | |
| class JwtService { | |
| +generateToken(userDetails): String | |
| +extractUsername(token): String | |
| +isTokenValid(token, userDetails): boolean | |
| +extractExpiration(token): Date | |
| } | |
| AuthController --> AuthService | |
| AuthService --> JwtService | |
| AuthService --> UserRepository | |
| ``` | |
| `AuthService.googleAuth()` calls Google's tokeninfo endpoint to verify the `idToken`, then finds or creates the user. If the user is new, `profileCompleted = false` until they fill the profile wizard. | |
| `AuthResponse` is the standard login response: | |
| ```java | |
| { | |
| token: String, // JWT | |
| role: String, | |
| message: String, | |
| user: UserDto, | |
| sheikhApprovalStatus: String // only for sheikh role | |
| } | |
| ``` | |
| --- | |
| ### 3.3 Student Module (`dashboard/student`) | |
| ```mermaid | |
| graph TB | |
| subgraph Controllers | |
| DashCtrl[DashboardController\n/api/student/dashboard] | |
| StuCtrl[StudentController\n/api/student/profile] | |
| SesCtrl[StudentSessionController\n/api/student/sessions] | |
| RecCtrl[StudentRecitationController\n/api/student/recitations] | |
| SheikhCtrl[SheikhController\n/api/sheikhs] | |
| end | |
| subgraph Services | |
| DashSvc[DashboardService] | |
| StuSessSvc[StudentSessionService] | |
| RecitSvc[StudentRecitationService] | |
| FindSvc[SheikhServiceFind] | |
| StreakSvc[StudentStreakService] | |
| end | |
| DashCtrl --> DashSvc | |
| SesCtrl --> StuSessSvc | |
| SesCtrl --> SessionService | |
| RecCtrl --> RecitSvc | |
| SheikhCtrl --> FindSvc | |
| DashSvc --> StreakSvc | |
| ``` | |
| **`StudentRecitationService`** is the most complex student service: | |
| 1. Receives audio `MultipartFile` + `surahNumber` | |
| 2. Converts audio to WAV using JAVE2 (ffmpeg wrapper) | |
| 3. Calls external AI service via `RestTemplate` with `multipart/form-data` | |
| 4. Parses AI JSON response into `AIReport` DTO | |
| 5. Calculates `wordScore`, `tashkeelScore`, `totalScore` from the AI report | |
| 6. Saves `Recitation` entity with `mistakesReportJson` (full AI response as JSONB) | |
| **`StudentSessionService`** handles booking: | |
| - Validates `startDateTime` is in the future | |
| - Calls `SheikhAvailabilityService.validateSessionSlot()` — checks slot is within availability and not already booked by REQUESTED/TOPAY/SCHEDULED sessions | |
| - Creates `Session` with status = `REQUESTED` | |
| --- | |
| ### 3.4 Sheikh Module (`dashboard/sheikh`) | |
| ```mermaid | |
| graph TB | |
| subgraph Controllers | |
| SesCtrl2[SessionController\n/api/sheikh/sessions] | |
| AvailCtrl[AvailabilityController\n/api/sheikh/availability] | |
| StudCtrl[MyStudents\n/api/sheikh/students] | |
| DashCtrl2[sheikhMainDashboard\n/api/sheikh/dashboard] | |
| PendCtrl[pendingSheikhController\n/api/sheikh/profile] | |
| ReviewCtrl[NotesAndReviewsController\n/api/reviews] | |
| end | |
| subgraph Services | |
| SessionSvc[SessionService] | |
| AvailSvc[SheikhAvailabilityService] | |
| StudSvc[MyStudentsServiceImpl] | |
| DashSvc2[sheikhDashboardService] | |
| ReviewSvc[NotesAndReviewsService] | |
| end | |
| SesCtrl2 --> SessionSvc | |
| AvailCtrl --> AvailSvc | |
| StudCtrl --> StudSvc | |
| DashCtrl2 --> DashSvc2 | |
| ReviewCtrl --> ReviewSvc | |
| ``` | |
| **`SessionService`** is the central service for session lifecycle. It owns all status transitions and event-driven scheduling: | |
| ```mermaid | |
| classDiagram | |
| class SessionService { | |
| -taskScheduler: TaskScheduler | |
| +rescheduleExistingSessionsOnStartup() | |
| +updateSessionStatus(sheikhId, sessionId, newStatus) | |
| +recordJoin(userId, sessionId) | |
| +recordLeave(userId, sessionId) | |
| +startSession(userId, sessionId) | |
| +endSession(sheikhId, sessionId, notes) | |
| -scheduleMissedCheck(sessionId, scheduledStart) | |
| -scheduleSheikhNoShowCheck(sessionId, scheduledStart) | |
| -scheduleCancelIfUnpaid(sessionId, scheduledStart) | |
| +checkAndMarkMissed(sessionId) | |
| +checkAndMarkMissedBySheikhNoShow(sessionId) | |
| +cancelIfUnpaid(sessionId) | |
| +triggerRefundForMissedSession(sessionId) | |
| +convertToDto(session): SessionDTO | |
| } | |
| ``` | |
| Three event-driven scheduled tasks fire automatically: | |
| - `scheduleMissedCheck` — fires at `scheduledStart + 1 min`. If neither party joined → `MISSED` + refund | |
| - `scheduleSheikhNoShowCheck` — fires at `scheduledStart + 10 min`. If sheikh didn't join → `MISSED` + refund | |
| - `scheduleCancelIfUnpaid` — fires at `scheduledStart - 5 min`. If still `TOPAY` → `CANCELLED` | |
| All three are registered on startup (`@EventListener(ApplicationReadyEvent)`) for surviving sessions. | |
| **`SheikhAvailabilityService`** manages the weekly recurring availability template: | |
| - `setAvailability()` — replaces overlapping slots with new ones | |
| - `getSlotsForDate()` — generates 1-hour slots within the availability window, minus already-booked sessions | |
| - `validateSessionSlot()` — called at booking time to reject out-of-window or overlapping requests | |
| --- | |
| ### 3.5 Admin Module (`dashboard/admin`) | |
| ```mermaid | |
| graph TB | |
| subgraph Controllers | |
| AdminCtrl[AdminController\n/api/admin/users] | |
| DashCtrl3[AdminMainDashboard\n/api/admin/dashboard] | |
| ApproveCtrl[ShiekhManagementAPProval\n/api/admin/sheikh-approvals] | |
| UserMgmtCtrl[UserManagement] | |
| end | |
| subgraph Services | |
| AdminSvc[AdminService] | |
| DashSvc3[AdminDashboardService] | |
| ApproveSvc[PendingSheikhService] | |
| end | |
| AdminCtrl --> AdminSvc | |
| DashCtrl3 --> DashSvc3 | |
| ApproveCtrl --> ApproveSvc | |
| ``` | |
| Admin module is relatively thin — it reads across all domain repositories to produce statistics and management views. Approval flow: `PENDING → INTERVIEW_SCHEDULED → APPROVED/REJECTED`. | |
| --- | |
| ### 3.6 Payment Module (`payment`) | |
| ```mermaid | |
| classDiagram | |
| class PaymentControllerStripe { | |
| +initiateCard(request): StripePaymentResponse | |
| +confirmPayment(paymentIntentId): StripePaymentResponse | |
| +confirmSession(sessionId): void | |
| +payoutSheikh(sessionId): StripeRefundResponse | |
| +refundStudent(sessionId, reason): StripeRefundResponse | |
| +createSheikhAccount(sheikhId): Map | |
| } | |
| class PaymentServiceStripe { | |
| -commissionPct: 15% | |
| +initiateCardPayment(request): StripePaymentResponse | |
| +confirmPayment(paymentIntentId): StripePaymentResponse | |
| +onSheikhAcceptsSession(sessionId): void | |
| +payoutSheikh(sessionId): StripeRefundResponse | |
| +refundStudent(sessionId, reason): StripeRefundResponse | |
| +createSheikhConnectedAccount(sheikhId): Map | |
| -savePayment(...): Payment | |
| -calcCommission(amount): BigDecimal | |
| } | |
| PaymentControllerStripe --> PaymentServiceStripe | |
| PaymentServiceStripe --> PaymentRepository | |
| PaymentServiceStripe --> SessionRepository | |
| PaymentServiceStripe --> UserRepository | |
| ``` | |
| Payment status lifecycle: | |
| ```mermaid | |
| stateDiagram-v2 | |
| [*] --> PENDING: initiateCardPayment | |
| PENDING --> PAID: confirmPayment (Stripe succeeded) | |
| PENDING --> FAILED: confirmPayment (Stripe failed) | |
| PAID --> PAYOUT_DONE: payoutSheikh | |
| PAID --> REFUNDED: refundStudent | |
| FAILED --> PENDING: retry (old record deleted) | |
| REFUNDED --> [*] | |
| PAYOUT_DONE --> [*] | |
| ``` | |
| If a `PENDING` payment record already exists when `initiateCardPayment` is called again, the existing Stripe PaymentIntent is retrieved and returned rather than creating a duplicate. | |
| --- | |
| ### 3.7 WebSocket / Signaling Module (`websocket`) | |
| ```mermaid | |
| classDiagram | |
| class WebSocketConfig { | |
| +configureMessageBroker(registry) | |
| +registerStompEndpoints(registry) | |
| } | |
| class SignalingController { | |
| -messagingTemplate: SimpMessagingTemplate | |
| +handleSignal(message: SignalingMessage): void | |
| } | |
| class SignalingMessage { | |
| +type: String | |
| +sender: String | |
| +sessionId: String | |
| +data: Object | |
| } | |
| WebSocketConfig --> SignalingController | |
| SignalingController --> SignalingMessage | |
| ``` | |
| `SignalingController` has a single handler: `@MessageMapping("/signal")`. It receives any signaling message and broadcasts it to `/topic/session/{sessionId}`. All session participants (student + sheikh) are subscribed to this topic. The controller does not interpret the message content — that is handled entirely by the browser's WebRTC stack. | |
| --- | |
| ### 3.8 Data Entities | |
| ```mermaid | |
| erDiagram | |
| USER ||--o| STUDENT_PROFILE : has | |
| USER ||--o| SHEIKH_PROFILE : has | |
| USER ||--o| STREAK : has | |
| USER ||--o{ AVAILABILITY : sets | |
| USER ||--o{ SESSION : "books as student" | |
| USER ||--o{ SESSION : "teaches as sheikh" | |
| USER ||--o{ RECITATION : records | |
| USER ||--o{ PAYMENT : "pays as student" | |
| USER ||--o{ PAYMENT : "receives as sheikh" | |
| USER ||--o{ REVIEW : writes | |
| SESSION ||--o| PAYMENT : has | |
| SESSION ||--o| REVIEW : has | |
| USER { | |
| bigint id PK | |
| varchar email UK | |
| varchar passwordHash | |
| varchar fullName | |
| enum role | |
| enum status | |
| varchar authProvider | |
| timestamp createdAt | |
| } | |
| SESSION { | |
| bigint id PK | |
| timestamp scheduledStart | |
| timestamp scheduledEnd | |
| timestamp actualStart | |
| timestamp actualEnd | |
| timestamp studentJoinedAt | |
| timestamp sheikhJoinedAt | |
| enum status | |
| decimal price | |
| } | |
| PAYMENT { | |
| bigint id PK | |
| decimal amount | |
| enum paymentStatus | |
| varchar transactionId UK | |
| decimal platformCommission | |
| decimal sheikhPayout | |
| } | |
| RECITATION { | |
| bigint id PK | |
| varchar surahName | |
| decimal wordScore | |
| decimal tashkeelScore | |
| jsonb mistakesReportJson | |
| } | |
| ``` | |
| --- | |
| ## 4. External Services | |
| ### 4.1 AI Recitation Service | |
| ```mermaid | |
| graph LR | |
| Backend -->|POST /analyze\nmultipart: audio + surah_number| AIService[AI Service\nngrok URL] | |
| Backend -->|GET /\nhealth check| AIService | |
| AIService -->|JSON: surah_info, overall_stats, ayahs| Backend | |
| ``` | |
| - Hosted externally, exposed via ngrok tunnel | |
| - Requires `ngrok-skip-browser-warning: true` header | |
| - Returns per-ayah word-stage and tashkeel-stage analysis | |
| - Scores are 0–100 percentages (not 0–1 fractions) | |
| - Backend stores full response as JSONB in `recitations.mistakes_report_json` | |
| ### 4.2 Stripe | |
| ```mermaid | |
| graph LR | |
| Backend -->|Create PaymentIntent| Stripe | |
| Frontend -->|confirmCardPayment with clientSecret| Stripe | |
| Backend -->|Retrieve PaymentIntent| Stripe | |
| Backend -->|Create Transfer to connected account| Stripe | |
| Backend -->|Create Refund| Stripe | |
| Backend -->|Create Express Account| Stripe | |
| ``` | |
| Two Stripe products are used: | |
| - **Payment Intents** — for student card payments | |
| - **Stripe Connect** (Custom accounts) — for sheikh payouts. Sheikhs get a Stripe connected account with a test bank attached. | |
| ### 4.3 Google OAuth | |
| ```mermaid | |
| graph LR | |
| Frontend -->|Google Identity Services button| Google | |
| Google -->|idToken| Frontend | |
| Frontend -->|POST /api/auth/google { idToken, role }| Backend | |
| Backend -->|verify idToken| GoogleAPIClient[Google API Client] | |
| GoogleAPIClient -->|{ email, name, picture }| Backend | |
| ``` | |
| Google OAuth is used for social login. The backend verifies the `idToken` using the `google-api-client` library. If the email doesn't exist in the DB, a new user is created and directed to the profile completion wizard. | |
| ### 4.4 Gmail SMTP | |
| Used exclusively for OTP password reset emails. Configured with: | |
| - Host: `smtp.gmail.com` | |
| - Port: `587` (STARTTLS) | |
| - Authentication: Gmail app password (not the account password) | |
| --- | |
| ## 5. Component Dependency Summary | |
| ```mermaid | |
| graph TD | |
| subgraph Frontend | |
| FE_AUTH[Auth Pages] --> FE_API[API Layer] | |
| FE_SESSIONS[Session Pages] --> FE_API | |
| FE_PRACTICE[Practice Pages] --> FE_API | |
| FE_ADMIN[Admin Pages] --> FE_API | |
| FE_SESSIONS --> WEBRTC_HOOK[useWebRTC] | |
| FE_API --> AUTH_CTX[AuthContext] | |
| end | |
| subgraph Backend | |
| BE_CTRL[Controllers] --> BE_SVC[Services] | |
| BE_SVC --> BE_REPO[Repositories] | |
| BE_SVC --> BE_SCHEDULER[TaskScheduler] | |
| BE_CTRL --> SEC[Security Layer] | |
| end | |
| FE_API -->|REST| BE_CTRL | |
| WEBRTC_HOOK -->|WebSocket| BE_CTRL | |
| BE_SVC -->|JDBC| DB[(PostgreSQL)] | |
| BE_SVC --> STRIPE[Stripe] | |
| BE_SVC --> AI[AI Service] | |
| BE_SVC --> GMAIL[Gmail SMTP] | |
| SEC --> GOOGLE[Google OAuth] | |
| ``` | |
| --- | |
| ## 6. Flow Index | |
| The following per-flow design documents go deeper into each major feature using the components defined above: | |
| | Flow | Document | Components Involved | | |
| |---|---|---| | |
| | Session End Flow | `.kiro/specs/session-end-flow/design.md` | `LiveSession`, `SessionEndWaiting`, `SessionService`, `PaymentServiceStripe` | | |
| | Authentication Flow | _next_ | `SignIn`, `Register`, `GoogleAuth`, `AuthContext`, `AuthService`, `JwtService` | | |
| | Session Booking Flow | _next_ | `SheikhProfile`, `MySessions`, `bookingApi`, `StudentSessionService`, `SheikhAvailabilityService` | | |
| | Payment Flow | _next_ | `MySessions`, `PaymentModal`, `paymentApi`, `PaymentServiceStripe`, Stripe | | |
| | AI Practice Flow | _next_ | `RecordingPractice`, `EvaluationReports`, `aiApi`, `StudentRecitationService`, AI Service | | |
| | Sheikh Approval Flow | _next_ | `SheikhApproval`, `InterviewSession`, `AdminService`, `SessionService` (WebRTC) | | |
| | WebRTC Session Flow | _next_ | `LiveSession`, `WaitingRoom`, `useWebRTC`, `SignalingController`, `SessionService` | | |