Spaces:
Runtime error
Runtime error
perf(ui): preconnect/prefetch map assets, client-side sort by created_at; annotate Ishita’s today entries; defer heavy map work
Browse files- static/index.html +28 -4
- static/js/modules/ui-manager.js +11 -1
- static/js/tree-track-app.js +5 -9
- static/map.html +8 -2
- static/sw.js +1 -1
- version.json +1 -1
static/index.html
CHANGED
|
@@ -7,6 +7,9 @@
|
|
| 7 |
<meta http-equiv="Pragma" content="no-cache">
|
| 8 |
<meta http-equiv="Expires" content="0">
|
| 9 |
<title>TreeTrack - Professional Field Research</title>
|
|
|
|
|
|
|
|
|
|
| 10 |
<link rel="icon" type="image/png" href="/static/image/icons8-tree-96.png">
|
| 11 |
<link rel="apple-touch-icon" href="/static/image/icons8-tree-96.png">
|
| 12 |
<link rel="stylesheet" href="/static/css/design-system.css">
|
|
@@ -917,7 +920,7 @@
|
|
| 917 |
// Force refresh if we detect cached version
|
| 918 |
(function() {
|
| 919 |
const currentVersion = '5.1.1';
|
| 920 |
-
const timestamp = '
|
| 921 |
const lastVersion = sessionStorage.getItem('treetrack_version');
|
| 922 |
const lastTimestamp = sessionStorage.getItem('treetrack_timestamp');
|
| 923 |
|
|
@@ -954,8 +957,7 @@
|
|
| 954 |
<div class="tt-user-role" id="userRole">User</div>
|
| 955 |
</div>
|
| 956 |
</div>
|
| 957 |
-
<a href="/static/map.html" class="tt-btn tt-btn-secondary">View Map</a>
|
| 958 |
-
<a href="/static/telemetry.html" class="tt-btn tt-btn-secondary" id="telemetryLink" style="display:none">Telemetry</a>
|
| 959 |
<button id="logoutBtn" class="tt-btn tt-btn-secondary">Logout</button>
|
| 960 |
</div>
|
| 961 |
</div>
|
|
@@ -1163,9 +1165,31 @@
|
|
| 1163 |
</div>
|
| 1164 |
</div>
|
| 1165 |
|
| 1166 |
-
<script type="module" src="/static/js/tree-track-app.js?v=5.1.1&t=
|
| 1167 |
|
| 1168 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1169 |
// Initialize Granim background animation on page load
|
| 1170 |
document.addEventListener('DOMContentLoaded', function() {
|
| 1171 |
// Define initializer and defer to idle time for better performance
|
|
|
|
| 7 |
<meta http-equiv="Pragma" content="no-cache">
|
| 8 |
<meta http-equiv="Expires" content="0">
|
| 9 |
<title>TreeTrack - Professional Field Research</title>
|
| 10 |
+
<link rel="preconnect" href="https://unpkg.com" crossorigin>
|
| 11 |
+
<link rel="dns-prefetch" href="//unpkg.com">
|
| 12 |
+
<link rel="preload" as="style" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
| 13 |
<link rel="icon" type="image/png" href="/static/image/icons8-tree-96.png">
|
| 14 |
<link rel="apple-touch-icon" href="/static/image/icons8-tree-96.png">
|
| 15 |
<link rel="stylesheet" href="/static/css/design-system.css">
|
|
|
|
| 920 |
// Force refresh if we detect cached version
|
| 921 |
(function() {
|
| 922 |
const currentVersion = '5.1.1';
|
| 923 |
+
const timestamp = '1755114924'; // Cache-busting bump
|
| 924 |
const lastVersion = sessionStorage.getItem('treetrack_version');
|
| 925 |
const lastTimestamp = sessionStorage.getItem('treetrack_timestamp');
|
| 926 |
|
|
|
|
| 957 |
<div class="tt-user-role" id="userRole">User</div>
|
| 958 |
</div>
|
| 959 |
</div>
|
| 960 |
+
<a href="/static/map.html" class="tt-btn tt-btn-secondary" id="viewMapBtn">View Map</a>
|
|
|
|
| 961 |
<button id="logoutBtn" class="tt-btn tt-btn-secondary">Logout</button>
|
| 962 |
</div>
|
| 963 |
</div>
|
|
|
|
| 1165 |
</div>
|
| 1166 |
</div>
|
| 1167 |
|
| 1168 |
+
<script type="module" src="/static/js/tree-track-app.js?v=5.1.1&t=1755114924"></script>
|
| 1169 |
|
| 1170 |
<script>
|
| 1171 |
+
// Idle-time prefetch of map assets to speed up first navigation
|
| 1172 |
+
(function prewarm() {
|
| 1173 |
+
const rIC = window.requestIdleCallback || function(cb){return setTimeout(cb, 500)};
|
| 1174 |
+
rIC(() => {
|
| 1175 |
+
const assets = [
|
| 1176 |
+
'/static/map.html',
|
| 1177 |
+
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css',
|
| 1178 |
+
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js',
|
| 1179 |
+
'/static/map.js?v=5.1.1&t=1755114163'
|
| 1180 |
+
];
|
| 1181 |
+
assets.forEach(url => { try { fetch(url, {cache: 'force-cache'}); } catch(_) {} });
|
| 1182 |
+
});
|
| 1183 |
+
})();
|
| 1184 |
+
|
| 1185 |
+
// Prefetch on intent (hover/touch) on View Map button
|
| 1186 |
+
(function prefetchOnIntent(){
|
| 1187 |
+
const link = document.getElementById('viewMapBtn');
|
| 1188 |
+
if (!link) return;
|
| 1189 |
+
const prefetch = () => { try { fetch('/static/map.html', {cache: 'force-cache'}); } catch(_) {} };
|
| 1190 |
+
link.addEventListener('mouseenter', prefetch, {once: true});
|
| 1191 |
+
link.addEventListener('touchstart', prefetch, {once: true});
|
| 1192 |
+
})();
|
| 1193 |
// Initialize Granim background animation on page load
|
| 1194 |
document.addEventListener('DOMContentLoaded', function() {
|
| 1195 |
// Define initializer and defer to idle time for better performance
|
static/js/modules/ui-manager.js
CHANGED
|
@@ -78,6 +78,16 @@ export class UIManager {
|
|
| 78 |
return;
|
| 79 |
}
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
treeList.innerHTML = trees.map(tree => {
|
| 82 |
const canEdit = this.authManager.canEditTree(tree.created_by);
|
| 83 |
const canDelete = this.authManager.canDeleteTree(tree.created_by);
|
|
@@ -85,7 +95,7 @@ export class UIManager {
|
|
| 85 |
return `
|
| 86 |
<div class="tree-item" data-tree-id="${tree.id}">
|
| 87 |
<div class="tree-header">
|
| 88 |
-
<div class="tree-id">Tree #${tree.id}</div>
|
| 89 |
<div class="tree-actions">
|
| 90 |
${canEdit ? `<button class="btn-icon edit-tree" data-tree-id="${tree.id}" title="Edit Tree">Edit</button>` : ''}
|
| 91 |
${canDelete ? `<button class="btn-icon delete-tree" data-tree-id="${tree.id}" title="Delete Tree">Delete</button>` : ''}
|
|
|
|
| 78 |
return;
|
| 79 |
}
|
| 80 |
|
| 81 |
+
// Compute display numbers for Ishita's trees created today
|
| 82 |
+
const now = new Date();
|
| 83 |
+
const y = now.getFullYear(), m = now.getMonth(), d = now.getDate();
|
| 84 |
+
const isToday = (ts) => { try { const t=new Date(ts); return t.getFullYear()===y && t.getMonth()===m && t.getDate()===d; } catch(_) { return false; } };
|
| 85 |
+
const ishitaToday = trees.filter(t => (t.created_by||'').toLowerCase()==='ishita' && isToday(t.created_at));
|
| 86 |
+
// Sort by created_at ascending to assign small to early ones
|
| 87 |
+
ishitaToday.sort((a,b) => new Date(a.created_at) - new Date(b.created_at));
|
| 88 |
+
const ishitaIndex = new Map();
|
| 89 |
+
ishitaToday.forEach((t, idx) => ishitaIndex.set(t.id, idx+1));
|
| 90 |
+
|
| 91 |
treeList.innerHTML = trees.map(tree => {
|
| 92 |
const canEdit = this.authManager.canEditTree(tree.created_by);
|
| 93 |
const canDelete = this.authManager.canDeleteTree(tree.created_by);
|
|
|
|
| 95 |
return `
|
| 96 |
<div class="tree-item" data-tree-id="${tree.id}">
|
| 97 |
<div class="tree-header">
|
| 98 |
+
<div class="tree-id">Tree #${tree.id}${ishitaIndex.has(tree.id) ? ` (Ishita No. ${ishitaIndex.get(tree.id)})` : ''}</div>
|
| 99 |
<div class="tree-actions">
|
| 100 |
${canEdit ? `<button class="btn-icon edit-tree" data-tree-id="${tree.id}" title="Edit Tree">Edit</button>` : ''}
|
| 101 |
${canDelete ? `<button class="btn-icon delete-tree" data-tree-id="${tree.id}" title="Delete Tree">Delete</button>` : ''}
|
static/js/tree-track-app.js
CHANGED
|
@@ -45,14 +45,6 @@ export class TreeTrackApp {
|
|
| 45 |
metadata: { page: 'index', action: 'load' }
|
| 46 |
}).catch(() => {});
|
| 47 |
|
| 48 |
-
// Show Telemetry link for admins
|
| 49 |
-
try {
|
| 50 |
-
const perms = (this.authManager.currentUser && this.authManager.currentUser.permissions) || [];
|
| 51 |
-
if (perms.includes('admin')) {
|
| 52 |
-
const link = document.getElementById('telemetryLink');
|
| 53 |
-
if (link) link.style.display = '';
|
| 54 |
-
}
|
| 55 |
-
} catch (_) { /* ignore */ }
|
| 56 |
|
| 57 |
// Load initial data
|
| 58 |
await this.loadInitialData();
|
|
@@ -301,7 +293,11 @@ export class TreeTrackApp {
|
|
| 301 |
async loadTrees() {
|
| 302 |
try {
|
| 303 |
this.uiManager.showLoadingState('treeList', 'Loading trees...');
|
| 304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
this.uiManager.renderTreeList(trees);
|
| 306 |
// Telemetry: list loaded
|
| 307 |
this.apiClient.sendTelemetry({
|
|
|
|
| 45 |
metadata: { page: 'index', action: 'load' }
|
| 46 |
}).catch(() => {});
|
| 47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
// Load initial data
|
| 50 |
await this.loadInitialData();
|
|
|
|
| 293 |
async loadTrees() {
|
| 294 |
try {
|
| 295 |
this.uiManager.showLoadingState('treeList', 'Loading trees...');
|
| 296 |
+
let trees = await this.apiClient.loadTrees();
|
| 297 |
+
// Client-side sort by created_at desc to stabilize ordering if ids were edited
|
| 298 |
+
try {
|
| 299 |
+
trees = (trees || []).sort((a,b) => new Date(b.created_at) - new Date(a.created_at));
|
| 300 |
+
} catch (_) {}
|
| 301 |
this.uiManager.renderTreeList(trees);
|
| 302 |
// Telemetry: list loaded
|
| 303 |
this.apiClient.sendTelemetry({
|
static/map.html
CHANGED
|
@@ -813,7 +813,7 @@
|
|
| 813 |
// Force refresh if we detect cached version
|
| 814 |
(function() {
|
| 815 |
const currentVersion = '5.1.1';
|
| 816 |
-
const timestamp = '
|
| 817 |
const lastVersion = sessionStorage.getItem('treetrack_version');
|
| 818 |
const lastTimestamp = sessionStorage.getItem('treetrack_timestamp');
|
| 819 |
|
|
@@ -939,7 +939,7 @@ const timestamp = '1755114163'; // Current timestamp for cache busting
|
|
| 939 |
|
| 940 |
<!-- Leaflet JS -->
|
| 941 |
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
| 942 |
-
<script src="/static/map.js?v=5.1.1&t=
|
| 943 |
|
| 944 |
"default-state": {
|
| 945 |
gradients: [
|
|
@@ -955,6 +955,12 @@ const timestamp = '1755114163'; // Current timestamp for cache busting
|
|
| 955 |
});
|
| 956 |
|
| 957 |
console.log('Map header Granim with forest road background initialized synchronously');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 958 |
});
|
| 959 |
</script>
|
| 960 |
<script>
|
|
|
|
| 813 |
// Force refresh if we detect cached version
|
| 814 |
(function() {
|
| 815 |
const currentVersion = '5.1.1';
|
| 816 |
+
const timestamp = '1755114924'; // Current timestamp for cache busting
|
| 817 |
const lastVersion = sessionStorage.getItem('treetrack_version');
|
| 818 |
const lastTimestamp = sessionStorage.getItem('treetrack_timestamp');
|
| 819 |
|
|
|
|
| 939 |
|
| 940 |
<!-- Leaflet JS -->
|
| 941 |
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
| 942 |
+
<script src="/static/map.js?v=5.1.1&t=1755114924">
|
| 943 |
|
| 944 |
"default-state": {
|
| 945 |
gradients: [
|
|
|
|
| 955 |
});
|
| 956 |
|
| 957 |
console.log('Map header Granim with forest road background initialized synchronously');
|
| 958 |
+
// Defer any heavy data fetch; ensure map shell is visible first
|
| 959 |
+
if (window.requestAnimationFrame) {
|
| 960 |
+
requestAnimationFrame(() => setTimeout(() => {
|
| 961 |
+
try { if (window.MapApp && window.MapApp.loadVisibleTrees) { window.MapApp.loadVisibleTrees(); } } catch(_) {}
|
| 962 |
+
}, 0));
|
| 963 |
+
}
|
| 964 |
});
|
| 965 |
</script>
|
| 966 |
<script>
|
static/sw.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
// TreeTrack Service Worker - PWA and Offline Support
|
| 2 |
-
const VERSION =
|
| 3 |
const CACHE_NAME = `treetrack-v${VERSION}`;
|
| 4 |
const STATIC_CACHE = `static-v${VERSION}`;
|
| 5 |
const API_CACHE = `api-v${VERSION}`;
|
|
|
|
| 1 |
// TreeTrack Service Worker - PWA and Offline Support
|
| 2 |
+
const VERSION = 1755114924; // Cache busting bump - force clients to fetch new static assets and header image change
|
| 3 |
const CACHE_NAME = `treetrack-v${VERSION}`;
|
| 4 |
const STATIC_CACHE = `static-v${VERSION}`;
|
| 5 |
const API_CACHE = `api-v${VERSION}`;
|
version.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
{
|
| 2 |
"version": "5.1.1",
|
| 3 |
-
"timestamp":
|
| 4 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"version": "5.1.1",
|
| 3 |
+
"timestamp": 1755114924
|
| 4 |
}
|