4sp-dv 1.1.8 → 1.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/4simpleproblems_v5.html +98 -1
- package/logged-in/dashboard.html +2 -82
- package/navigation.js +2 -2
- package/package.json +1 -1
package/4simpleproblems_v5.html
CHANGED
|
@@ -1116,8 +1116,12 @@
|
|
|
1116
1116
|
|
|
1117
1117
|
const docRef = doc(db, 'analytics', sessionId);
|
|
1118
1118
|
|
|
1119
|
+
// --- FIX: Determine userId before writing ---
|
|
1120
|
+
const currentUid = (auth.currentUser && auth.currentUser.uid) ? auth.currentUser.uid : 'anonymous';
|
|
1121
|
+
|
|
1119
1122
|
// Using Firestore functions imported at top
|
|
1120
1123
|
setDoc(docRef, {
|
|
1124
|
+
userId: currentUid, // <--- ADDED THIS FIELD
|
|
1121
1125
|
sessionId: sessionId,
|
|
1122
1126
|
userAgent: navigator.userAgent,
|
|
1123
1127
|
lastActive: serverTimestamp(),
|
|
@@ -1274,7 +1278,66 @@
|
|
|
1274
1278
|
// Initial fetch to check admin status (less frequent change)
|
|
1275
1279
|
const adminDoc = await getDoc(doc(db, "admins", ownerUid));
|
|
1276
1280
|
|
|
1277
|
-
userDataUnsubscribe = onSnapshot(doc(db, "users", ownerUid), (docSnap) => {
|
|
1281
|
+
userDataUnsubscribe = onSnapshot(doc(db, "users", ownerUid), async (docSnap) => {
|
|
1282
|
+
// --- INTEGRITY CHECK START ---
|
|
1283
|
+
let needsRepair = false;
|
|
1284
|
+
let missingFields = [];
|
|
1285
|
+
|
|
1286
|
+
if (!docSnap.exists()) {
|
|
1287
|
+
console.warn("User document missing. Attempting repair...");
|
|
1288
|
+
needsRepair = true;
|
|
1289
|
+
missingFields = ['document_missing'];
|
|
1290
|
+
} else {
|
|
1291
|
+
const d = docSnap.data();
|
|
1292
|
+
// Required fields based on connection.html schema
|
|
1293
|
+
if (!d.email) missingFields.push('email');
|
|
1294
|
+
if (!d.username) missingFields.push('username');
|
|
1295
|
+
if (!d.authMethod) missingFields.push('authMethod');
|
|
1296
|
+
if (!d.createdAt) missingFields.push('createdAt');
|
|
1297
|
+
// emailVerified is boolean, check for undefined
|
|
1298
|
+
if (d.emailVerified === undefined) missingFields.push('emailVerified');
|
|
1299
|
+
|
|
1300
|
+
if (missingFields.length > 0) {
|
|
1301
|
+
console.warn("User document incomplete:", missingFields);
|
|
1302
|
+
needsRepair = true;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
if (needsRepair) {
|
|
1307
|
+
const user = auth.currentUser;
|
|
1308
|
+
if (user && user.uid === ownerUid) {
|
|
1309
|
+
try {
|
|
1310
|
+
const repairData = {
|
|
1311
|
+
email: user.email,
|
|
1312
|
+
// Guess auth method or default to unknown if complex
|
|
1313
|
+
authMethod: user.providerData.length > 0 ? user.providerData[0].providerId : 'unknown',
|
|
1314
|
+
username: user.displayName || user.email.split('@')[0].replace(/[^a-zA-Z0-9]/g, '').slice(0,16),
|
|
1315
|
+
emailVerified: user.emailVerified,
|
|
1316
|
+
photoURL: user.photoURL || null
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
// Add createdAt only if missing to avoid overwriting existing timestamp (if any)
|
|
1320
|
+
// If doc missing, we need it.
|
|
1321
|
+
if (!docSnap.exists() || !docSnap.data().createdAt) {
|
|
1322
|
+
repairData.createdAt = serverTimestamp();
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
await setDoc(doc(db, "users", ownerUid), repairData, { merge: true });
|
|
1326
|
+
console.log("User document auto-repaired.");
|
|
1327
|
+
// The snapshot listener will fire again with the fix, so we return here to avoid double processing UI
|
|
1328
|
+
return;
|
|
1329
|
+
} catch (err) {
|
|
1330
|
+
console.error("Auto-repair failed:", err);
|
|
1331
|
+
showIntegrityAlert(missingFields);
|
|
1332
|
+
}
|
|
1333
|
+
} else {
|
|
1334
|
+
showIntegrityAlert(missingFields);
|
|
1335
|
+
}
|
|
1336
|
+
} else {
|
|
1337
|
+
hideIntegrityAlert();
|
|
1338
|
+
}
|
|
1339
|
+
// --- INTEGRITY CHECK END ---
|
|
1340
|
+
|
|
1278
1341
|
if (docSnap.exists()) {
|
|
1279
1342
|
const data = docSnap.data();
|
|
1280
1343
|
// Merge with existing local data to preserve properties not in basic user doc if any
|
|
@@ -1325,6 +1388,40 @@
|
|
|
1325
1388
|
initAnalytics(db, ownerUid);
|
|
1326
1389
|
}
|
|
1327
1390
|
}
|
|
1391
|
+
|
|
1392
|
+
function showIntegrityAlert(issues) {
|
|
1393
|
+
let alertBox = document.getElementById('integrity-alert-box');
|
|
1394
|
+
if (!alertBox) {
|
|
1395
|
+
alertBox = document.createElement('div');
|
|
1396
|
+
alertBox.id = 'integrity-alert-box';
|
|
1397
|
+
alertBox.className = 'fixed bottom-4 right-4 bg-red-900/90 border border-red-500/50 text-white px-4 py-3 rounded-xl shadow-2xl flex items-center gap-4 z-[10000] backdrop-blur-md transition-all duration-300 transform translate-y-20 opacity-0';
|
|
1398
|
+
alertBox.innerHTML = `
|
|
1399
|
+
<div class="flex items-center gap-3">
|
|
1400
|
+
<i class="fa-solid fa-triangle-exclamation text-red-400 text-xl"></i>
|
|
1401
|
+
<div class="flex flex-col">
|
|
1402
|
+
<span class="font-bold text-sm">Account Sync Error</span>
|
|
1403
|
+
<span class="text-xs text-gray-300">Your account data is incomplete.</span>
|
|
1404
|
+
</div>
|
|
1405
|
+
</div>
|
|
1406
|
+
<a href="https://4sp-organization.github.io/connection.html" target="_blank" class="bg-white/10 hover:bg-white/20 text-white text-xs font-medium py-2 px-4 rounded-lg transition border border-white/10 whitespace-nowrap">
|
|
1407
|
+
Redirect <i class="fa-solid fa-arrow-right ml-1"></i>
|
|
1408
|
+
</a>
|
|
1409
|
+
`;
|
|
1410
|
+
document.body.appendChild(alertBox);
|
|
1411
|
+
// Animate in
|
|
1412
|
+
setTimeout(() => {
|
|
1413
|
+
alertBox.classList.remove('translate-y-20', 'opacity-0');
|
|
1414
|
+
}, 100);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
function hideIntegrityAlert() {
|
|
1419
|
+
const alertBox = document.getElementById('integrity-alert-box');
|
|
1420
|
+
if (alertBox) {
|
|
1421
|
+
alertBox.classList.add('translate-y-20', 'opacity-0');
|
|
1422
|
+
setTimeout(() => alertBox.remove(), 300);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1328
1425
|
|
|
1329
1426
|
// Helper to update the settings panel preview if open
|
|
1330
1427
|
function updateSettingsPfpPreview(user, previewEl) {
|
package/logged-in/dashboard.html
CHANGED
|
@@ -369,38 +369,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
369
369
|
</div>
|
|
370
370
|
</div>
|
|
371
371
|
|
|
372
|
-
<button id="stats-btn" class="fixed bottom-8 left-8 w-10 h-10 rounded-xl bg-black border border-[#333] text-gray-400 flex items-center justify-center hover:bg-[#111] hover:text-white hover:scale-110 transition-all duration-300 z-50">
|
|
373
|
-
<i class="fa-solid fa-chart-simple"></i>
|
|
374
|
-
</button>
|
|
375
372
|
|
|
376
|
-
<div id="stats-dropdown" class="fixed bottom-20 left-8 bg-black border border-[#333] rounded-[18px] p-4 min-w-[240px] opacity-0 pointer-events-none transform translate-y-4 transition-all duration-300 z-50 shadow-2xl">
|
|
377
|
-
<div class="flex items-center gap-3 mb-4 pb-3 border-b border-[#222]">
|
|
378
|
-
<div class="w-10 h-10 rounded-full bg-[#111] flex items-center justify-center text-white font-bold text-lg" id="stat-avatar">
|
|
379
|
-
?
|
|
380
|
-
</div>
|
|
381
|
-
<div>
|
|
382
|
-
<div class="text-white text-sm font-medium" id="stat-email">Loading...</div>
|
|
383
|
-
<div class="text-xs text-gray-500 font-mono" id="stat-uid">...</div>
|
|
384
|
-
</div>
|
|
385
|
-
</div>
|
|
386
|
-
|
|
387
|
-
<div class="space-y-3">
|
|
388
|
-
<div>
|
|
389
|
-
<div class="text-[10px] uppercase tracking-wider text-gray-600 font-semibold mb-1">Member Since</div>
|
|
390
|
-
<div class="text-gray-300 text-sm" id="stat-created">...</div>
|
|
391
|
-
</div>
|
|
392
|
-
<div>
|
|
393
|
-
<div class="text-[10px] uppercase tracking-wider text-gray-600 font-semibold mb-1">Last Sign In</div>
|
|
394
|
-
<div class="text-gray-300 text-sm" id="stat-last-login">...</div>
|
|
395
|
-
</div>
|
|
396
|
-
<div>
|
|
397
|
-
<div class="text-[10px] uppercase tracking-wider text-gray-600 font-semibold mb-1">Provider</div>
|
|
398
|
-
<div class="text-gray-300 text-sm flex items-center gap-2" id="stat-provider">
|
|
399
|
-
...
|
|
400
|
-
</div>
|
|
401
|
-
</div>
|
|
402
|
-
</div>
|
|
403
|
-
</div>
|
|
404
373
|
|
|
405
374
|
<button id="retry-weather-btn" onclick="retryWeather()" title="Retry Weather">
|
|
406
375
|
<i class="fa-solid fa-cloud-sun"></i>
|
|
@@ -410,57 +379,8 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
410
379
|
import { firebaseConfig } from '../firebase-config.js';
|
|
411
380
|
|
|
412
381
|
// --- STATS DROPDOWN LOGIC ---
|
|
413
|
-
|
|
414
|
-
const statsDropdown = document.getElementById('stats-dropdown');
|
|
415
|
-
|
|
416
|
-
if(statsBtn && statsDropdown) {
|
|
417
|
-
statsBtn.addEventListener('click', (e) => {
|
|
418
|
-
e.stopPropagation();
|
|
419
|
-
const isVisible = statsDropdown.classList.contains('opacity-100');
|
|
420
|
-
if (isVisible) {
|
|
421
|
-
statsDropdown.classList.remove('opacity-100', 'translate-y-0', 'pointer-events-auto');
|
|
422
|
-
statsDropdown.classList.add('opacity-0', 'translate-y-4', 'pointer-events-none');
|
|
423
|
-
} else {
|
|
424
|
-
statsDropdown.classList.add('opacity-100', 'translate-y-0', 'pointer-events-auto');
|
|
425
|
-
statsDropdown.classList.remove('opacity-0', 'translate-y-4', 'pointer-events-none');
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
// Close when clicking outside
|
|
430
|
-
document.addEventListener('click', (e) => {
|
|
431
|
-
if (!statsDropdown.contains(e.target) && !statsBtn.contains(e.target)) {
|
|
432
|
-
statsDropdown.classList.remove('opacity-100', 'translate-y-0', 'pointer-events-auto');
|
|
433
|
-
statsDropdown.classList.add('opacity-0', 'translate-y-4', 'pointer-events-none');
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
}
|
|
382
|
+
// Removed as per request.
|
|
437
383
|
|
|
438
|
-
function populateStats(user) {
|
|
439
|
-
if (!user) return;
|
|
440
|
-
|
|
441
|
-
// Email & UID
|
|
442
|
-
document.getElementById('stat-email').textContent = user.email || 'Anonymous';
|
|
443
|
-
document.getElementById('stat-uid').textContent = user.uid.substring(0, 12) + '...';
|
|
444
|
-
document.getElementById('stat-avatar').textContent = (user.email || 'A').charAt(0).toUpperCase();
|
|
445
|
-
|
|
446
|
-
// Dates
|
|
447
|
-
const created = new Date(user.metadata.creationTime);
|
|
448
|
-
const lastLogin = new Date(user.metadata.lastSignInTime);
|
|
449
|
-
|
|
450
|
-
document.getElementById('stat-created').textContent = created.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' });
|
|
451
|
-
document.getElementById('stat-last-login').textContent = lastLogin.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) + ' at ' + lastLogin.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
|
|
452
|
-
|
|
453
|
-
// Provider
|
|
454
|
-
const providerId = user.providerData[0]?.providerId || 'password';
|
|
455
|
-
let providerIcon = 'fa-envelope';
|
|
456
|
-
let providerName = 'Email';
|
|
457
|
-
|
|
458
|
-
if (providerId.includes('google')) { providerIcon = 'fa-google'; providerName = 'Google'; }
|
|
459
|
-
else if (providerId.includes('github')) { providerIcon = 'fa-github'; providerName = 'GitHub'; }
|
|
460
|
-
else if (providerId.includes('microsoft')) { providerIcon = 'fa-microsoft'; providerName = 'Microsoft'; }
|
|
461
|
-
|
|
462
|
-
document.getElementById('stat-provider').innerHTML = `<i class="fa-brands ${providerIcon}"></i> ${providerName}`;
|
|
463
|
-
}
|
|
464
384
|
|
|
465
385
|
// --- GLOBAL STATE ---
|
|
466
386
|
let CURRENT_LAT = null;
|
|
@@ -802,7 +722,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
802
722
|
if (firebase.auth) {
|
|
803
723
|
firebase.auth().onAuthStateChanged((user) => {
|
|
804
724
|
if (user) {
|
|
805
|
-
|
|
725
|
+
// Stats removed
|
|
806
726
|
} else if (!window._LOCAL_MODE) {
|
|
807
727
|
window.location.replace(redirectPath);
|
|
808
728
|
}
|
package/navigation.js
CHANGED
|
@@ -603,8 +603,8 @@ let db;
|
|
|
603
603
|
const container = document.getElementById('navbar-container');
|
|
604
604
|
if (!container) return;
|
|
605
605
|
|
|
606
|
-
|
|
607
|
-
const tabWrapper =
|
|
606
|
+
// Fix for navigation.js:607 crash
|
|
607
|
+
const tabWrapper = container.querySelector('.tab-wrapper');
|
|
608
608
|
const authControlsWrapper = document.getElementById('auth-controls-wrapper');
|
|
609
609
|
const navbarLogo = document.getElementById('navbar-logo');
|
|
610
610
|
|