Minerva666 commited on
Commit
e532bcc
·
verified ·
1 Parent(s): d1a7526

Upload download.html

Browse files
Files changed (1) hide show
  1. static/download.html +347 -0
static/download.html ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Download — Energy Modelling Tools</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=Playfair+Display:wght@500;600;700&display=swap" rel="stylesheet">
9
+ <style>
10
+ :root {
11
+ --bg: #f4f2ee;
12
+ --text: #1a1a1a;
13
+ --text-muted: #5c5c5c;
14
+ --accent: #2d6a4f;
15
+ --card-bg: #ffffff;
16
+ --card-shadow: 0 2px 20px rgba(0,0,0,0.06);
17
+ }
18
+ * { margin: 0; padding: 0; box-sizing: border-box; }
19
+ body {
20
+ font-family: 'DM Sans', sans-serif;
21
+ background: var(--bg);
22
+ color: var(--text);
23
+ min-height: 100vh;
24
+ }
25
+
26
+ .bg-deco {
27
+ position: fixed; inset: 0; pointer-events: none; z-index: 0; overflow: hidden;
28
+ }
29
+ .bg-deco::before {
30
+ content: '';
31
+ position: absolute;
32
+ top: -30%; right: -20%;
33
+ width: 900px; height: 900px;
34
+ border-radius: 50%;
35
+ background: radial-gradient(circle, rgba(45,106,79,0.06) 0%, transparent 70%);
36
+ }
37
+
38
+ .container {
39
+ position: relative; z-index: 1;
40
+ max-width: 720px;
41
+ margin: 0 auto;
42
+ padding: 0 24px;
43
+ }
44
+
45
+ /* Back link */
46
+ .back {
47
+ display: inline-flex;
48
+ align-items: center;
49
+ gap: 6px;
50
+ margin-top: 40px;
51
+ font-size: 0.9rem;
52
+ color: var(--text-muted);
53
+ text-decoration: none;
54
+ transition: color 0.2s;
55
+ }
56
+ .back:hover { color: var(--accent); }
57
+ .back svg { width: 18px; height: 18px; }
58
+
59
+ /* Header */
60
+ .page-header {
61
+ text-align: center;
62
+ padding: 40px 0 12px;
63
+ }
64
+ .page-header .icon {
65
+ width: 72px; height: 72px;
66
+ border-radius: 50%;
67
+ margin: 0 auto 18px;
68
+ display: flex; align-items: center; justify-content: center;
69
+ background: var(--accent);
70
+ box-shadow: 0 4px 16px rgba(0,0,0,0.1);
71
+ }
72
+ .page-header h1 {
73
+ font-family: 'Playfair Display', serif;
74
+ font-weight: 600;
75
+ font-size: 2rem;
76
+ margin-bottom: 6px;
77
+ }
78
+ .page-header p {
79
+ color: var(--text-muted);
80
+ font-size: 1rem;
81
+ }
82
+
83
+ .divider {
84
+ width: 50px; height: 3px;
85
+ background: var(--accent);
86
+ margin: 28px auto 36px;
87
+ border-radius: 2px;
88
+ }
89
+
90
+ /* Form */
91
+ .download-card {
92
+ background: var(--card-bg);
93
+ border-radius: 16px;
94
+ padding: 36px 32px;
95
+ box-shadow: var(--card-shadow);
96
+ margin-bottom: 60px;
97
+ animation: fadeUp 0.45s ease forwards;
98
+ opacity: 0;
99
+ transform: translateY(16px);
100
+ }
101
+ @keyframes fadeUp { to { opacity: 1; transform: translateY(0); } }
102
+
103
+ .field { margin-bottom: 20px; }
104
+ .field label {
105
+ display: block;
106
+ font-weight: 500;
107
+ font-size: 0.85rem;
108
+ text-transform: uppercase;
109
+ letter-spacing: 0.5px;
110
+ color: var(--text-muted);
111
+ margin-bottom: 8px;
112
+ }
113
+ .field select {
114
+ width: 100%;
115
+ padding: 12px 16px;
116
+ font-family: inherit;
117
+ font-size: 0.95rem;
118
+ border: 1.5px solid #ddd;
119
+ border-radius: 10px;
120
+ background: #fafaf8;
121
+ color: var(--text);
122
+ appearance: none;
123
+ cursor: pointer;
124
+ transition: border-color 0.2s;
125
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%235c5c5c'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
126
+ background-repeat: no-repeat;
127
+ background-position: right 14px center;
128
+ }
129
+ .field select:focus {
130
+ outline: none;
131
+ border-color: var(--accent);
132
+ }
133
+ .field select:disabled {
134
+ opacity: 0.5;
135
+ cursor: wait;
136
+ }
137
+
138
+ .btn-download {
139
+ display: inline-flex;
140
+ align-items: center;
141
+ gap: 8px;
142
+ margin-top: 8px;
143
+ padding: 14px 32px;
144
+ background: var(--accent);
145
+ color: #fff;
146
+ border: none;
147
+ border-radius: 10px;
148
+ font-family: inherit;
149
+ font-size: 1rem;
150
+ font-weight: 600;
151
+ cursor: pointer;
152
+ transition: background 0.2s, transform 0.15s;
153
+ width: 100%;
154
+ justify-content: center;
155
+ }
156
+ .btn-download:hover { background: #245a42; transform: translateY(-1px); }
157
+ .btn-download:active { transform: translateY(0); }
158
+ .btn-download:disabled {
159
+ background: #bbb;
160
+ cursor: not-allowed;
161
+ transform: none;
162
+ }
163
+ .btn-download svg { width: 20px; height: 20px; }
164
+
165
+ /* Preview area */
166
+ .preview {
167
+ margin-top: 24px;
168
+ overflow-x: auto;
169
+ }
170
+ .preview table {
171
+ width: 100%;
172
+ border-collapse: collapse;
173
+ font-size: 0.82rem;
174
+ }
175
+ .preview th {
176
+ text-align: left;
177
+ padding: 8px 10px;
178
+ background: #f0ede8;
179
+ border-bottom: 2px solid #ddd;
180
+ font-weight: 600;
181
+ white-space: nowrap;
182
+ }
183
+ .preview td {
184
+ padding: 7px 10px;
185
+ border-bottom: 1px solid #eee;
186
+ white-space: nowrap;
187
+ }
188
+ .preview tr:last-child td { border-bottom: none; }
189
+
190
+ .spinner {
191
+ display: inline-block;
192
+ width: 18px; height: 18px;
193
+ border: 2px solid rgba(255,255,255,0.3);
194
+ border-top-color: #fff;
195
+ border-radius: 50%;
196
+ animation: spin 0.6s linear infinite;
197
+ }
198
+ @keyframes spin { to { transform: rotate(360deg); } }
199
+
200
+ .status-msg {
201
+ margin-top: 12px;
202
+ font-size: 0.85rem;
203
+ color: var(--text-muted);
204
+ }
205
+ .status-msg.error { color: #c0392b; }
206
+
207
+ footer {
208
+ text-align: center;
209
+ padding: 24px 0;
210
+ font-size: 0.8rem;
211
+ color: var(--text-muted);
212
+ border-top: 1px solid rgba(0,0,0,0.06);
213
+ }
214
+ </style>
215
+ </head>
216
+ <body>
217
+ <div class="bg-deco"></div>
218
+ <div class="container">
219
+ <a href="/" class="back">
220
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
221
+ All Tools
222
+ </a>
223
+
224
+ <div class="page-header">
225
+ <div class="icon" id="toolIcon">
226
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="1.8"><path d="M3 3v18h18"/><path d="M7 16l4-6 4 4 5-8"/></svg>
227
+ </div>
228
+ <h1 id="toolTitle">Loading…</h1>
229
+ <p id="toolSubtitle"></p>
230
+ </div>
231
+ <div class="divider"></div>
232
+
233
+ <div class="download-card">
234
+ <div class="field">
235
+ <label for="datasetSelect">Dataset</label>
236
+ <select id="datasetSelect" disabled><option>Loading datasets…</option></select>
237
+ </div>
238
+ <div class="field">
239
+ <label for="fileSelect">File</label>
240
+ <select id="fileSelect" disabled><option>Select a dataset first</option></select>
241
+ </div>
242
+ <button class="btn-download" id="downloadBtn" disabled>
243
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
244
+ Download
245
+ </button>
246
+ <div id="status" class="status-msg"></div>
247
+ <div id="preview" class="preview"></div>
248
+ </div>
249
+ </div>
250
+
251
+ <footer>
252
+ <div class="container">© 2025–2026 CCG. and contributors</div>
253
+ </footer>
254
+
255
+ <script>
256
+ const TOOL_META = {
257
+ maed: { color: '#1b3a5c', label: 'MAED', full_name: 'Model for Analysis of Energy Demand' },
258
+ OnStove: { color: '#c0392b', label: 'OnStove', full_name: 'Open-source Spatial Clean Cooking' },
259
+ };
260
+
261
+ const toolId = window.location.pathname.split('/').pop();
262
+ const meta = TOOL_META[toolId] || { color: '#2d6a4f', label: toolId, full_name: '' };
263
+
264
+ // Set header
265
+ document.getElementById('toolTitle').textContent = meta.label;
266
+ document.getElementById('toolSubtitle').textContent = meta.full_name;
267
+ document.getElementById('toolIcon').style.background = meta.color;
268
+ document.title = `${meta.label} — Energy Modelling Tools`;
269
+
270
+ const datasetSel = document.getElementById('datasetSelect');
271
+ const fileSel = document.getElementById('fileSelect');
272
+ const dlBtn = document.getElementById('downloadBtn');
273
+ const statusEl = document.getElementById('status');
274
+
275
+ let currentDatasetValue = '';
276
+
277
+ // Load datasets
278
+ fetch(`/api/datasets/${toolId}`)
279
+ .then(r => r.json())
280
+ .then(data => {
281
+ datasetSel.innerHTML = '';
282
+ data.datasets.forEach((d, i) => {
283
+ const opt = document.createElement('option');
284
+ opt.value = d.value;
285
+ opt.textContent = d.label;
286
+ datasetSel.appendChild(opt);
287
+ });
288
+ datasetSel.disabled = false;
289
+ if (data.datasets.length) {
290
+ currentDatasetValue = data.datasets[0].value;
291
+ loadFiles(currentDatasetValue);
292
+ }
293
+ })
294
+ .catch(() => {
295
+ datasetSel.innerHTML = '<option>Error loading datasets</option>';
296
+ });
297
+
298
+ datasetSel.addEventListener('change', () => {
299
+ currentDatasetValue = datasetSel.value;
300
+ loadFiles(currentDatasetValue);
301
+ });
302
+
303
+ function loadFiles(dataset) {
304
+ fileSel.disabled = true;
305
+ fileSel.innerHTML = '<option>Loading files…</option>';
306
+ dlBtn.disabled = true;
307
+ fetch(`/api/files/${toolId}?dataset=${encodeURIComponent(dataset)}`)
308
+ .then(r => r.json())
309
+ .then(data => {
310
+ fileSel.innerHTML = '';
311
+ data.files.forEach(f => {
312
+ const opt = document.createElement('option');
313
+ opt.value = f;
314
+ opt.textContent = f;
315
+ fileSel.appendChild(opt);
316
+ });
317
+ fileSel.disabled = false;
318
+ dlBtn.disabled = data.files.length === 0;
319
+ })
320
+ .catch(() => {
321
+ fileSel.innerHTML = '<option>Error loading files</option>';
322
+ });
323
+ }
324
+
325
+ fileSel.addEventListener('change', () => {
326
+ dlBtn.disabled = !fileSel.value;
327
+ });
328
+
329
+ dlBtn.addEventListener('click', () => {
330
+ const dataset = currentDatasetValue;
331
+ const file = fileSel.value;
332
+ if (!file) return;
333
+ const url = `/api/download/${toolId}?dataset=${encodeURIComponent(dataset)}&file=${encodeURIComponent(file)}`;
334
+ // Trigger download via hidden link
335
+ const a = document.createElement('a');
336
+ a.href = url;
337
+ a.download = file;
338
+ document.body.appendChild(a);
339
+ a.click();
340
+ document.body.removeChild(a);
341
+ statusEl.textContent = `Downloading ${file}…`;
342
+ statusEl.className = 'status-msg';
343
+ setTimeout(() => { statusEl.textContent = ''; }, 4000);
344
+ });
345
+ </script>
346
+ </body>
347
+ </html>