updated the community map, rem username, long, latt

This commit is contained in:
jettythek
2025-12-16 07:32:21 -08:00
parent bd2d7e2798
commit 78091e7641
8 changed files with 159 additions and 60 deletions

View File

@@ -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;
}
})();

View File

@@ -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

View File

@@ -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";

View File

@@ -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>

View File

@@ -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.');

View File

@@ -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;

View File

@@ -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>

View File

@@ -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):