Skip to content

Commit a257dfe

Browse files
Enhance stars ranking sync resilience and update last updated timestamps in project data
1 parent da28c0f commit a257dfe

3 files changed

Lines changed: 80 additions & 29 deletions

File tree

website/scripts/sync-stars-ranking.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ async function syncStarsRanking() {
8888
starData.sort((a, b) => b.stars - a.stars);
8989
const totalStars = starData.reduce((sum, p) => sum + p.stars, 0);
9090

91+
// Resilience: if every fetch failed (e.g. GitHub rate-limited the build with
92+
// no GITHUB_TOKEN), keep the last committed ranking instead of wiping it.
93+
if (starData.length === 0 && githubProjects.length > 0) {
94+
console.warn('⚠ No stars fetched (likely a GitHub rate limit). Keeping the existing stars-ranking.json.');
95+
return;
96+
}
97+
9198
writeFileSync(OUTPUT_PATH, JSON.stringify(starData, null, 2) + '\n');
9299
console.log(`✓ Wrote ${successCount} projects, total stars: ${totalStars}`);
93100
} catch (error) {

website/src/data/stars-ranking.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"language-model",
1919
"transformer"
2020
],
21-
"lastUpdated": "2026-06-23T23:17:50.404Z"
21+
"lastUpdated": "2026-06-25T11:12:56.481Z"
2222
},
2323
{
2424
"name": "MegaPlantTF",
@@ -39,7 +39,7 @@
3939
"protein-classification",
4040
"webserver"
4141
],
42-
"lastUpdated": "2026-06-23T23:17:50.067Z"
42+
"lastUpdated": "2026-06-25T11:12:56.100Z"
4343
},
4444
{
4545
"name": "ClimateFiller",
@@ -60,6 +60,6 @@
6060
"time-series",
6161
"machine-learning"
6262
],
63-
"lastUpdated": "2026-06-23T23:17:49.705Z"
63+
"lastUpdated": "2026-06-25T11:12:55.728Z"
6464
}
6565
]

website/src/views/AddYours.vue

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,10 @@
272272

273273
<!-- Submit Button -->
274274
<div class="pt-4">
275-
<button type="submit" class="btn-primary w-full py-3 text-lg">
276-
Submit Project
275+
<button type="submit" :disabled="submitting" class="btn-primary w-full py-3 text-lg disabled:opacity-60 disabled:cursor-not-allowed">
276+
{{ submitting ? 'Submitting...' : 'Submit Project' }}
277277
</button>
278+
<p v-if="submitError" class="mt-3 text-sm text-red-600 text-center">{{ submitError }}</p>
278279
</div>
279280
</form>
280281
</div>
@@ -305,6 +306,13 @@
305306
import { ref } from 'vue'
306307
307308
const submitted = ref(false)
309+
const submitting = ref(false)
310+
const submitError = ref('')
311+
312+
// Free email delivery via Web3Forms (https://web3forms.com).
313+
// Get an access key (enter your email, they email you one), then set it as a
314+
// Cloudflare build variable named VITE_WEB3FORMS_ACCESS_KEY, or paste it below.
315+
const WEB3FORMS_ACCESS_KEY = import.meta.env.VITE_WEB3FORMS_ACCESS_KEY || 'af33691a-2b85-4d81-8376-9a04248e7daa'
308316
309317
const domains = [
310318
'Agriculture & Food Systems',
@@ -339,46 +347,82 @@ const form = ref({
339347
email: ''
340348
})
341349
342-
const handleSubmit = () => {
343-
// Process tags
350+
const handleSubmit = async () => {
351+
submitError.value = ''
352+
344353
const tags = form.value.tagsInput
345354
.split(',')
346355
.map(t => t.trim())
347356
.filter(t => t.length > 0)
348357
349-
// Process units
350358
const units = form.value.unit
351359
.split(',')
352360
.map(u => u.trim())
353361
.filter(u => u.length > 0)
354362
355-
// Create project object
356-
const project = {
363+
// A ready-to-paste catalog/mosaic.json entry, included in the email.
364+
const mosaicEntry = {
357365
name: form.value.name,
358366
description: form.value.description,
359-
url: form.value.url,
360-
platform: form.value.platform,
361-
type: form.value.type,
362-
domain: form.value.domain,
363-
tags: tags,
364-
owner: form.value.owner,
365-
unit: units.length === 1 ? units[0] : units,
366-
language: form.value.language,
367-
license: form.value.license,
367+
url: form.value.url ? [form.value.url] : [],
368+
platform: form.value.platform ? [form.value.platform] : [],
369+
type: form.value.type ? [form.value.type] : [],
370+
domain: form.value.domain ? [form.value.domain] : [],
371+
tags,
372+
affiliations: units,
373+
sub_affiliations: [],
374+
language: form.value.language ? [form.value.language] : [],
375+
license: form.value.license || null,
368376
doi: form.value.doi || null,
369-
status: form.value.status,
370-
created_at: form.value.created_at
377+
status: form.value.status || 'active',
378+
created_at: form.value.created_at || null,
371379
}
372380
373-
// Log to console (in production, this would send to backend)
374-
console.log('Project Submission:', project)
375-
console.log('Contact Email:', form.value.email)
376-
377-
// Show success message
378-
submitted.value = true
381+
if (!WEB3FORMS_ACCESS_KEY || WEB3FORMS_ACCESS_KEY === 'YOUR_WEB3FORMS_ACCESS_KEY') {
382+
submitError.value = 'Submissions are not configured yet. Set a Web3Forms access key to enable email delivery.'
383+
return
384+
}
379385
380-
// Scroll to top
381-
window.scrollTo({ top: 0, behavior: 'smooth' })
386+
submitting.value = true
387+
try {
388+
const res = await fetch('https://api.web3forms.com/submit', {
389+
method: 'POST',
390+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
391+
body: JSON.stringify({
392+
access_key: WEB3FORMS_ACCESS_KEY,
393+
subject: `MOSAIC submission: ${form.value.name || 'Untitled project'}`,
394+
from_name: 'MOSAIC Website',
395+
replyto: form.value.email,
396+
Project: form.value.name,
397+
Description: form.value.description,
398+
URL: form.value.url,
399+
Platform: form.value.platform,
400+
Type: form.value.type,
401+
Domain: form.value.domain,
402+
Tags: tags.join(', '),
403+
Owner: form.value.owner,
404+
Affiliation: form.value.unit,
405+
Language: form.value.language,
406+
License: form.value.license,
407+
DOI: form.value.doi || '',
408+
Status: form.value.status,
409+
Created: form.value.created_at,
410+
Contact_email: form.value.email,
411+
mosaic_json_entry: JSON.stringify(mosaicEntry, null, 2),
412+
}),
413+
})
414+
const data = await res.json()
415+
if (data.success) {
416+
submitted.value = true
417+
window.scrollTo({ top: 0, behavior: 'smooth' })
418+
} else {
419+
submitError.value = data.message || 'Something went wrong sending your submission. Please try again.'
420+
}
421+
} catch (e) {
422+
submitError.value = 'Network error. Please check your connection and try again.'
423+
} finally {
424+
submitting.value = false
425+
}
382426
}
383427
384428
const resetForm = () => {

0 commit comments

Comments
 (0)