-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinvite.html
More file actions
148 lines (131 loc) · 8 KB
/
invite.html
File metadata and controls
148 lines (131 loc) · 8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notify | Class Invitation</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.2/css/all.min.css">
<style>
body { font-family: 'Inter', sans-serif; }
.font-poppins { font-family: 'Poppins', sans-serif; }
.btn-bubbly { transition: transform 0.1s ease-out; }
.btn-bubbly:active { transform: scale(0.95); }
</style>
</head>
<body class="bg-slate-100 flex items-center justify-center h-screen p-4">
<div id="status-container" class="w-full max-w-md text-center p-8 bg-white rounded-xl shadow-lg">
<div id="icon-container">
<i class="fa-solid fa-spinner fa-spin fa-3x text-blue-500"></i>
</div>
<h1 id="status-heading" class="text-2xl font-bold font-poppins text-gray-800 mt-5">Processing Invitation...</h1>
<p id="status-message" class="text-gray-600 mt-2">Please wait while we verify your invite code.</p>
<div class="mt-6">
<a href="dashboard.html" id="dashboard-link" class="hidden inline-block px-6 py-2.5 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 btn-bubbly">Go to Dashboard</a>
<a href="login.html" id="login-link" class="hidden inline-block px-6 py-2.5 bg-green-500 text-white font-semibold rounded-lg hover:bg-green-600 btn-bubbly">Login or Sign Up</a>
</div>
</div>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js";
import { getAuth, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-auth.js";
import { getFirestore, doc, getDoc, collection, query, where, updateDoc, arrayUnion, addDoc, serverTimestamp, getDocs } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-firestore.js";
import { firebaseConfig } from "../firebase-config.js";
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const iconContainer = document.getElementById('icon-container');
const statusHeading = document.getElementById('status-heading');
const statusMessage = document.getElementById('status-message');
const dashboardLink = document.getElementById('dashboard-link');
const loginLink = document.getElementById('login-link');
function updateStatus(icon, heading, message, showDashboard = false, showLogin = false) {
iconContainer.innerHTML = icon;
statusHeading.textContent = heading;
statusMessage.textContent = message;
dashboardLink.classList.toggle('hidden', !showDashboard);
loginLink.classList.toggle('hidden', !showLogin);
}
window.addEventListener('DOMContentLoaded', () => {
const urlParams = new URLSearchParams(window.location.search);
const inviteCode = urlParams.get('code');
if (!inviteCode) {
updateStatus(
'<i class="fa-solid fa-link-slash fa-3x text-red-500"></i>',
'Invalid Link',
'No invite code was found in the URL. Please use a valid invitation link.',
true
);
return;
}
onAuthStateChanged(auth, async (user) => {
if (user) {
const userDoc = await getDoc(doc(db, 'users', user.uid));
if (!userDoc.exists()) {
updateStatus(
'<i class="fa-solid fa-user-xmark fa-3x text-red-500"></i>',
'Account Error',
'We could not find your user profile. Please try signing in again.',
false, true
);
return;
}
const userData = { uid: user.uid, ...userDoc.data() };
processInvite(inviteCode, userData);
} else {
sessionStorage.setItem('postLoginRedirect', window.location.href);
updateStatus(
'<i class="fa-solid fa-right-to-bracket fa-3x text-gray-500"></i>',
'Authentication Required',
'Please log in or sign up to accept this invitation. Once you are logged in, you will be automatically redirected.',
false, true
);
}
});
});
async function processInvite(code, userData) {
const qStudent = query(collection(db, "classes"), where("studentInviteCode", "==", code));
const qTeacher = query(collection(db, "classes"), where("teacherInviteCode", "==", code));
const [studentSnap, teacherSnap] = await Promise.all([getDocs(qStudent), getDocs(qTeacher)]);
const classDoc = studentSnap.docs[0] || teacherSnap.docs[0];
if (!classDoc) {
updateStatus('<i class="fa-solid fa-circle-xmark fa-3x text-red-500"></i>', 'Invalid Code', 'This invitation code is not valid or has expired.', true);
return;
}
const classId = classDoc.id;
const classData = classDoc.data();
const isMember = (classData.studentIds || []).includes(userData.uid) || (classData.teacherIds || []).includes(userData.uid) || classData.teacherId === userData.uid;
if (isMember) {
updateStatus('<i class="fa-solid fa-check-double fa-3x text-blue-500"></i>', 'Already Enrolled', `You are already a member of "${classData.name}".`, true);
return;
}
if (!studentSnap.empty) { // Student invite
await updateDoc(doc(db, 'classes', classId), { studentIds: arrayUnion(userData.uid) });
updateStatus('<i class="fa-solid fa-circle-check fa-3x text-green-500"></i>', 'Success!', `You have been added to "${classData.name}" as a student.`, true);
} else if (!teacherSnap.empty) { // Teacher invite
if (userData.role !== 'teacher') {
updateStatus('<i class="fa-solid fa-user-slash fa-3x text-orange-500"></i>', 'Role Mismatch', `This is a co-teacher invite, but your account is a student account. You cannot accept this invitation.`, true);
return;
}
// Check if a request already exists
const reqQuery = query(collection(db, 'classes', classId, 'coTeacherRequests'), where('requesterId', '==', userData.uid));
const existingReqs = await getDocs(reqQuery);
if (!existingReqs.empty) {
updateStatus('<i class="fa-solid fa-paper-plane fa-3x text-blue-500"></i>', 'Request Already Sent', `You have already requested to co-teach "${classData.name}". The owner will review it soon.`, true);
return;
}
await addDoc(collection(db, 'classes', classId, 'coTeacherRequests'), {
requesterId: userData.uid,
requesterName: userData.username,
requesterEmail: userData.email,
status: 'pending',
createdAt: serverTimestamp()
});
updateStatus('<i class="fa-solid fa-paper-plane fa-3x text-blue-500"></i>', 'Request Sent', `Your request to join "${classData.name}" as a co-teacher has been sent to the class owner for approval.`, true);
}
}
</script>
</body>
</html>