diff --git a/javascript/JS_Esercizi 11 - API/04_ricerca_post/index.html b/javascript/JS_Esercizi 11 - API/04_ricerca_post/index.html new file mode 100644 index 0000000..b734c1d --- /dev/null +++ b/javascript/JS_Esercizi 11 - API/04_ricerca_post/index.html @@ -0,0 +1,36 @@ + + + + + + Esercizio 4 - Ricerca Post + + + + ← Dashboard + +
+

🔍 Ricerca Post

+

Fetch multipli con filtri incrociati

+ + +
+ +
+ + +
+
+ + +
+ ⏳ Caricamento... +
+ + +
+
+ + + + diff --git a/javascript/JS_Esercizi 11 - API/04_ricerca_post/script.js b/javascript/JS_Esercizi 11 - API/04_ricerca_post/script.js new file mode 100644 index 0000000..62602ec --- /dev/null +++ b/javascript/JS_Esercizi 11 - API/04_ricerca_post/script.js @@ -0,0 +1,115 @@ +// ⚠️ COMPILARE E CONTROLLARE PRIMA DI INIZIARE ⚠️ +const BASE_URL = 'http://localhost:3000/api'; +const keyword = document.querySelector('#keyword'); +const loading = document.querySelector('#loading'); +const resultsContainer = document.querySelector('#resultsContainer'); +const btnSearch = document.querySelector('#btnSearch'); + + +/** + * FUNZIONE: Crea la card HTML per un autore e i suoi post trovati + * + * Funzione già fatta - non modificare + */ +function createAuthorCard(author, posts) { + const postsHTML = posts.map(post => ` +
+
${post.titolo}
+
${post.contenuto}
+
+ 📅 ${post.data} + ❤️ ${post.likes} likes +
+
+ `).join(''); + + return ` +
+
+ Avatar +
+

${author.nome} ${author.cognome}

+

📧 ${author.email}

