fix(firebase): fix save fcm token logic

This commit is contained in:
sankalp committed 2025-04-22 16:33:51 +05:45
1 parent 4b23e1b531
commit 200661be65
6 files changed
+309 -204

No files matched your search

+2 -2
View File
@@ -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:
+32 -4
View File
@@ -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)
+136 -161
View File
@@ -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):
+1 -2
View File
@@ -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:
+116 -35
View File
@@ -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);
});
+22
View File
@@ -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());
});
});
});