mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-03-11 17:49:25 -05:00
updated the community map, rem username, long, latt
This commit is contained in:
@@ -2,7 +2,7 @@ import { injectStyles } from './modules/styles.js';
|
||||
import { renderLayout, updateNavState } from './modules/ui.js?v=2';
|
||||
import { getSession } from './modules/state.js?v=2';
|
||||
import { openModal, closeModal, handleToggle, handleAuth, handleLogout, setMode, verifySession } from './modules/auth.js?v=2';
|
||||
import { openProfileModal, closeProfileModal, handleProfileUpdate } from './modules/profile.js';
|
||||
import { openProfileModal, closeProfileModal, handleProfileUpdate, geocodeAndSetCoordinates } from './modules/profile.js';
|
||||
import { setupCameraEvents } from './modules/camera.js';
|
||||
import { getBasePath } from './modules/config.js';
|
||||
|
||||
@@ -85,15 +85,18 @@ import { getBasePath } from './modules/config.js';
|
||||
if (profileLogoutBtn) profileLogoutBtn.addEventListener('click', handleLogout);
|
||||
if (profileForm) profileForm.addEventListener('submit', handleProfileUpdate);
|
||||
|
||||
// Add blur event listener for geocoding the location
|
||||
const profileLocationInput = document.getElementById('profileLocation');
|
||||
if (profileLocationInput) {
|
||||
profileLocationInput.addEventListener('blur', () => {
|
||||
geocodeAndSetCoordinates(profileLocationInput.value);
|
||||
});
|
||||
}
|
||||
|
||||
// Check for redirect action
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const action = params.get('action');
|
||||
|
||||
if (params.get('community')) {
|
||||
window.location.href = getBasePath() + '/community.html';
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'login') {
|
||||
localStorage.removeItem("tinytorch_token");
|
||||
localStorage.removeItem("tinytorch_refresh_token");
|
||||
@@ -112,5 +115,8 @@ import { getBasePath } from './modules/config.js';
|
||||
if (!isLoggedIn) {
|
||||
openModal('signup');
|
||||
}
|
||||
} else if (params.get('community')) {
|
||||
window.location.href = getBasePath() + '/community.html';
|
||||
return;
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -339,18 +339,8 @@
|
||||
{lat: 22.5726, lng: 88.3639, name: "Kolkata"}
|
||||
];
|
||||
|
||||
// Fallback data if API fails or returns empty (preserving original demo data)
|
||||
const fallbackLocations = [
|
||||
{"latitude": 22, "longitude": 88, "user": "dev_guru_88", "completed": 85, "institution": "IIT Kharagpur"},
|
||||
{"latitude": 12.61315, "longitude": 38.37723, "user": "ethiopia_code", "completed": 92, "institution": "Addis Ababa Univ"},
|
||||
{"latitude": -30, "longitude": -58, "user": "arg_hacker", "completed": 64, "institution": "Univ of Buenos Aires"},
|
||||
{"latitude": -14.270972, "longitude": -170.132217, "user": "island_coder", "completed": 78, "institution": "Samoa Polytech"},
|
||||
{"latitude": 28.033886, "longitude": 1.659626, "user": "sahara_js", "completed": 45, "institution": "Univ of Algiers"},
|
||||
{"latitude": 40.463667, "longitude": -3.74922, "user": "madrid_frontend", "completed": 99, "institution": "Complutense Univ"},
|
||||
{"latitude": 35.907757, "longitude": 127.766922, "user": "seoul_surfer", "completed": 88, "institution": "KAIST"},
|
||||
{"latitude": 23.634501, "longitude": -102.552784, "user": "mx_fullstack", "completed": 72, "institution": "UNAM"},
|
||||
{"latitude": 42.3736, "longitude": -71.1097, "user": "crimson_dev", "completed": 95, "institution": "Harvard University"}
|
||||
];
|
||||
// Fallback data is now disabled to ensure only real-time information is shown.
|
||||
const fallbackLocations = [];
|
||||
|
||||
let locations = [];
|
||||
let institutions = []; // Aggregated institution data
|
||||
@@ -497,13 +487,16 @@
|
||||
? { latitude: p.latitude, longitude: p.longitude }
|
||||
: getRoughCoords(p.location || p.institution || p.username);
|
||||
|
||||
const displayName = (p.display_name && p.display_name.trim()) ? p.display_name.trim() : (p.username ? p.username.split('@')[0].trim() : 'Anonymous');
|
||||
|
||||
return {
|
||||
latitude: roughCoords.latitude,
|
||||
longitude: roughCoords.longitude,
|
||||
user: p.username,
|
||||
displayName: p.display_name || p.username,
|
||||
displayName: displayName,
|
||||
completed: "Member",
|
||||
institution: Array.isArray(p.institution) ? p.institution.join(", ") : (p.institution || "Independent")
|
||||
institution: Array.isArray(p.institution) ? p.institution.join(", ") : (p.institution || "Independent"),
|
||||
location: p.location || null // Store the original location string
|
||||
};
|
||||
});
|
||||
|
||||
@@ -527,17 +520,14 @@
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// If no results (and no query), use fallback
|
||||
if (!query && locations.length === 0) {
|
||||
locations = fallbackLocations.map(l => ({...l, displayName: l.user, institution: l.institution || "Independent"}));
|
||||
} else if (query) {
|
||||
// If searching and no results, clear map
|
||||
// If API returns no results, clear the map.
|
||||
if (query) {
|
||||
locations = [];
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Fetch failed, using fallback data.", e);
|
||||
if (locations.length === 0) locations = fallbackLocations.map(l => ({...l, displayName: l.user, institution: l.institution || "Independent"}));
|
||||
console.warn("Fetch failed, clearing locations.", e);
|
||||
locations = []; // Clear locations on API failure.
|
||||
}
|
||||
|
||||
renderMarkers();
|
||||
@@ -807,6 +797,7 @@
|
||||
<h3>${d.displayName}</h3>
|
||||
<div class="info-row">Status: <span class="highlight">${d.completed}</span></div>
|
||||
<div class="info-row"><i>${d.institution}</i></div>
|
||||
${d.location === "Lost at Sea 🌊" ? `<div class="info-row">${d.location}</div>` : ''}
|
||||
`);
|
||||
|
||||
tooltip
|
||||
|
||||
@@ -686,7 +686,7 @@
|
||||
// Show all completers (scrolling enabled via CSS)
|
||||
data.forEach(user => {
|
||||
const date = new Date(user.finished_at).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
||||
const displayName = user.name || user.username || user.email || "Anonymous";
|
||||
const displayName = (user.display_name && user.display_name.trim()) ? user.display_name.trim() : (user.username ? user.username.split('@')[0].trim() : 'Anonymous');
|
||||
|
||||
let instHtml = "";
|
||||
if (user.institution) {
|
||||
@@ -1374,24 +1374,29 @@
|
||||
<div id="p-loading" style="display:block; text-align:center; color:#888;">Loading profile...</div>
|
||||
|
||||
<div id="p-content" style="display:none;">
|
||||
<h2 id="p-name" style="margin:0 0 5px 0; color:#222;">User Name</h2>
|
||||
<div id="p-meta" style="color:#ff6600; font-family:'Courier New', monospace; font-size:0.9rem; margin-bottom:15px;">@username</div>
|
||||
<p id="p-bio" style="color:#555; font-style:italic; margin-bottom:20px;">Bio goes here...</p>
|
||||
|
||||
<div style="background:#f9f9f9; padding:15px; border-radius:8px;">
|
||||
<div style="display:flex; justify-content:space-between; margin-bottom:5px;">
|
||||
<span style="color:#888; font-size:0.8rem; text-transform:uppercase;">Institution</span>
|
||||
<span id="p-inst" style="font-weight:bold; color:#333;">-</span>
|
||||
</div>
|
||||
<div style="display:flex; justify-content:space-between;">
|
||||
<span style="color:#888; font-size:0.8rem; text-transform:uppercase;">Location</span>
|
||||
<span id="p-loc" style="font-weight:bold; color:#333;">-</span>
|
||||
<div style="display: flex; align-items: center; gap: 20px; margin-bottom: 20px;">
|
||||
<img id="p-avatar" src="" alt="Avatar" style="width: 80px; height: 80px; border-radius: 50%; object-fit: cover; background: #eee; border: 2px solid #ff6600;">
|
||||
<div>
|
||||
<h2 id="p-name" style="margin:0; color:#222; font-size: 1.5rem;">User Name</h2>
|
||||
<div id="p-meta" style="color:#666; font-family:'Courier New', monospace;">@username</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:20px; text-align:center;">
|
||||
<span id="p-modules-count" style="font-size:2rem; font-weight:bold; color:#333;">0</span>
|
||||
<div style="font-size:0.8rem; color:#888; text-transform:uppercase; letter-spacing:1px;">Modules Completed</div>
|
||||
<p id="p-bio" style="color:#555; font-style:italic; margin-bottom:25px; border-left: 3px solid #ffefe0; padding-left: 15px; font-size: 0.95rem;">Bio goes here...</p>
|
||||
|
||||
<div style="background:#f9f9f9; padding:15px; border-radius:8px; font-size: 0.9rem;">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px; padding-bottom: 8px; border-bottom: 1px dashed #eee;">
|
||||
<span style="color:#888;">Institution</span>
|
||||
<span id="p-inst" style="font-weight:bold; color:#333; text-align:right;">-</span>
|
||||
</div>
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px; padding-bottom: 8px; border-bottom: 1px dashed #eee;">
|
||||
<span style="color:#888;">Location</span>
|
||||
<span id="p-loc" style="font-weight:bold; color:#333; text-align:right;">-</span>
|
||||
</div>
|
||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<span style="color:#888;">Modules Completed</span>
|
||||
<span id="p-modules-count" style="font-weight:bold; color:#333;">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1410,6 +1415,7 @@
|
||||
const elInst = document.getElementById('p-inst');
|
||||
const elLoc = document.getElementById('p-loc');
|
||||
const elCount = document.getElementById('p-modules-count');
|
||||
const elAvatar = document.getElementById('p-avatar');
|
||||
|
||||
function closeProfileModal() {
|
||||
pModal.style.opacity = '0';
|
||||
@@ -1443,8 +1449,10 @@
|
||||
const modules = data.completed_modules || [];
|
||||
|
||||
// Fill Data
|
||||
elName.textContent = p.full_name || p.username || p.email || "Unknown User";
|
||||
elMeta.textContent = `@${p.username || p.email || 'N/A'}`;
|
||||
if(elAvatar) elAvatar.src = p.avatar_url || 'assets/flame.svg';
|
||||
|
||||
elName.textContent = p.display_name || p.full_name || (p.username ? p.username.split('@')[0].trim() : "Unknown User");
|
||||
elMeta.textContent = `@${p.username ? p.username.split('@')[0].trim() : 'N/A'}`;
|
||||
elBio.textContent = p.bio || "No bio available.";
|
||||
|
||||
let inst = "N/A";
|
||||
|
||||
@@ -171,8 +171,6 @@
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.feed-item {
|
||||
@@ -191,12 +189,21 @@
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.feed-item a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* --- 1. The Journey Card (Right) --- */
|
||||
#process-card {
|
||||
top: 45vh;
|
||||
right: 30px;
|
||||
left: auto; /* Ensure no conflict */
|
||||
transform: translateX(150%);
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#process-card.visible {
|
||||
@@ -241,14 +248,18 @@
|
||||
}
|
||||
|
||||
#process-card .feed-item.active-step::before {
|
||||
border-color: #ff6600;
|
||||
border-top-color: transparent;
|
||||
animation: spin 1s linear infinite;
|
||||
background-color: #ff6600; /* Orange core for the flame */
|
||||
border-color: #ff9900;
|
||||
animation: glow 1.5s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
@keyframes glow {
|
||||
from {
|
||||
box-shadow: 0 0 4px #ff9900, 0 0 8px #ff6600;
|
||||
}
|
||||
to {
|
||||
box-shadow: 0 0 8px #ff9900, 0 0 16px #ff6600;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- 2. News Feed Card (Left) --- */
|
||||
@@ -342,9 +353,10 @@
|
||||
<span style="font-size:0.7em; opacity:0.8;">GUIDE</span>
|
||||
</div>
|
||||
<ul class="feed-list process-list">
|
||||
<li class="feed-item">Create Account & Track Progress</li>
|
||||
<li class="feed-item">Clone & Run TinyTorch Repo</li>
|
||||
<li class="feed-item">Build AI Infrastructure (Mimic PyTorch)</li>
|
||||
<li class="feed-item"><a href="?action=join">Create Account & Track Progress</a></li>
|
||||
<li class="feed-item"><a href="dashboard.html">View and interact with others during your TinyTorch journey</a></li>
|
||||
<li class="feed-item"><a href="community.html">View the global TinyTorch community</a></li>
|
||||
<li class="feed-item"><a href="?action=profile">Update Your Profile</a></li>
|
||||
<li class="feed-item">Connect with Global Builders</li>
|
||||
<li class="feed-item">Join Hackathons & Earn Badges</li>
|
||||
<li class="feed-item">Innovate in the AI Space</li>
|
||||
@@ -1013,5 +1025,39 @@
|
||||
// setInterval(fetchRealSubmissions, 10000); // Poll every 10 seconds
|
||||
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const processList = document.querySelector('#process-card .process-list');
|
||||
if (processList) {
|
||||
processList.addEventListener('click', (e) => {
|
||||
const link = e.target.closest('a');
|
||||
if (!link) return;
|
||||
|
||||
e.preventDefault();
|
||||
const href = link.getAttribute('href');
|
||||
if (!href || href === '#') return;
|
||||
|
||||
// Re-implement getBasePath logic from modules/config.js
|
||||
let basePath = '';
|
||||
const hostname = window.location.hostname;
|
||||
if (hostname === 'mlsysbook.ai') {
|
||||
basePath = '/tinytorch/community';
|
||||
} else if (hostname === 'tinytorch.ai' || (hostname === 'localhost' && window.location.port === '8000')) {
|
||||
basePath = '/community';
|
||||
} else if (hostname === 'harvard-edge.github.io') {
|
||||
basePath = '/cs249r_book_dev/tinytorch/community';
|
||||
}
|
||||
|
||||
if (href.startsWith('?')) {
|
||||
// It's a modal action, reload the page with the param
|
||||
window.location.href = window.location.pathname + href;
|
||||
} else {
|
||||
// It's a page navigation
|
||||
window.location.href = basePath ? `${basePath}/${href}` : href;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NETLIFY_URL, SUPABASE_URL, getBasePath } from './config.js';
|
||||
import { updateNavState } from './ui.js?v=2';
|
||||
import { closeProfileModal } from './profile.js';
|
||||
import { closeProfileModal, openProfileModal } from './profile.js';
|
||||
import { getSession, forceLogin, clearSession } from './state.js?v=2';
|
||||
|
||||
let currentMode = 'signup';
|
||||
@@ -207,7 +207,13 @@ export async function handleAuth(e) {
|
||||
localStorage.setItem("tinytorch_user", JSON.stringify(data.user));
|
||||
updateNavState();
|
||||
|
||||
window.location.href = basePath + '/dashboard.html';
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.get('action') === 'profile') {
|
||||
closeModal();
|
||||
openProfileModal();
|
||||
} else {
|
||||
window.location.href = basePath + '/dashboard.html';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert('If an account exists for this email, we have sent you a login link. Otherwise, please check your email to confirm your signup.');
|
||||
|
||||
@@ -1,6 +1,38 @@
|
||||
import { SUPABASE_URL, NETLIFY_URL, getBasePath } from './config.js';
|
||||
import { forceLogin } from './state.js?v=2';
|
||||
|
||||
export async function geocodeAndSetCoordinates(location) {
|
||||
const latInput = document.getElementById('profileLatitude');
|
||||
const lonInput = document.getElementById('profileLongitude');
|
||||
|
||||
if (!location) {
|
||||
if(latInput) latInput.value = '';
|
||||
if(lonInput) lonInput.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(location)}&limit=1`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Geocoding search failed');
|
||||
}
|
||||
const data = await response.json();
|
||||
if (data && data.length > 0) {
|
||||
const { lat, lon } = data[0];
|
||||
if(latInput) latInput.value = lat;
|
||||
if(lonInput) lonInput.value = lon;
|
||||
} else {
|
||||
console.warn(`Could not geocode '${location}'`);
|
||||
if(latInput) latInput.value = '';
|
||||
if(lonInput) lonInput.value = '';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Geocoding error:', error);
|
||||
if(latInput) latInput.value = '';
|
||||
if(lonInput) lonInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
export function openProfileModal() {
|
||||
const profileOverlay = document.getElementById('profileOverlay');
|
||||
profileOverlay.classList.add('active');
|
||||
@@ -113,6 +145,8 @@ function populateProfileForm(data) {
|
||||
const profileInstitutionInput = document.getElementById('profileInstitution');
|
||||
const profileWebsitesInput = document.getElementById('profileWebsites');
|
||||
const avatarPreview = document.getElementById('avatarPreview');
|
||||
const profileLatitude = document.getElementById('profileLatitude');
|
||||
const profileLongitude = document.getElementById('profileLongitude');
|
||||
|
||||
profileDisplayNameInput.value = data.display_name || '';
|
||||
profileAvatarUrlInput.value = data.avatar || data.avatar_url || '';
|
||||
@@ -120,6 +154,8 @@ function populateProfileForm(data) {
|
||||
profileFullNameInput.value = data.full_name || '';
|
||||
profileSummaryTextarea.value = data.bio || data.summary || '';
|
||||
profileLocationInput.value = data.location || '';
|
||||
if (profileLatitude) profileLatitude.value = data.latitude || '';
|
||||
if (profileLongitude) profileLongitude.value = data.longitude || '';
|
||||
|
||||
profileInstitutionInput.value = Array.isArray(data.institution) ? data.institution.join(', ') : (data.institution || '');
|
||||
|
||||
@@ -147,15 +183,19 @@ export async function handleProfileUpdate(e) {
|
||||
const profileLocationInput = document.getElementById('profileLocation');
|
||||
const profileInstitutionInput = document.getElementById('profileInstitution');
|
||||
const profileWebsitesInput = document.getElementById('profileWebsites');
|
||||
const profileLatitude = document.getElementById('profileLatitude');
|
||||
const profileLongitude = document.getElementById('profileLongitude');
|
||||
|
||||
const updatedProfile = {
|
||||
display_name: profileDisplayNameInput.value,
|
||||
avatar: profileAvatarUrlInput.value,
|
||||
full_name: profileFullNameInput.value,
|
||||
full_name: profileFullNameInput.value,
|
||||
summary: profileSummaryTextarea.value,
|
||||
location: profileLocationInput.value,
|
||||
institution: profileInstitutionInput.value.split(',').map(s => s.trim()).filter(s => s),
|
||||
website: profileWebsitesInput.value.split(',').map(s => s.trim()).filter(s => s),
|
||||
latitude: profileLatitude && profileLatitude.value ? parseFloat(profileLatitude.value) : null,
|
||||
longitude: profileLongitude && profileLongitude.value ? parseFloat(profileLongitude.value) : null,
|
||||
};
|
||||
|
||||
let retryCount = 0;
|
||||
|
||||
@@ -146,6 +146,8 @@ export function renderLayout() {
|
||||
<div class="profile-form-group">
|
||||
<label for="profileLocation" class="profile-label">Location:</label>
|
||||
<input type="text" class="profile-input" id="profileLocation" placeholder="City, Country">
|
||||
<input type="hidden" id="profileLatitude">
|
||||
<input type="hidden" id="profileLongitude">
|
||||
</div>
|
||||
<div class="profile-form-group">
|
||||
<label for="profileInstitution" class="profile-label">Institution (comma-separated):</label>
|
||||
|
||||
@@ -16,7 +16,7 @@ from ..core.browser import open_url
|
||||
|
||||
# Community URLs
|
||||
URL_COMMUNITY_MAP = "https://mlsysbook.ai/tinytorch/community/community.html"
|
||||
URL_COMMUNITY_PROFILE = "https://mlsysbook.ai/tinytorch/community/?action=profile"
|
||||
URL_COMMUNITY_PROFILE = "https://mlsysbook.ai/tinytorch/community/?action=profile&community=true"
|
||||
|
||||
|
||||
class CommunityCommand(BaseCommand):
|
||||
|
||||
Reference in New Issue
Block a user