+
+
+
+
+ Post trovati (${posts.length}) +
+ ${postsHTML} +
+
+ `; +} + + +/** + * FUNZIONE: Gestione errori + * + * Mostra un messaggio di errore e logga in console + * Funzione già fatta - non modificare + */ +function handleError(message) { + resultsContainer.innerHTML = ` +
+ ❌ ${message} +
+ `; + console.error('Errore:', message); +} + + +/** + * FUNZIONE: Visualizza i risultati della ricerca + * + * Raggruppa i post per autore e mostra le card + * Funzione già fatta - non modificare + */ +function displayResults(results) { + if (results.length === 0) { + resultsContainer.innerHTML = ` +
+ Nessun post trovato con la parola chiave +
+ `; + return; + } + + const cardsHTML = results.map(item => + createAuthorCard(item.author, item.posts) + ).join(''); + + resultsContainer.innerHTML = cardsHTML; +} + + +/** + * ESERCIZIO 5: Ricerca Post con Autori + * + * Devi completare questa funzione: + * 1. Leggi la parola chiave dall'input + * 2. Valida che non sia vuota (usa trim) + * 3. Mostra lo spinner di caricamento + * 4. Apri un blocco try/catch + * 5. Fai una GET a /posts + * 6. Filtra i post dove titolo O contenuto contiene la keyword (case-insensitive) + * 7. Se nessun post trovato, usa handleError() e return + * 8. Estrai gli ID univoci degli utenti che hanno scritto questi post + * 9. Fai una GET a /users + * 10. Crea un array di risultati: [{author: userObj, posts: [...]}, ...] + * dove posts sono solo quelli trovati per quell'autore + * 11. Chiama displayResults(results) + * 12. Gestisci gli errori con handleError() + * 13. Nascondi lo spinner di caricamento + */ +async function fetchPostsByKeyword() { +} + + +// COLLEGA IL BOTTONE +btnSearch.addEventListener('click', fetchPostsByKeyword); + +// PERMETTI ENTER +keyword.addEventListener('keypress', (e) => { + if (e.key === 'Enter') fetchPostsByKeyword(); +}); diff --git a/javascript/JS_Esercizi 11 - API/04_ricerca_post/style.css b/javascript/JS_Esercizi 11 - API/04_ricerca_post/style.css new file mode 100644 index 0000000..a531a8a --- /dev/null +++ b/javascript/JS_Esercizi 11 - API/04_ricerca_post/style.css @@ -0,0 +1,207 @@ +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); + display: flex; + justify-content: center; + align-items: flex-start; + min-height: 100vh; + margin: 0; + padding: 20px; +} + +.app-container { + background: white; + width: 100%; + max-width: 700px; + padding: 30px; + border-radius: 15px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); + margin-top: 20px; +} + +h1 { + color: #333; + text-align: center; + margin: 0 0 10px 0; + font-size: 1.8rem; +} + +.subtitle { + color: #666; + text-align: center; + margin: 0 0 30px 0; + font-size: 1rem; +} + +.config-box { + background: #f9f9f9; + padding: 20px; + border-radius: 10px; + margin-bottom: 25px; +} + +.config-box label { + display: block; + color: #333; + font-weight: 600; + margin-bottom: 12px; +} + +.input-group { + display: flex; + gap: 10px; +} + +.input-group input, +.input-group button { + padding: 12px 15px; + border-radius: 6px; + border: 1px solid #ddd; + font-size: 1rem; +} + +.input-group input { + flex: 1; + border-color: #ddd; +} + +.input-group input:focus { + outline: none; + border-color: #007bff; + box-shadow: 0 0 5px rgba(0, 123, 255, 0.2); +} + +.input-group button { + background: #007bff; + color: white; + border: none; + font-weight: bold; + cursor: pointer; + padding: 12px 25px; +} + +.input-group button:hover { + background: #0056b3; +} + +.loading { + text-align: center; + color: #666; + font-size: 1rem; + padding: 30px; + animation: pulse 1s infinite; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.6; } +} + +.results-container { + display: flex; + flex-direction: column; + gap: 20px; +} + +.author-section { + background: #f9f9f9; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + animation: slideIn 0.3s ease-out; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.author-header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 20px; + display: flex; + align-items: center; + gap: 15px; +} + +.author-avatar { + width: 60px; + height: 60px; + border-radius: 50%; + object-fit: cover; + border: 3px solid white; +} + +.author-info h3 { + margin: 0 0 5px 0; + font-size: 1.2rem; +} + +.author-info p { + margin: 0; + font-size: 0.9rem; + opacity: 0.9; +} + +.posts-list { + padding: 15px; +} + +.post-item { + background: white; + padding: 12px; + margin-bottom: 10px; + border-radius: 6px; + border-left: 3px solid #667eea; +} + +.post-item:last-child { + margin-bottom: 0; +} + +.post-title { + font-weight: 600; + color: #333; + margin: 0 0 5px 0; +} + +.post-preview { + color: #666; + font-size: 0.9rem; + margin: 0 0 8px 0; + line-height: 1.4; +} + +.post-meta { + display: flex; + gap: 15px; + font-size: 0.8rem; + color: #999; +} + +.empty { + text-align: center; + color: #999; + padding: 40px 20px; + font-size: 1.1rem; +} + +.error { + background: #fee; + color: #c00; + padding: 15px; + border-radius: 6px; + border-left: 4px solid #c00; + font-weight: 500; +} + +.nascosto { + display: none; +} diff --git a/javascript/JS_Esercizi 11 - API/04_todo_app_crud/index.html b/javascript/JS_Esercizi 11 - API/05_todo_app_crud/index.html similarity index 100% rename from javascript/JS_Esercizi 11 - API/04_todo_app_crud/index.html rename to javascript/JS_Esercizi 11 - API/05_todo_app_crud/index.html diff --git a/javascript/JS_Esercizi 11 - API/04_todo_app_crud/script.js b/javascript/JS_Esercizi 11 - API/05_todo_app_crud/script.js similarity index 100% rename from javascript/JS_Esercizi 11 - API/04_todo_app_crud/script.js rename to javascript/JS_Esercizi 11 - API/05_todo_app_crud/script.js diff --git a/javascript/JS_Esercizi 11 - API/04_todo_app_crud/style.css b/javascript/JS_Esercizi 11 - API/05_todo_app_crud/style.css similarity index 100% rename from javascript/JS_Esercizi 11 - API/04_todo_app_crud/style.css rename to javascript/JS_Esercizi 11 - API/05_todo_app_crud/style.css diff --git a/javascript/JS_Esercizi 11 - API/05_meteo/index.html b/javascript/JS_Esercizi 11 - API/extra_meteo/index.html similarity index 97% rename from javascript/JS_Esercizi 11 - API/05_meteo/index.html rename to javascript/JS_Esercizi 11 - API/extra_meteo/index.html index 28d565a..ab617d1 100644 --- a/javascript/JS_Esercizi 11 - API/05_meteo/index.html +++ b/javascript/JS_Esercizi 11 - API/extra_meteo/index.html @@ -26,7 +26,7 @@ - `; - container.innerHTML = html; + weatherContainer.innerHTML = html; } -// COLLEGA IL BOTTONE -document.getElementById('btnSearch').addEventListener('click', searchWeather); +// ===== COLLEGA GLI EVENTI ===== +btnSearch.addEventListener('click', searchWeather); // PERMETTI ENTER -document.getElementById('latitude').addEventListener('keypress', (e) => { +latitude.addEventListener('keypress', (e) => { if (e.key === 'Enter') searchWeather(); }); -document.getElementById('longitude').addEventListener('keypress', (e) => { +longitude.addEventListener('keypress', (e) => { if (e.key === 'Enter') searchWeather(); }); diff --git a/javascript/JS_Esercizi 11 - API/05_meteo/style.css b/javascript/JS_Esercizi 11 - API/extra_meteo/style.css similarity index 69% rename from javascript/JS_Esercizi 11 - API/05_meteo/style.css rename to javascript/JS_Esercizi 11 - API/extra_meteo/style.css index 1372374..1da88a8 100644 --- a/javascript/JS_Esercizi 11 - API/05_meteo/style.css +++ b/javascript/JS_Esercizi 11 - API/extra_meteo/style.css @@ -40,30 +40,39 @@ h1 { margin-bottom: 25px; } +.search-box h2 { + color: #333; + margin: 0 0 15px 0; + font-size: 1.1rem; +} + .search-box label { display: block; color: #555; font-weight: 600; margin-bottom: 10px; + font-size: 0.95rem; } .coord-group { display: flex; gap: 10px; + margin-bottom: 10px; } .coord-group input { flex: 1; - padding: 12px; - border: 2p0px; + padding: 10px; border: 2px solid #ddd; border-radius: 6px; font-size: 15px; + font-family: inherit; } .coord-group input:focus { outline: none; border-color: #007bff; + box-shadow: 0 0 5px rgba(0, 123, 255, 0.2); } .coord-group button { @@ -74,23 +83,25 @@ h1 { border-radius: 6px; font-weight: bold; cursor: pointer; - white-space: nowrap; + transition: background 0.2s; } .coord-group button:hover { - background: #0056b3 + background: #0056b3; +} .hint-text { color: #666; - font-size: 0.9em; - margin-top: 185rpx; + font-size: 0.9rem; + margin-top: 10px; } .loading { text-align: center; - color: white; - font-si#666; - padding: 3 pulse 1s infinite; + color: #666; + font-size: 1rem; + padding: 30px; + animation: pulse 1s infinite; } @keyframes pulse { @@ -100,7 +111,6 @@ h1 { .weather-container { margin-bottom: 50px; -}30px; } .weather-card { @@ -108,25 +118,14 @@ h1 { border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + animation: slideIn 0.3s ease-out; +} .location { text-align: center; color: #666; margin-bottom: 20px; - font-size: 0.95em; -} - -.weather-main { - text-align: center; - margin: 30px 0; -} - -.emoji { - font-size: 4em; - color: #666; - margin-bottom: 20px; - font-size: 0.9rem; - text-align: center; + font-size: 0.95rem; } .weather-main { @@ -135,12 +134,12 @@ h1 { } .emoji { - font-size: 3.5em; + font-size: 3.5rem; margin-bottom: 10px; } .temp { - font-size: 2.2em; + font-size: 2.2rem; font-weight: bold; color: #333; } @@ -149,28 +148,25 @@ h1 { background: white; padding: 15px; border-radius: 6px; - border-left: 4px solid #007bff; + margin-top: 15px; } .weather-details p { + margin: 8px 0; color: #555; - margin: 10px 0; - line-height: 1.5 rgba(255, 255, 255, 0.95); - padding: 25px; - border-radius: 12px; - margin-top: 30px; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); + font-size: 0.95rem; } -.instructions h2 { - color: #333; - margin-bottom: 20px; - font-size15px; +.error { + background: #fee; + color: #c00; + padding: 15px; border-radius: 6px; - border-left: 4px solid #c - margin-left: 25px; - color: #555; - line-heig-box { + border-left: 4px solid #c00; + font-weight: 500; +} + +.instructions { background: #f9f9f9; padding: 20px; border-radius: 10px; @@ -178,29 +174,30 @@ h1 { margin-top: 30px; } -.instructions-box h2 { +.instructions h2 { color: #333; margin: 0 0 15px 0; font-size: 1.1rem; } -.instructions-box ol, .instructions-box ul { +.instructions ol, +.instructions ul { margin-left: 20px; color: #555; line-height: 1.7; } -.instructions-box li { +.instructions li { margin-bottom: 8px; } -.instructions-box code { +.instructions code { background: white; padding: 2px 5px; border-radius: 3px; font-family: 'Courier New', monospace; color: #d63384; - font-size: 0.9em; + font-size: 0.9rem; } .hint { @@ -222,7 +219,41 @@ h1 { padding: 10px; border-radius: 4px; overflow-x: auto; - font-size: 0.8em; + font-size: 0.85rem; color: #333; margin: 0; - word-break: break-all \ No newline at end of file +} + +.challenge { + background: white; + border: 1px solid #ddd; + padding: 15px; + border-radius: 6px; + margin-top: 15px; +} + +.challenge strong { + color: #28a745; + display: block; + margin-bottom: 10px; +} + +.challenge p { + margin: 0; + color: #555; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.nascosto { + display: none; +} diff --git a/javascript/JS_Esercizi 11 - API/06_pokedex/index.html b/javascript/JS_Esercizi 11 - API/extra_pokedex/index.html similarity index 97% rename from javascript/JS_Esercizi 11 - API/06_pokedex/index.html rename to javascript/JS_Esercizi 11 - API/extra_pokedex/index.html index fbf8cb4..fb7b91a 100644 --- a/javascript/JS_Esercizi 11 - API/06_pokedex/index.html +++ b/javascript/JS_Esercizi 11 - API/extra_pokedex/index.html @@ -25,7 +25,7 @@ - `; - container.innerHTML = html; + pokemonContainer.innerHTML = html; } -// COLLEGA IL BOTTONE -document.getElementById('btnSearch').addEventListener('click', searchPokemon); +// ===== COLLEGA GLI EVENTI ===== +btnSearch.addEventListener('click', searchPokemon); // PERMETTI ENTER -document.getElementById('pokemonInput').addEventListener('keypress', (e) => { +pokemonInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { searchPokemon(); } diff --git a/javascript/JS_Esercizi 11 - API/06_pokedex/style.css b/javascript/JS_Esercizi 11 - API/extra_pokedex/style.css similarity index 65% rename from javascript/JS_Esercizi 11 - API/06_pokedex/style.css rename to javascript/JS_Esercizi 11 - API/extra_pokedex/style.css index 81bafc2..c9fad42 100644 --- a/javascript/JS_Esercizi 11 - API/06_pokedex/style.css +++ b/javascript/JS_Esercizi 11 - API/extra_pokedex/style.css @@ -34,30 +34,30 @@ h1 { } .search-box { - background: white; - padding: 25px; - border-radius: 12px; - margin-bottom: 30px; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); + background: #f9f9f9; + padding: 20px; + border-radius: 10px; + margin-bottom: 25px; } .search-box h2 { color: #333; - margin-bottom: 20px; - font-size: 1.2em; -} -#f9f9f9; - padding: 20px; - border-radius: 10px; - margin-bottom: 25px; + margin: 0 0 15px 0; + font-size: 1.1rem; } .search-box label { display: block; color: #555; font-weight: 600; - margin-bottom: 10 #ddd; - border-radius: 60px; + margin-bottom: 10px; + font-size: 0.95rem; +} + +.input-group { + display: flex; + gap: 10px; + margin-bottom: 10px; } .input-group input { @@ -66,11 +66,13 @@ h1 { border: 2px solid #ddd; border-radius: 6px; font-size: 15px; + font-family: inherit; } .input-group input:focus { outline: none; border-color: #007bff; + box-shadow: 0 0 5px rgba(0, 123, 255, 0.2); } .input-group button { @@ -81,17 +83,26 @@ h1 { border-radius: 6px; font-weight: bold; cursor: pointer; + transition: background 0.2s; } .input-group button:hover { - background: #85r056b3 + background: #0056b3; +} + +.hint-text { + color: #666; + font-size: 0.9rem; + margin-top: 10px; +} .loading { text-align: center; - color: white; - font-size: 1.2em; - padding#666; - padding: 3 + color: #666; + font-size: 1rem; + padding: 30px; + animation: pulse 1s infinite; +} @keyframes pulse { 0%, 100% { opacity: 1; } @@ -102,14 +113,15 @@ h1 { margin-bottom: 50px; } -.pokemon-card {30px; -} - .pokemon-card { background: #f9f9f9; border-radius: 10px; overflow: hidden; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);-header { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + animation: slideIn 0.3s ease-out; +} + +.card-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; @@ -117,44 +129,33 @@ h1 { } .card-header h2 { - font-size: 1.8em; + margin: 0; + font-size: 1.3rem; } .card-image { - background: #f9f9f9; + background: white; padding: 30px; text-align: center; - border-bottom: 2px solid #f0f0f0; -}007bff 0%, #0056b3 100%); - color: white; - padding: 20px; - text-align: center; -} - -.card-header h2 { - margin: 0; - font-size: 1.3r -.card-details {white; - padding: 20px; - text-align: center; border-bottom: 1px solid #f0f0f0; } .card-image img { - max-width: 18 0; - border-bottom: 1px solid #eee; + max-width: 180px; + height: auto; } -.detail-row:last-child { - border-bottom: none; -}0px; +.card-details { + padding: 20px; } .detail-row { display: flex; justify-content: space-between; padding: 10px 0; - border-bottom: 1px solid #f0f0f0; + border-bottom: 1px solid #eee; + color: #555; + font-size: 0.95rem; } .detail-row:last-child { @@ -162,33 +163,41 @@ h1 { } .detail-row strong { - color: #333solid #eee; + color: #333; + font-weight: 600; +} + +.card-footer { + background: #f9f9f9; + padding: 12px 20px; + text-align: center; + border-top: 1px solid #eee; } .card-footer small { color: #999; + font-size: 0.8rem; } .card-footer a { - color: #667eea; + color: #007bff; text-decoration: none; - font-weight: 500; -}2px; - text-align: center; - border-top: 1px solid #f0f0f0; - font-size: 0.85rem; - color: #999; } -.card-footer a { - color: #007bff 8px; +.card-footer a:hover { + text-decoration: underline; +} + +.error { + background: #fee; + color: #c00; + padding: 15px; + border-radius: 6px; border-left: 4px solid #c00; font-weight: 500; } .instructions { - background: rgba(255, 255, 255, 0.95); - padding: -box { background: #f9f9f9; padding: 20px; border-radius: 10px; @@ -196,29 +205,30 @@ h1 { margin-top: 30px; } -.instructions-box h2 { +.instructions h2 { color: #333; margin: 0 0 15px 0; font-size: 1.1rem; } -.instructions-box ol, .instructions-box ul { +.instructions ol, +.instructions ul { margin-left: 20px; color: #555; line-height: 1.7; } -.instructions-box li { +.instructions li { margin-bottom: 8px; } -.instructions-box code { +.instructions code { background: white; padding: 2px 5px; border-radius: 3px; font-family: 'Courier New', monospace; color: #d63384; - font-size: 0.9em; + font-size: 0.9rem; } .hint { @@ -240,6 +250,41 @@ h1 { padding: 10px; border-radius: 4px; overflow-x: auto; - font-size: 0.8em; + font-size: 0.85rem; color: #333; - margin: 0 \ No newline at end of file + margin: 0; +} + +.challenge { + background: white; + border: 1px solid #ddd; + padding: 15px; + border-radius: 6px; + margin-top: 15px; +} + +.challenge strong { + color: #28a745; + display: block; + margin-bottom: 10px; +} + +.challenge p { + margin: 0; + color: #555; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.nascosto { + display: none; +} diff --git a/javascript/JS_Esercizi 11 - API/index.html b/javascript/JS_Esercizi 11 - API/index.html index 8f8badd..a538fc4 100644 --- a/javascript/JS_Esercizi 11 - API/index.html +++ b/javascript/JS_Esercizi 11 - API/index.html @@ -124,7 +124,7 @@ text-transform: uppercase; letter-spacing: 0.5px; } - + .difficulty.tutorial { background: #e3f2fd; color: #1565c0; @@ -211,7 +211,15 @@ - + +
🔍
+
+

