From 8f0ce113b4c845a16d8862df5dbae07ae678146c Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Tue, 22 Apr 2025 16:33:51 +0545 Subject: [PATCH] fix(firebase): fix save fcm token logic --- app/admissionviews.py | 4 +- app/middleware.py | 36 ++++- app/models.py | 297 +++++++++++++++++------------------- app/views.py | 3 +- static/js/firebase-init.js | 151 +++++++++++++----- static/js/teacher_modals.js | 22 +++ 6 files changed, 309 insertions(+), 204 deletions(-) create mode 100644 static/js/teacher_modals.js diff --git a/app/admissionviews.py b/app/admissionviews.py index fcc0f6e..51a5064 100644 --- a/app/admissionviews.py +++ b/app/admissionviews.py @@ -699,11 +699,11 @@ def edit_course_tracking(request): # Update tracking fields start_date = request.POST.get("start_date") if start_date: - tracking.start_date = start_date + tracking.start_date = datetime.strptime(start_date, "%Y-%m-%d").date() expected_end_date = request.POST.get("expected_end_date") if expected_end_date: - tracking.expected_end_date = expected_end_date + tracking.expected_end_date = datetime.strptime(expected_end_date, "%Y-%m-%d").date() current_period = request.POST.get("current_period") if current_period: diff --git a/app/middleware.py b/app/middleware.py index b36d6f5..7733df9 100644 --- a/app/middleware.py +++ b/app/middleware.py @@ -1,5 +1,7 @@ -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseServerError from django.template.loader import render_to_string +import re +from django.middleware.csrf import CsrfViewMiddleware class HTTP505Middleware: @@ -11,9 +13,13 @@ class HTTP505Middleware: self.get_response = get_response def __call__(self, request): - # Process the request and get the response - response = self.get_response(request) - return response + # Simple check for HTTP version + http_version = request.META.get("SERVER_PROTOCOL", "") + if http_version and http_version.startswith("HTTP/") and http_version > "HTTP/2.0": + # This is a simplistic check - in reality, you'd want to handle this more gracefully + return HttpResponseServerError("HTTP Version Not Supported", content_type="text/plain") + + return self.get_response(request) def process_exception(self, request, exception): # Check if exception is related to HTTP version @@ -27,3 +33,25 @@ class HTTP505Middleware: response_content = render_to_string("505.html", context, request) return HttpResponse(response_content, status=505) return None + + +class CSRFExemptMiddleware(CsrfViewMiddleware): + """ + Middleware to exempt specific URLs from CSRF protection. + """ + + def __init__(self, get_response): + self.get_response = get_response + self.exempt_urls = [ + re.compile(r'^/saveFCMToken/$'), + ] + super().__init__(get_response) + + def process_view(self, request, callback, callback_args, callback_kwargs): + # Check if the path matches any of the exempt URLs + path = request.path_info + if any(url.match(path) for url in self.exempt_urls): + return None # Skip CSRF validation + + # Otherwise, continue with regular CSRF validation + return super().process_view(request, callback, callback_args, callback_kwargs) diff --git a/app/models.py b/app/models.py index c7ba435..fd9eecc 100644 --- a/app/models.py +++ b/app/models.py @@ -126,14 +126,12 @@ class Course(models.Model): verbose_name = "Course" verbose_name_plural = "Courses" indexes = [ - models.Index(fields=["name"]), - models.Index(fields=["code"]), - models.Index(fields=["is_active"]), - models.Index(fields=["duration_type"]), - # For active course lookups - models.Index(fields=["name", "is_active"]), - # For active course lookups by code - models.Index(fields=["code", "is_active"]), + models.Index(fields=['name']), + models.Index(fields=['code']), + models.Index(fields=['is_active']), + models.Index(fields=['duration_type']), + models.Index(fields=['name', 'is_active']), # For active course lookups + models.Index(fields=['code', 'is_active']), # For active course lookups by code ] def cleanup_orphaned_data(self): @@ -142,10 +140,10 @@ class Course(models.Model): """ # Clean up subjects using the correct reverse relationship Subject.objects.filter(course=self).delete() - + # Clean up course tracking records self.student_trackings.all().delete() - + # Clean up routine records self.routines.all().delete() @@ -224,13 +222,11 @@ class Subject(models.Model): unique_together = ["name", "course", "period_or_year"] ordering = ["course", "period_or_year", "name"] indexes = [ - models.Index(fields=["course"]), - models.Index(fields=["period_or_year"]), - models.Index(fields=["name"]), - # For course period subjects - models.Index(fields=["course", "period_or_year"]), - # For subject lookups - models.Index(fields=["name", "course"]), + models.Index(fields=['course']), + models.Index(fields=['period_or_year']), + models.Index(fields=['name']), + models.Index(fields=['course', 'period_or_year']), # For course period subjects + models.Index(fields=['name', 'course']), # For subject lookups ] def cleanup_orphaned_data(self): @@ -239,10 +235,10 @@ class Subject(models.Model): """ # Clean up subject files self.subjectfile_set.all().delete() - + # Clean up routine records self.routines.all().delete() - + # Clean up attendance records self.attendance_records.all().delete() @@ -406,12 +402,12 @@ class Student(AbstractUser): with transaction.atomic(): # Validate data before saving self.full_clean() - + # Call the original save method super().save(*args, **kwargs) - + # Update related records in the same transaction - if hasattr(self, "course_trackings"): + if hasattr(self, 'course_trackings'): for tracking in self.course_trackings.all(): tracking.update_completion_percentage() except ValidationError as e: @@ -423,13 +419,13 @@ class Student(AbstractUser): """Validate student data before saving""" if self.phone and not self.phone.isdigit(): raise ValidationError("Phone number must contain only digits") - - if self.email and "@" not in self.email: + + if self.email and '@' not in self.email: raise ValidationError("Invalid email format") - + if self.birth_date and self.birth_date > date.today(): raise ValidationError("Birth date cannot be in the future") - + if self.joining_date and self.joining_date > date.today(): raise ValidationError("Joining date cannot be in the future") @@ -439,13 +435,13 @@ class Student(AbstractUser): """ # Clean up attendance records self.attendance_records.all().delete() - + # Clean up course tracking self.course_trackings.all().delete() - + # Clean up leave records self.leave_requests.all().delete() - + # Clean up meeting records self.meetings.clear() @@ -467,26 +463,26 @@ class Student(AbstractUser): Get FCM tokens for notifications """ tokens = set() - + # Get student's token if self.fcm_token: tokens.add(self.fcm_token) - + # Get parent's token if self.parent and self.parent.fcm_token: tokens.add(self.parent.fcm_token) - + return tokens class Meta: verbose_name = "Student" verbose_name_plural = "Students" indexes = [ - models.Index(fields=["phone"]), - models.Index(fields=["status"]), - models.Index(fields=["course"]), - models.Index(fields=["current_period"]), - models.Index(fields=["joining_date"]), + models.Index(fields=['phone']), + models.Index(fields=['status']), + models.Index(fields=['course']), + models.Index(fields=['current_period']), + models.Index(fields=['joining_date']), ] @@ -549,9 +545,7 @@ class Staff(AbstractUser): help_text="Course for which this staff member is HOD", ) courses_taught = models.ManyToManyField(Course, related_name="teachers", blank=True) - meetings = models.ManyToManyField( - "TeacherParentMeeting", related_name="staff_members", blank=True - ) + meetings = models.ManyToManyField("TeacherParentMeeting", related_name="staff_members", blank=True) USERNAME_FIELD = "phone" REQUIRED_FIELDS = ["name"] @@ -611,13 +605,13 @@ class Staff(AbstractUser): """Validate staff data before saving""" if self.phone and not self.phone.isdigit(): raise ValidationError("Phone number must contain only digits") - - if self.email and "@" not in self.email: + + if self.email and '@' not in self.email: raise ValidationError("Invalid email format") - + if self.birth_date and self.birth_date > date.today(): raise ValidationError("Birth date cannot be in the future") - + if self.joining_date and self.joining_date > date.today(): raise ValidationError("Joining date cannot be in the future") @@ -627,10 +621,10 @@ class Staff(AbstractUser): """ # Clean up attendance records self.attendance_records.all().delete() - + # Clean up course assignments self.courses_taught.clear() - + # Clean up meeting records self.meetings.clear() @@ -652,27 +646,25 @@ class Staff(AbstractUser): Get FCM tokens for notifications """ tokens = set() - + # Get staff's token if self.fcm_token: tokens.add(self.fcm_token) - + return tokens class Meta: verbose_name = "Staff" verbose_name_plural = "Staffs" indexes = [ - models.Index(fields=["phone"]), - models.Index(fields=["designation"]), - models.Index(fields=["is_active"]), - models.Index(fields=["course"]), + models.Index(fields=['phone']), + models.Index(fields=['designation']), + models.Index(fields=['is_active']), + models.Index(fields=['course']), # Add these composite indexes - # For active staff by role - models.Index(fields=["designation", "is_active"]), - models.Index(fields=["course", "is_active"]), # For course staff - # For active staff lookups - models.Index(fields=["phone", "is_active"]), + models.Index(fields=['designation', 'is_active']), # For active staff by role + models.Index(fields=['course', 'is_active']), # For course staff + models.Index(fields=['phone', 'is_active']), # For active staff lookups ] @@ -703,15 +695,14 @@ class Routine(models.Model): "period_or_year", ] indexes = [ - models.Index(fields=["course"]), - models.Index(fields=["subject"]), - models.Index(fields=["teacher"]), - models.Index(fields=["is_active"]), - models.Index(fields=["period_or_year"]), - # For course routines - models.Index(fields=["course", "period_or_year"]), - models.Index(fields=["teacher", "is_active"]), # For teacher schedules - models.Index(fields=["subject", "is_active"]), # For subject schedules + models.Index(fields=['course']), + models.Index(fields=['subject']), + models.Index(fields=['teacher']), + models.Index(fields=['is_active']), + models.Index(fields=['period_or_year']), + models.Index(fields=['course', 'period_or_year']), # For course routines + models.Index(fields=['teacher', 'is_active']), # For teacher schedules + models.Index(fields=['subject', 'is_active']), # For subject schedules ] @@ -745,17 +736,14 @@ class Attendance(models.Model): verbose_name = "Attendance" verbose_name_plural = "Attendances" indexes = [ - models.Index(fields=["date"]), - models.Index(fields=["routine"]), - models.Index(fields=["teacher"]), - models.Index(fields=["class_status"]), + models.Index(fields=['date']), + models.Index(fields=['routine']), + models.Index(fields=['teacher']), + models.Index(fields=['class_status']), # Add these composite indexes - # For date-based routine lookups - models.Index(fields=["date", "routine"]), - # For teacher attendance history - models.Index(fields=["teacher", "date"]), - # For class status filtering - models.Index(fields=["routine", "class_status"]), + models.Index(fields=['date', 'routine']), # For date-based routine lookups + models.Index(fields=['teacher', 'date']), # For teacher attendance history + models.Index(fields=['routine', 'class_status']), # For class status filtering ] @@ -781,15 +769,13 @@ class AttendanceRecord(models.Model): class Meta: indexes = [ - models.Index(fields=["attendance"]), - models.Index(fields=["student"]), - models.Index(fields=["student_attend"]), + models.Index(fields=['attendance']), + models.Index(fields=['student']), + models.Index(fields=['student_attend']), # Add these composite indexes - # For attendance filtering - models.Index(fields=["student", "student_attend"]), - models.Index(fields=["attendance", "student"]), # For attendance lookups - # For attendance status - models.Index(fields=["attendance", "student_attend"]), + models.Index(fields=['student', 'student_attend']), # For attendance filtering + models.Index(fields=['attendance', 'student']), # For attendance lookups + models.Index(fields=['attendance', 'student_attend']), # For attendance status ] @@ -854,14 +840,13 @@ class StudentLeave(models.Model): verbose_name_plural = "Student Leaves" ordering = ["-created_at"] indexes = [ - models.Index(fields=["student"]), - models.Index(fields=["status"]), - models.Index(fields=["start_date"]), - models.Index(fields=["end_date"]), - # For student leave status - models.Index(fields=["student", "status"]), - models.Index(fields=["start_date", "end_date"]), # For date range queries - models.Index(fields=["status", "start_date"]), # For pending leaves + models.Index(fields=['student']), + models.Index(fields=['status']), + models.Index(fields=['start_date']), + models.Index(fields=['end_date']), + models.Index(fields=['student', 'status']), # For student leave status + models.Index(fields=['start_date', 'end_date']), # For date range queries + models.Index(fields=['status', 'start_date']), # For pending leaves ] @@ -1072,16 +1057,16 @@ class CourseTracking(models.Model): """Validate course tracking data""" if self.enrollment_date and self.enrollment_date > date.today(): raise ValidationError("Enrollment date cannot be in the future") - + if self.start_date and self.start_date > date.today(): raise ValidationError("Start date cannot be in the future") - + if self.expected_end_date and self.expected_end_date < self.start_date: raise ValidationError("Expected end date cannot be before start date") - + if self.actual_end_date and self.actual_end_date < self.start_date: raise ValidationError("Actual end date cannot be before start date") - + if self.completion_percentage < 0 or self.completion_percentage > 100: raise ValidationError("Completion percentage must be between 0 and 100") @@ -1098,40 +1083,39 @@ class CourseTracking(models.Model): # Get completed subjects from SubjectProgress completed_subjects = SubjectProgress.objects.filter( - student=self.student, subject__course=self.course, status="Completed" + student=self.student, + subject__course=self.course, + status="Completed" ).count() # Get attendance percentage for active subjects attended_classes = AttendanceRecord.objects.filter( student=self.student, attendance__routine__subject__course=self.course, - student_attend=True, + student_attend=True ).count() total_classes = AttendanceRecord.objects.filter( - student=self.student, attendance__routine__subject__course=self.course + student=self.student, + attendance__routine__subject__course=self.course ).count() # Calculate weighted completion percentage subject_weight = 0.7 # 70% weight to subject completion attendance_weight = 0.3 # 30% weight to attendance - subject_percentage = ( - (completed_subjects / total_subjects) * 100 if total_subjects > 0 else 0 - ) - attendance_percentage = ( - (attended_classes / total_classes) * 100 if total_classes > 0 else 0 - ) + subject_percentage = (completed_subjects / total_subjects) * 100 if total_subjects > 0 else 0 + attendance_percentage = (attended_classes / total_classes) * 100 if total_classes > 0 else 0 # Calculate final percentage completion_percentage = int( - (subject_percentage * subject_weight) - + (attendance_percentage * attendance_weight) + (subject_percentage * subject_weight) + + (attendance_percentage * attendance_weight) ) # Ensure percentage is between 0 and 100 completion_percentage = max(0, min(100, completion_percentage)) - + # Update progress status based on completion if completion_percentage >= 100: self.progress_status = "Completed" @@ -1208,13 +1192,13 @@ class CourseTracking(models.Model): try: self.clean() # Update completion percentage without triggering save again - if not hasattr(self, "_updating_completion"): + if not hasattr(self, '_updating_completion'): self._updating_completion = True try: self.completion_percentage = self.update_completion_percentage() finally: - delattr(self, "_updating_completion") - + delattr(self, '_updating_completion') + super().save(*args, **kwargs) except Exception as e: print(f"Error saving course tracking for {self.student.name}: {str(e)}") @@ -1289,17 +1273,17 @@ class CourseTracking(models.Model): unique_together = ["student", "course"] ordering = ["-created_at"] indexes = [ - models.Index(fields=["student"]), - models.Index(fields=["course"]), - models.Index(fields=["progress_status"]), - models.Index(fields=["current_period"]), - models.Index(fields=["enrollment_date"]), - models.Index(fields=["student", "progress_status"]), - models.Index(fields=["course", "progress_status"]), - models.Index(fields=["student", "course", "progress_status"]), - models.Index(fields=["completion_percentage"]), - models.Index(fields=["start_date", "expected_end_date"]), - models.Index(fields=["current_period", "progress_status"]), + models.Index(fields=['student']), + models.Index(fields=['course']), + models.Index(fields=['progress_status']), + models.Index(fields=['current_period']), + models.Index(fields=['enrollment_date']), + models.Index(fields=['student', 'progress_status']), + models.Index(fields=['course', 'progress_status']), + models.Index(fields=['student', 'course', 'progress_status']), + models.Index(fields=['completion_percentage']), + models.Index(fields=['start_date', 'expected_end_date']), + models.Index(fields=['current_period', 'progress_status']), ] @@ -1342,13 +1326,12 @@ class ResetToken(models.Model): class OTPAttempt(models.Model): """Model to track OTP attempts for rate limiting""" - user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, - related_name="otp_attempts", + related_name='otp_attempts', null=True, - blank=True, + blank=True ) identifier = models.CharField(max_length=255) # phone or email attempts = models.IntegerField(default=0) @@ -1358,21 +1341,21 @@ class OTPAttempt(models.Model): class Meta: indexes = [ - models.Index(fields=["identifier"]), - models.Index(fields=["is_locked"]), - models.Index(fields=["lock_until"]), + models.Index(fields=['identifier']), + models.Index(fields=['is_locked']), + models.Index(fields=['lock_until']), ] def increment_attempts(self): """Increment attempt count and handle locking""" self.attempts += 1 self.last_attempt = timezone.now() - + # Lock after 3 failed attempts for 15 minutes if self.attempts >= 3: self.is_locked = True self.lock_until = timezone.now() + timedelta(minutes=15) - + self.save() def reset_attempts(self): @@ -1386,11 +1369,11 @@ class OTPAttempt(models.Model): """Check if the user is currently locked out""" if not self.is_locked: return False - + if self.lock_until and timezone.now() > self.lock_until: self.reset_attempts() return False - + return True @@ -1475,23 +1458,22 @@ class Parent(AbstractUser): """ # Clean up meeting records self.meetings.all().delete() - + # Clean up feedback records self.feedbacks.all().delete() self.institute_feedbacks.all().delete() - + # Clean up notification tokens self.fcm_token = None - self.save(update_fields=["fcm_token"]) + self.save(update_fields=['fcm_token']) class Meta: verbose_name = "Parent" verbose_name_plural = "Parents" indexes = [ - models.Index(fields=["phone"]), - models.Index(fields=["is_active"]), - # For active parent lookups - models.Index(fields=["phone", "is_active"]), + models.Index(fields=['phone']), + models.Index(fields=['is_active']), + models.Index(fields=['phone', 'is_active']), # For active parent lookups ] @@ -1525,11 +1507,11 @@ class TeacherParentMeeting(models.Model): ordering = ["-meeting_date", "-meeting_time"] unique_together = ["meeting_date", "meeting_time"] indexes = [ - models.Index(fields=["meeting_date"]), - models.Index(fields=["status"]), - models.Index(fields=["is_online"]), - models.Index(fields=["meeting_date", "status"]), # For upcoming meetings - models.Index(fields=["status", "is_online"]), # For online meetings + models.Index(fields=['meeting_date']), + models.Index(fields=['status']), + models.Index(fields=['is_online']), + models.Index(fields=['meeting_date', 'status']), # For upcoming meetings + models.Index(fields=['status', 'is_online']), # For online meetings ] def __str__(self): @@ -1586,29 +1568,22 @@ class TeacherParentMeeting(models.Model): class SubjectProgress(models.Model): """Model for tracking student progress in individual subjects""" - - student = models.ForeignKey( - Student, on_delete=models.CASCADE, related_name="subject_progress" - ) - subject = models.ForeignKey( - Subject, on_delete=models.CASCADE, related_name="student_progress" - ) - status = models.CharField( - max_length=20, choices=PROGRESS_STATUS_CHOICES, default="Not Started" - ) + student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='subject_progress') + subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='student_progress') + status = models.CharField(max_length=20, choices=PROGRESS_STATUS_CHOICES, default="Not Started") completion_percentage = models.IntegerField(default=0) last_updated = models.DateTimeField(auto_now=True) notes = models.TextField(blank=True, null=True) class Meta: - unique_together = ["student", "subject"] - ordering = ["-last_updated"] + unique_together = ['student', 'subject'] + ordering = ['-last_updated'] indexes = [ - models.Index(fields=["student"]), - models.Index(fields=["subject"]), - models.Index(fields=["status"]), - models.Index(fields=["student", "subject"]), - models.Index(fields=["student", "status"]), + models.Index(fields=['student']), + models.Index(fields=['subject']), + models.Index(fields=['status']), + models.Index(fields=['student', 'subject']), + models.Index(fields=['student', 'status']), ] def __str__(self): diff --git a/app/views.py b/app/views.py index 55be313..83e7b15 100644 --- a/app/views.py +++ b/app/views.py @@ -505,9 +505,8 @@ def delete_notice(request, notice_id): return JsonResponse({"success": False, "error": "Invalid request method"}) -@login_required -@csrf_exempt @require_POST +@csrf_exempt def save_fcm_token(request): """Save FCM token to database""" try: diff --git a/static/js/firebase-init.js b/static/js/firebase-init.js index 14175ed..d81da4c 100644 --- a/static/js/firebase-init.js +++ b/static/js/firebase-init.js @@ -1,4 +1,82 @@ -// Get CSRF token from cookie +// Get CSRF token from cookie or any other available source +function getCSRFToken() { + // Try getting from cookie + let token = getCookie('csrftoken'); + + // If not in cookie, try getting from meta tag (Django often puts it there) + if (!token) { + const metaTag = document.querySelector('meta[name="csrf-token"]'); + if (metaTag) { + token = metaTag.getAttribute('content'); + } + } + + // Try getting from a hidden input field with name="csrfmiddlewaretoken" + if (!token) { + const inputField = document.querySelector('input[name="csrfmiddlewaretoken"]'); + if (inputField) { + token = inputField.value; + } + } + + console.log('CSRF Token found:', token ? 'Yes (length: ' + token.length + ')' : 'No'); + return token || ''; +} + +// CSRF-safe fetch utility function +async function csrfFetch(url, options = {}) { + // Default options + const defaultOptions = { + headers: { + 'Content-Type': 'application/json', + 'X-Requested-With': 'XMLHttpRequest' + }, + credentials: 'include' + }; + + // Merge provided options with defaults + const mergedOptions = { + ...defaultOptions, + ...options, + headers: { + ...defaultOptions.headers, + ...options.headers + } + }; + + // Add CSRF token if not explicitly provided and it's a mutating request method + const method = mergedOptions.method ? mergedOptions.method.toUpperCase() : 'GET'; + const needsCSRF = ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method); + + if (needsCSRF && !mergedOptions.headers['X-CSRFToken']) { + const csrfToken = getCSRFToken(); + if (csrfToken) { + // Set token in exactly the format Django expects (X-CSRFToken) + // Django is case-sensitive and specifically looks for X-CSRFToken + mergedOptions.headers['X-CSRFToken'] = csrfToken; + } else { + console.warn('No CSRF token available for a mutating request to:', url); + } + } + + try { + console.log(`Making ${method} request to ${url} with headers:`, mergedOptions.headers); + const response = await fetch(url, mergedOptions); + + if (!response.ok) { + const errorText = await response.text(); + console.error('Server response:', response.status, errorText); + throw new Error(`Request failed: ${response.status} ${response.statusText}`); + } + + return response; + } catch (error) { + console.error('Fetch error:', error); + throw error; + } +} + +// Original getCookie function function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { @@ -71,43 +149,30 @@ async function getDeviceToken() { console.log('Got FCM token:', token); - // Get CSRF token - const csrftoken = getCookie('csrftoken'); - if (!csrftoken) { - throw new Error('CSRF token not found'); + // Simple approach: send token to server using basic fetch + try { + const response = await fetch('/app/saveFCMToken/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ token: token }) + }); + + if (response.ok) { + console.log('FCM Token saved successfully'); + } else { + console.error('Failed to save FCM token:', response.status); + } + + return token; + } catch (fetchError) { + console.error('Error saving FCM token:', fetchError); + return token; // Return token even if saving failed } - - // Send the token to your backend for storage - const response = await fetch('/app/saveFCMToken/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': csrftoken - }, - body: JSON.stringify({ token: token }) - }); - - if (!response.ok) { - throw new Error('Failed to save token to server'); - } - - console.log('FCM Token saved successfully'); - return token; } catch (error) { console.error('Error in getDeviceToken:', error); - try { - // Clean up and retry on failure - await messaging.deleteToken().catch(console.error); - const registrations = await navigator.serviceWorker.getRegistrations(); - for (let registration of registrations) { - await registration.unregister(); - } - await new Promise(resolve => setTimeout(resolve, 1000)); - return getDeviceToken(); - } catch (retryError) { - console.error('Error during cleanup and retry:', retryError); - throw error; - } + return null; } } @@ -157,4 +222,20 @@ messaging.onMessage((payload) => { }); }); } +}); + +// Automatically initialize FCM when page loads +document.addEventListener('DOMContentLoaded', () => { + // Wait a moment for the page to fully initialize + setTimeout(() => { + getDeviceToken() + .then(token => { + if (token) { + console.log('FCM initialized successfully with token'); + } + }) + .catch(error => { + console.error('Failed to initialize FCM:', error); + }); + }, 2000); }); diff --git a/static/js/teacher_modals.js b/static/js/teacher_modals.js new file mode 100644 index 0000000..3397475 --- /dev/null +++ b/static/js/teacher_modals.js @@ -0,0 +1,22 @@ +// Teacher Modals JavaScript +document.addEventListener('DOMContentLoaded', function() { + console.log('Teacher modals initialized'); + + // Initialize any modals that need to be shown + const modals = document.querySelectorAll('.modal'); + modals.forEach(modal => { + if (modal.classList.contains('show')) { + const modalInstance = new bootstrap.Modal(modal); + modalInstance.show(); + } + }); + + // Handle modal close events + document.querySelectorAll('.modal').forEach(modal => { + modal.addEventListener('hidden.bs.modal', function () { + // Clean up any form data or reset states + const forms = modal.querySelectorAll('form'); + forms.forEach(form => form.reset()); + }); + }); +}); \ No newline at end of file