Ricerca Post Medio

+

Fetch multipli con filtri incrociati

+
+
+ +

Todo App CRUD Difficile

@@ -222,7 +230,7 @@

Esercizi su Server Esterno

- +
🌤️

App Meteo Medio

@@ -230,7 +238,7 @@
- +
🔴

Pokédex Medio

@@ -241,7 +249,9 @@
⚠️ Prima di Iniziare:
- Per esercizi 1-4: avvia il server con cd server-api e poi npm start + Per esercizi 1-5: avvia il server con cd server-api e poi npm start
+ L'insegnante dovrebbe averlo già avviato, ma potete farlo anche in locale.
+ Controllare sempre che BASE_URL in ogni script punti al corretto URL.
diff --git a/server-api/database/db.json b/server-api/database/db.json index 654e776..c94a3e4 100644 --- a/server-api/database/db.json +++ b/server-api/database/db.json @@ -481,6 +481,126 @@ "contenuto": "Chi vuole aiutarmi con una app open source?", "likes": 0, "data": "2023-10-14" + }, + { + "id": 11, + "userId": 5, + "titolo": "JavaScript async/await", + "contenuto": "Finalmente ho capito la differenza tra Promises e async/await.", + "likes": 67, + "data": "2023-10-16" + }, + { + "id": 12, + "userId": 1, + "titolo": "API REST in 10 minuti", + "contenuto": "Un semplice tutorial su come creare una API.", + "likes": 34, + "data": "2023-10-18" + }, + { + "id": 13, + "userId": 12, + "titolo": "Imparare CSS Grid", + "contenuto": "Grid è più semplice di quanto pensi.", + "likes": 28, + "data": "2023-10-17" + }, + { + "id": 14, + "userId": 10, + "titolo": "Debugging con console.log", + "contenuto": "A volte le soluzioni più semplici funzionano meglio.", + "likes": 15, + "data": "2023-10-19" + }, + { + "id": 15, + "userId": 7, + "titolo": "Open source per principianti", + "contenuto": "Iniziare a contribuire al tuo primo progetto.", + "likes": 42, + "data": "2023-10-20" + }, + { + "id": 16, + "userId": 15, + "titolo": "Il mio primo npm package", + "contenuto": "Ho pubblicato la mia prima libreria JavaScript.", + "likes": 11, + "data": "2023-10-21" + }, + { + "id": 17, + "userId": 2, + "titolo": "CSS Flexbox Tips", + "contenuto": "Alcuni trucchi utili per usare Flexbox meglio.", + "likes": 56, + "data": "2023-10-22" + }, + { + "id": 18, + "userId": 20, + "titolo": "Testing in JavaScript", + "contenuto": "Perché i test sono importanti per il tuo codice.", + "likes": 73, + "data": "2023-10-23" + }, + { + "id": 19, + "userId": 5, + "titolo": "TypeScript per chi inizia", + "contenuto": "Le basi di TypeScript senza complicazioni.", + "likes": 89, + "data": "2023-10-24" + }, + { + "id": 20, + "userId": 12, + "titolo": "Responsive Design Essenziale", + "contenuto": "Mobile first è il nuovo standard.", + "likes": 44, + "data": "2023-10-25" + }, + { + "id": 21, + "userId": 1, + "titolo": "DOM Manipulation Semplice", + "contenuto": "Come modificare il DOM senza framework.", + "likes": 26, + "data": "2023-10-26" + }, + { + "id": 22, + "userId": 4, + "titolo": "Alternativa a jQuery", + "contenuto": "Non hai bisogno di jQuery per le cose semplici.", + "likes": 37, + "data": "2023-10-27" + }, + { + "id": 23, + "userId": 15, + "titolo": "Gestione degli errori", + "contenuto": "Try/catch: quando e come usarlo.", + "likes": 19, + "data": "2023-10-28" + }, + { + "id": 24, + "userId": 10, + "titolo": "Le Arrow Function", + "contenuto": "Cosa sono e quando usarle nella pratica.", + "likes": 31, + "data": "2023-10-29" + }, + { + "id": 25, + "userId": 20, + "titolo": "Destructuring in JavaScript", + "contenuto": "Una sintassi comoda che ti farà risparmiare tempo.", + "likes": 52, + "data": "2023-10-30" } ], "todos": [