removed unnecessary files
This commit is contained in:
@@ -1,166 +0,0 @@
|
|||||||
# 🔌 Esercizi API REST - Guida Completa
|
|
||||||
|
|
||||||
## 📋 Struttura del Progetto
|
|
||||||
|
|
||||||
```
|
|
||||||
JS_Esercizi 11 - API/
|
|
||||||
├── index.html # Hub principale - accesso agli esercizi
|
|
||||||
├── tutorial/
|
|
||||||
│ └── index.html # Tutorial su API REST e Fetch
|
|
||||||
└── esercizi/
|
|
||||||
├── meteo/
|
|
||||||
│ └── index.html # Esercizio 1: GET Request base
|
|
||||||
├── lista_utenti/
|
|
||||||
│ └── index.html # Esercizio 2: GET con iterazione e DOM
|
|
||||||
└── todo_app/
|
|
||||||
└── index.html # Esercizio 3: CRUD completo (POST, PUT, DELETE)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 Come Iniziare
|
|
||||||
|
|
||||||
### 1. Avvia il Server API
|
|
||||||
Prima di iniziare qualsiasi esercizio, **devi avviare il server**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd server-api
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
Il server sarà disponibile a: `http://localhost:3000/api`
|
|
||||||
|
|
||||||
### ⚠️ Importante
|
|
||||||
Se non avvii il server, gli esercizi non funzioneranno perché non riusciranno a recuperare i dati!
|
|
||||||
|
|
||||||
## 📚 Contenuto del Corso
|
|
||||||
|
|
||||||
### 🎓 Tutorial
|
|
||||||
Leggi prima il tutorial per capire:
|
|
||||||
- Cos'è un'API REST
|
|
||||||
- Come funziona la Fetch API
|
|
||||||
- Metodi HTTP (GET, POST, PUT, DELETE)
|
|
||||||
- Promise e Async/Await
|
|
||||||
|
|
||||||
**Accedi da:** [index.html → Tutorial](tutorial/index.html)
|
|
||||||
|
|
||||||
### 🔧 Esercizio 1: App Meteo (GET Base)
|
|
||||||
**Difficoltà:** ⭐ Base
|
|
||||||
|
|
||||||
Impara i fondamenti delle GET request:
|
|
||||||
- Fetch API base
|
|
||||||
- Conversione JSON
|
|
||||||
- Visualizzazione dati
|
|
||||||
- Gestione errori
|
|
||||||
|
|
||||||
**Cosa fare:** Scrivi il codice per recuperare e visualizzare dati meteo
|
|
||||||
|
|
||||||
**Accedi da:** [index.html → App Meteo](esercizi/meteo/index.html)
|
|
||||||
|
|
||||||
### 👥 Esercizio 2: Lista Utenti (GET + DOM)
|
|
||||||
**Difficoltà:** ⭐⭐ Intermedio
|
|
||||||
|
|
||||||
Sviluppa skills di manipolazione del DOM:
|
|
||||||
- GET request avanzato
|
|
||||||
- Iterazione array con map()
|
|
||||||
- Creazione dinamica di elementi HTML
|
|
||||||
- Conteggio e statistiche
|
|
||||||
|
|
||||||
**Cosa fare:** Recupera una lista di utenti e crea card dinamiche
|
|
||||||
|
|
||||||
**Accedi da:** [index.html → Lista Utenti](esercizi/lista_utenti/index.html)
|
|
||||||
|
|
||||||
### ✓ Esercizio 3: Todo App (CRUD Completo)
|
|
||||||
**Difficoltà:** ⭐⭐⭐ Avanzato
|
|
||||||
|
|
||||||
Implementa tutte le operazioni CRUD:
|
|
||||||
- **CREATE:** POST request per aggiungere todo
|
|
||||||
- **READ:** GET request per recuperare todo
|
|
||||||
- **UPDATE:** PUT request per segnare come completato
|
|
||||||
- **DELETE:** DELETE request per eliminare
|
|
||||||
|
|
||||||
**Cosa fare:** Crea un'app TODO completa con tutte le operazioni
|
|
||||||
|
|
||||||
**Accedi da:** [index.html → Todo App](esercizi/todo_app/index.html)
|
|
||||||
|
|
||||||
## 🛠️ API Endpoints Disponibili
|
|
||||||
|
|
||||||
Il server fornisce questi endpoint:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/weather → Ottieni dati meteo
|
|
||||||
GET /api/users → Ottieni lista utenti
|
|
||||||
GET /api/users/:id → Ottieni un utente
|
|
||||||
POST /api/users → Crea nuovo utente
|
|
||||||
PUT /api/users/:id → Aggiorna utente
|
|
||||||
DELETE /api/users/:id → Elimina utente
|
|
||||||
|
|
||||||
GET /api/todos → Ottieni lista todo
|
|
||||||
GET /api/todos/:id → Ottieni un todo
|
|
||||||
POST /api/todos → Crea nuovo todo
|
|
||||||
PUT /api/todos/:id → Aggiorna todo
|
|
||||||
DELETE /api/todos/:id → Elimina todo
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📖 Esempi di Codice
|
|
||||||
|
|
||||||
### GET Request Base
|
|
||||||
```javascript
|
|
||||||
const response = await fetch('http://localhost:3000/api/users');
|
|
||||||
const users = await response.json();
|
|
||||||
console.log(users);
|
|
||||||
```
|
|
||||||
|
|
||||||
### POST Request
|
|
||||||
```javascript
|
|
||||||
const response = await fetch('http://localhost:3000/api/todos', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ title: 'Nuovo todo', completed: false })
|
|
||||||
});
|
|
||||||
const newTodo = await response.json();
|
|
||||||
```
|
|
||||||
|
|
||||||
### DELETE Request
|
|
||||||
```javascript
|
|
||||||
const response = await fetch('http://localhost:3000/api/todos/1', {
|
|
||||||
method: 'DELETE'
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## ✅ Checklist Completamento
|
|
||||||
|
|
||||||
- [ ] Ho avviato il server `npm start` nella cartella server-api
|
|
||||||
- [ ] Ho letto il Tutorial
|
|
||||||
- [ ] Ho completato l'Esercizio 1 (App Meteo)
|
|
||||||
- [ ] Ho completato l'Esercizio 2 (Lista Utenti)
|
|
||||||
- [ ] Ho completato l'Esercizio 3 (Todo App)
|
|
||||||
|
|
||||||
## 💡 Suggerimenti
|
|
||||||
|
|
||||||
1. **Leggi sempre il tutorial prima** di iniziare gli esercizi
|
|
||||||
2. **Apri la console browser** (F12) per vedere i messaggi di errore
|
|
||||||
3. **Utilizza network tab** in DevTools per vedere le richieste HTTP
|
|
||||||
4. **Prova i comandi curl** nel terminale per testare gli endpoint
|
|
||||||
5. **Inizia semplice:** completa prima le funzioni di base
|
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
|
||||||
|
|
||||||
**Errore: "Fetch failed"**
|
|
||||||
- Controlla che il server sia avviato con `npm start`
|
|
||||||
- Verifica che il URL sia corretto
|
|
||||||
|
|
||||||
**Errore: "response.json() is not a function"**
|
|
||||||
- Controlla che la risposta sia valida JSON
|
|
||||||
- Vedi la console per il messaggio di errore esatto
|
|
||||||
|
|
||||||
**Dati non visualizzati**
|
|
||||||
- Controlla che il codice fetch sia implementato
|
|
||||||
- Verifica che il DOM sia aggiornato correttamente
|
|
||||||
- Usa `console.log()` per debuggare
|
|
||||||
|
|
||||||
## 📚 Risorse Utili
|
|
||||||
|
|
||||||
- [MDN - Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
|
|
||||||
- [MDN - async/await](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises)
|
|
||||||
- [JSON Server GitHub](https://github.com/typicode/json-server)
|
|
||||||
|
|
||||||
Buon lavoro! 🚀
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="it">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Hub Esercizi API</title>
|
|
||||||
<style>
|
|
||||||
/* RESET & BASE */
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
||||||
margin: 0;
|
|
||||||
padding: 40px;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CONTENITORE PRINCIPALE */
|
|
||||||
.hub-container {
|
|
||||||
background: white;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 650px;
|
|
||||||
padding: 40px;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.subtitle {
|
|
||||||
text-align: center;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* LISTA CARD */
|
|
||||||
.exercise-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CARD ESERCIZIO */
|
|
||||||
.card {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
text-decoration: none;
|
|
||||||
background: #fff;
|
|
||||||
border: 2px solid #f0f0f0;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 20px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
transform: translateY(-3px);
|
|
||||||
border-color: #007bff;
|
|
||||||
box-shadow: 0 5px 15px rgba(0, 123, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ICONA E TESTI */
|
|
||||||
.icon {
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-right: 20px;
|
|
||||||
width: 40px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info h3 {
|
|
||||||
margin: 0 0 5px 0;
|
|
||||||
color: #2c3e50;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info p {
|
|
||||||
margin: 0;
|
|
||||||
color: #7f8c8d;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TAG DIFFICOLTA' */
|
|
||||||
.badge {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 3px 8px;
|
|
||||||
border-radius: 6px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tutorial {
|
|
||||||
background-color: #e3f2fd;
|
|
||||||
color: #1565c0;
|
|
||||||
border: 1px solid #bbdefb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Blu */
|
|
||||||
.easy {
|
|
||||||
background-color: #e8f5e9;
|
|
||||||
color: #2e7d32;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verde */
|
|
||||||
.medium {
|
|
||||||
background-color: #fff3e0;
|
|
||||||
color: #ef6c00;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Arancione */
|
|
||||||
.hard {
|
|
||||||
background-color: #ffebee;
|
|
||||||
color: #c62828;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rosso */
|
|
||||||
|
|
||||||
/* FRECCIA AL PASSAGGIO DEL MOUSE */
|
|
||||||
.arrow {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
color: #ddd;
|
|
||||||
transition: 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover .arrow {
|
|
||||||
color: #007bff;
|
|
||||||
transform: translateX(5px);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="hub-container">
|
|
||||||
<h1>Esercizi API</h1>
|
|
||||||
<p class="subtitle">Corso Web Developer</p>
|
|
||||||
|
|
||||||
<div class="exercise-list">
|
|
||||||
|
|
||||||
<a href="tutorial/index.html" class="card">
|
|
||||||
<div class="icon">🧪</div>
|
|
||||||
<div class="info">
|
|
||||||
<h3>Tutorial<span class="badge tutorial">Tutorial</span></h3>
|
|
||||||
<p>Impara a comunicare con le API REST e recuperare dati dal server.</p>
|
|
||||||
</div>
|
|
||||||
<div class="arrow">→</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="meteo/index.html" class="card">
|
|
||||||
<div class="icon">🌤️</div>
|
|
||||||
<div class="info">
|
|
||||||
<h3>App Meteo <span class="badge easy">Base</span></h3>
|
|
||||||
<p>Fetch API, GET request, manipolazione JSON.</p>
|
|
||||||
</div>
|
|
||||||
<div class="arrow">→</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="lista_utenti/index.html" class="card">
|
|
||||||
<div class="icon">👥</div>
|
|
||||||
<div class="info">
|
|
||||||
<h3>Lista Utenti <span class="badge medium">Intermedio</span></h3>
|
|
||||||
<p>GET request, iterazione array, DOM manipulation.</p>
|
|
||||||
</div>
|
|
||||||
<div class="arrow">→</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="todo_app/index.html" class="card">
|
|
||||||
<div class="icon">✓</div>
|
|
||||||
<div class="info">
|
|
||||||
<h3>Todo App <span class="badge hard">Avanzato</span></h3>
|
|
||||||
<p>POST, PUT, DELETE request, gestione dello stato.</p>
|
|
||||||
</div>
|
|
||||||
<div class="arrow">→</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="it">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Lista Utenti - GET e DOM Manipulation</title>
|
|
||||||
<link rel="stylesheet" href="styles.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>👥 Lista Utenti</h1>
|
|
||||||
<p class="subtitle">Recupera e visualizza utenti dal server</p>
|
|
||||||
|
|
||||||
<div class="instructions">
|
|
||||||
<h3>📝 Obiettivo dell'Esercizio</h3>
|
|
||||||
<p>Dovrai scrivere il codice JavaScript per:</p>
|
|
||||||
<ol>
|
|
||||||
<li>Recuperare la lista di utenti da <code>/api/users</code></li>
|
|
||||||
<li>Iterare attraverso l'array di utenti</li>
|
|
||||||
<li>Creare e inserire dinamicamente le card degli utenti nel DOM</li>
|
|
||||||
<li>Mostrare il numero totale di utenti</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button onclick="fetchAndDisplayUsers()">Carica Lista Utenti</button>
|
|
||||||
|
|
||||||
<div class="loading" id="loading">
|
|
||||||
<div class="spinner"></div>
|
|
||||||
<p>Caricamento utenti...</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="users-container" id="usersContainer">
|
|
||||||
<!-- Le card degli utenti verranno inserite qui -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="../index.html" class="back-button">← Torna al Hub</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="script.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
// TODO: Completa questa funzione
|
|
||||||
async function fetchAndDisplayUsers() {
|
|
||||||
const loading = document.getElementById('loading');
|
|
||||||
const container = document.getElementById('usersContainer');
|
|
||||||
|
|
||||||
// 1. Mostra il loading spinner
|
|
||||||
loading.style.display = 'block';
|
|
||||||
container.innerHTML = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 2. Scrivi qui il fetch per ottenere gli utenti da:
|
|
||||||
// http://localhost:3000/api/users
|
|
||||||
// 3. Converti la risposta in JSON
|
|
||||||
// 4. Passa i dati alla funzione displayUsers()
|
|
||||||
|
|
||||||
// SUGGERIMENTO:
|
|
||||||
// const response = await fetch('...');
|
|
||||||
// const users = await response.json();
|
|
||||||
// displayUsers(users);
|
|
||||||
|
|
||||||
console.log('Scrivi il codice qui!');
|
|
||||||
throw new Error('Codice non implementato');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Errore:', error);
|
|
||||||
container.innerHTML = `
|
|
||||||
<div class="error">
|
|
||||||
<strong>❌ Errore:</strong> ${error.message}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} finally {
|
|
||||||
loading.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Funzione helper per visualizzare gli utenti
|
|
||||||
function displayUsers(users) {
|
|
||||||
const container = document.getElementById('usersContainer');
|
|
||||||
|
|
||||||
if (!Array.isArray(users) || users.length === 0) {
|
|
||||||
container.innerHTML = '<div class="empty">Nessun utente trovato</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crea le card per ogni utente
|
|
||||||
const cardsHTML = users.map(user => `
|
|
||||||
<div class="user-card">
|
|
||||||
<h3>${user.name || 'Nome sconosciuto'}</h3>
|
|
||||||
<p>📧 <strong>Email:</strong> ${user.email || 'N/A'}</p>
|
|
||||||
<p>📞 <strong>Telefono:</strong> ${user.phone || 'N/A'}</p>
|
|
||||||
<p>🌐 <strong>Website:</strong> ${user.website || 'N/A'}</p>
|
|
||||||
</div>
|
|
||||||
`).join('');
|
|
||||||
|
|
||||||
// Aggiungi il contatore di utenti
|
|
||||||
const counterHTML = `<div class="counter">📊 Totale: ${users.length} utenti</div>`;
|
|
||||||
|
|
||||||
container.innerHTML = cardsHTML + counterHTML;
|
|
||||||
}
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 40px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 40px;
|
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
text-align: center;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions {
|
|
||||||
background: #e3f2fd;
|
|
||||||
border-left: 4px solid #2196F3;
|
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions h3 {
|
|
||||||
color: #1565c0;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions ol {
|
|
||||||
margin-left: 20px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions li {
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background: #667eea;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #764ba2;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
|
||||||
display: none;
|
|
||||||
text-align: center;
|
|
||||||
color: #667eea;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
|
||||||
border: 4px solid #f3f3f3;
|
|
||||||
border-top: 4px solid #667eea;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin: 0 auto 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.users-container {
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-card {
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-left: 4px solid #667eea;
|
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-card:hover {
|
|
||||||
background: #f5f5f5;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-card h3 {
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-card p {
|
|
||||||
color: #666;
|
|
||||||
margin: 5px 0;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-card strong {
|
|
||||||
color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
background: #ffebee;
|
|
||||||
border-left-color: #f44336;
|
|
||||||
color: #c62828;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
text-align: center;
|
|
||||||
color: #999;
|
|
||||||
padding: 40px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
background: #999;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button:hover {
|
|
||||||
background: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: monospace;
|
|
||||||
color: #d63384;
|
|
||||||
}
|
|
||||||
|
|
||||||
.counter {
|
|
||||||
background: #667eea;
|
|
||||||
color: white;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="it">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>App Meteo - GET Request</title>
|
|
||||||
<link rel="stylesheet" href="styles.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>🌤️ App Meteo</h1>
|
|
||||||
<p class="subtitle">Impara a fare GET request con Fetch API</p>
|
|
||||||
|
|
||||||
<div class="instructions">
|
|
||||||
<h3>📝 Obiettivo dell'Esercizio</h3>
|
|
||||||
<p>Dovrai scrivere il codice JavaScript per:</p>
|
|
||||||
<ol>
|
|
||||||
<li>Recuperare una lista di dati meteo dal server usando <code>fetch()</code></li>
|
|
||||||
<li>Visualizzare i dati ricevuti nella pagina</li>
|
|
||||||
<li>Gestire gli errori di rete</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group">
|
|
||||||
<label for="cityInput">Cerca una città (opzionale):</label>
|
|
||||||
<input type="text" id="cityInput" placeholder="es: Roma">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button onclick="fetchWeatherData()">Carica Dati Meteo</button>
|
|
||||||
|
|
||||||
<div class="loading" id="loading">
|
|
||||||
<div class="spinner"></div>
|
|
||||||
<p>Caricamento dati...</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="result" id="result">
|
|
||||||
<div class="weather-info" id="weatherInfo"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="../index.html" class="back-button">← Torna al Hub</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="script.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
// TODO: Completa questa funzione
|
|
||||||
async function fetchWeatherData() {
|
|
||||||
const loading = document.getElementById('loading');
|
|
||||||
const result = document.getElementById('result');
|
|
||||||
const weatherInfo = document.getElementById('weatherInfo');
|
|
||||||
|
|
||||||
// 1. Mostra il loading spinner
|
|
||||||
loading.style.display = 'block';
|
|
||||||
result.classList.remove('show');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 2. Scrivi qui il fetch per ottenere i dati da:
|
|
||||||
// http://localhost:3000/api/weather
|
|
||||||
// 3. Converti la risposta in JSON
|
|
||||||
// 4. Mostra i dati nella pagina
|
|
||||||
|
|
||||||
// SUGGERIMENTO: Usa la struttura vista nel tutorial
|
|
||||||
// const response = await fetch('...');
|
|
||||||
// const data = await response.json();
|
|
||||||
|
|
||||||
console.log('Scrivi il codice qui!');
|
|
||||||
throw new Error('Codice non implementato');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Errore:', error);
|
|
||||||
result.classList.add('show');
|
|
||||||
weatherInfo.innerHTML = `
|
|
||||||
<div class="weather-detail error">
|
|
||||||
<strong>❌ Errore:</strong> ${error.message}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} finally {
|
|
||||||
loading.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Funzione helper per formattare la risposta
|
|
||||||
function displayWeatherData(data) {
|
|
||||||
const weatherInfo = document.getElementById('weatherInfo');
|
|
||||||
const result = document.getElementById('result');
|
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
// Se è un array
|
|
||||||
weatherInfo.innerHTML = data.map(item => `
|
|
||||||
<div class="weather-detail success">
|
|
||||||
<h3>${item.city || item.name || 'Città sconosciuta'}</h3>
|
|
||||||
<p>🌡️ Temperatura: ${item.temperature || item.temp || 'N/A'}°C</p>
|
|
||||||
<p>💨 Umidità: ${item.humidity || 'N/A'}%</p>
|
|
||||||
<p>☁️ Condizioni: ${item.condition || item.description || 'N/A'}</p>
|
|
||||||
</div>
|
|
||||||
`).join('');
|
|
||||||
} else {
|
|
||||||
// Se è un singolo oggetto
|
|
||||||
weatherInfo.innerHTML = `
|
|
||||||
<div class="weather-detail success">
|
|
||||||
<h3>${data.city || data.name || 'Città sconosciuta'}</h3>
|
|
||||||
<p>🌡️ Temperatura: ${data.temperature || data.temp || 'N/A'}°C</p>
|
|
||||||
<p>💨 Umidità: ${data.humidity || 'N/A'}%</p>
|
|
||||||
<p>☁️ Condizioni: ${data.condition || data.description || 'N/A'}</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.classList.add('show');
|
|
||||||
}
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 40px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 40px;
|
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
text-align: center;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions {
|
|
||||||
background: #e3f2fd;
|
|
||||||
border-left: 4px solid #2196F3;
|
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions h3 {
|
|
||||||
color: #1565c0;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions ol {
|
|
||||||
margin-left: 20px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions li {
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
border: 2px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 1rem;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background: #667eea;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #764ba2;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
|
||||||
display: none;
|
|
||||||
text-align: center;
|
|
||||||
color: #667eea;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
|
||||||
border: 4px solid #f3f3f3;
|
|
||||||
border-top: 4px solid #667eea;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin: 0 auto 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 20px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
border-radius: 8px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.show {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weather-info {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weather-info h2 {
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weather-detail {
|
|
||||||
background: white;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 10px 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
border-left: 4px solid #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
background: #ffebee;
|
|
||||||
border-left-color: #f44336;
|
|
||||||
color: #c62828;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success {
|
|
||||||
background: #e8f5e9;
|
|
||||||
border-left-color: #4caf50;
|
|
||||||
color: #2e7d32;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
background: #999;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button:hover {
|
|
||||||
background: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: monospace;
|
|
||||||
color: #d63384;
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="it">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Todo App - CRUD Operations</title>
|
|
||||||
<link rel="stylesheet" href="styles.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>✓ Todo App</h1>
|
|
||||||
<p class="subtitle">Operazioni CRUD (Create, Read, Update, Delete)</p>
|
|
||||||
|
|
||||||
<div class="instructions">
|
|
||||||
<h3>📝 Obiettivo dell'Esercizio</h3>
|
|
||||||
<p>Dovrai implementare un'app TODO con tutte le operazioni CRUD:</p>
|
|
||||||
<ol>
|
|
||||||
<li><strong>Ricerca:</strong> Seleziona un utente dalla barra di ricerca</li>
|
|
||||||
<li><strong>CREATE:</strong> Aggiungi nuovi todo per l'utente selezionato</li>
|
|
||||||
<li><strong>READ:</strong> Visualizza i todo dell'utente</li>
|
|
||||||
<li><strong>UPDATE:</strong> Segna come completato</li>
|
|
||||||
<li><strong>DELETE:</strong> Elimina todo</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Barra di ricerca utenti -->
|
|
||||||
<div class="search-section">
|
|
||||||
<h3>👤 Seleziona Utente</h3>
|
|
||||||
<div class="search-container">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="userSearch"
|
|
||||||
placeholder="Cerca un utente per nome..."
|
|
||||||
autocomplete="off"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="suggestions" id="suggestions"></div>
|
|
||||||
<div id="selectedUser" class="selected-user">Nessun utente selezionato</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Input per aggiungere todo -->
|
|
||||||
<div class="input-group">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="todoInput"
|
|
||||||
placeholder="Aggiungi un nuovo todo..."
|
|
||||||
onkeypress="if(event.key === 'Enter') addTodo()"
|
|
||||||
>
|
|
||||||
<button class="btn" onclick="addTodo()">Aggiungi</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="loading" id="loading">
|
|
||||||
<div class="spinner"></div>
|
|
||||||
<p>Caricamento...</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="todos-container" id="todosContainer">
|
|
||||||
<!-- I todo verranno inseriti qui -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="../index.html" class="back-button">← Torna al Hub</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="script.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
let currentUserId = null;
|
|
||||||
|
|
||||||
// 1. Funzione che crea un todo (oggetto)
|
|
||||||
function createTodo(id, title, completato) {
|
|
||||||
return { id, title, completato };
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Carica tutti i todos di un particolare utente
|
|
||||||
async function loadUserTodos(userId) {
|
|
||||||
const loading = document.getElementById('loading');
|
|
||||||
const container = document.getElementById('todosContainer');
|
|
||||||
|
|
||||||
loading.style.display = 'block';
|
|
||||||
container.innerHTML = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`http://localhost:3000/api/todos?userId=${userId}`);
|
|
||||||
const todos = await response.json();
|
|
||||||
displayTodos(todos);
|
|
||||||
} catch (error) {
|
|
||||||
container.innerHTML = `<div class="error">❌ ${error.message}</div>`;
|
|
||||||
} finally {
|
|
||||||
loading.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Aggiungi un todo
|
|
||||||
async function addTodo() {
|
|
||||||
if (!currentUserId) {
|
|
||||||
alert('Seleziona un utente prima!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const input = document.getElementById('todoInput');
|
|
||||||
const title = input.value.trim();
|
|
||||||
|
|
||||||
if (!title) {
|
|
||||||
alert('Inserisci un todo!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('http://localhost:3000/api/todos', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ userId: currentUserId, title, completato: false })
|
|
||||||
});
|
|
||||||
input.value = '';
|
|
||||||
loadUserTodos(currentUserId);
|
|
||||||
} catch (error) {
|
|
||||||
alert('Errore: ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Toggle completato
|
|
||||||
async function toggleTodo(id, currentCompleted) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`http://localhost:3000/api/todos/${id}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ completato: !currentCompleted })
|
|
||||||
});
|
|
||||||
loadUserTodos(currentUserId);
|
|
||||||
} catch (error) {
|
|
||||||
alert('Errore: ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Rimuovi un todo
|
|
||||||
async function deleteTodo(id) {
|
|
||||||
if (!confirm('Sei sicuro di voler eliminare?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`http://localhost:3000/api/todos/${id}`, {
|
|
||||||
method: 'DELETE'
|
|
||||||
});
|
|
||||||
loadUserTodos(currentUserId);
|
|
||||||
} catch (error) {
|
|
||||||
alert('Errore: ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visualizza i todos
|
|
||||||
function displayTodos(todos) {
|
|
||||||
const container = document.getElementById('todosContainer');
|
|
||||||
|
|
||||||
if (!Array.isArray(todos) || todos.length === 0) {
|
|
||||||
container.innerHTML = '<div class="empty">Nessun todo</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const todosHTML = todos.map(todo => `
|
|
||||||
<div class="todo-item ${todo.completato ? 'completed' : ''}">
|
|
||||||
<div class="todo-content">
|
|
||||||
<div class="todo-text">${todo.title}</div>
|
|
||||||
<div class="todo-id">ID: ${todo.id}</div>
|
|
||||||
</div>
|
|
||||||
<div class="todo-actions">
|
|
||||||
<button class="btn btn-small btn-complete" onclick="toggleTodo(${todo.id}, ${todo.completato})">
|
|
||||||
${todo.completato ? '↩️' : '✓'}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-small btn-delete" onclick="deleteTodo(${todo.id})">
|
|
||||||
🗑️
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`).join('');
|
|
||||||
|
|
||||||
container.innerHTML = todosHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carica gli utenti nella ricerca
|
|
||||||
async function loadUsers() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('http://localhost:3000/api/users');
|
|
||||||
const users = await response.json();
|
|
||||||
|
|
||||||
const searchInput = document.getElementById('userSearch');
|
|
||||||
const suggestionsContainer = document.getElementById('suggestions');
|
|
||||||
|
|
||||||
// Filtra gli utenti mentre digiti
|
|
||||||
searchInput.addEventListener('input', (e) => {
|
|
||||||
const searchTerm = e.target.value.toLowerCase();
|
|
||||||
|
|
||||||
if (searchTerm.length === 0) {
|
|
||||||
suggestionsContainer.innerHTML = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filtered = users.filter(user =>
|
|
||||||
user.nome.toLowerCase().includes(searchTerm) ||
|
|
||||||
user.cognome.toLowerCase().includes(searchTerm)
|
|
||||||
);
|
|
||||||
|
|
||||||
suggestionsContainer.innerHTML = filtered.map(user => `
|
|
||||||
<div class="suggestion-item" onclick="selectUser(${user.id}, '${user.nome} ${user.cognome}')">
|
|
||||||
${user.nome} ${user.cognome} (${user.email})
|
|
||||||
</div>
|
|
||||||
`).join('');
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Errore nel caricamento utenti:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seleziona un utente
|
|
||||||
function selectUser(userId, userName) {
|
|
||||||
currentUserId = userId;
|
|
||||||
document.getElementById('userSearch').value = userName;
|
|
||||||
document.getElementById('suggestions').innerHTML = '';
|
|
||||||
document.getElementById('selectedUser').textContent = `Utente: ${userName}`;
|
|
||||||
loadUserTodos(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carica gli utenti all'avvio
|
|
||||||
loadUsers();
|
|
||||||
@@ -1,316 +0,0 @@
|
|||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 40px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 700px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 40px;
|
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
text-align: center;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions {
|
|
||||||
background: #e3f2fd;
|
|
||||||
border-left: 4px solid #2196F3;
|
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions h3 {
|
|
||||||
color: #1565c0;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions ol {
|
|
||||||
margin-left: 20px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions li {
|
|
||||||
margin: 8px 0;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
flex: 1;
|
|
||||||
padding: 12px;
|
|
||||||
border: 2px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 1rem;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 12px 20px;
|
|
||||||
background: #667eea;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background: #764ba2;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-small {
|
|
||||||
padding: 6px 12px;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-delete {
|
|
||||||
background: #f44336;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-delete:hover {
|
|
||||||
background: #d32f2f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-complete {
|
|
||||||
background: #4caf50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-complete:hover {
|
|
||||||
background: #388e3c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todos-container {
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-item {
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-left: 4px solid #667eea;
|
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-item:hover {
|
|
||||||
background: #f5f5f5;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-item.completed {
|
|
||||||
opacity: 0.6;
|
|
||||||
border-left-color: #4caf50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-item.completed .todo-text {
|
|
||||||
text-decoration: line-through;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-text {
|
|
||||||
color: #333;
|
|
||||||
font-size: 1rem;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-id {
|
|
||||||
color: #999;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
background: #ffebee;
|
|
||||||
border-left: 4px solid #f44336;
|
|
||||||
color: #c62828;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success {
|
|
||||||
background: #e8f5e9;
|
|
||||||
border-left: 4px solid #4caf50;
|
|
||||||
color: #2e7d32;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
text-align: center;
|
|
||||||
color: #999;
|
|
||||||
padding: 40px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
|
||||||
display: none;
|
|
||||||
text-align: center;
|
|
||||||
color: #667eea;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
|
||||||
border: 4px solid #f3f3f3;
|
|
||||||
border-top: 4px solid #667eea;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin: 0 auto 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
background: #999;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button:hover {
|
|
||||||
background: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: monospace;
|
|
||||||
color: #d63384;
|
|
||||||
}
|
|
||||||
|
|
||||||
.counter {
|
|
||||||
background: #667eea;
|
|
||||||
color: white;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-section {
|
|
||||||
background: #f0f4ff;
|
|
||||||
border: 2px solid #667eea;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-section h3 {
|
|
||||||
color: #667eea;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#userSearch {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
border: 2px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 1rem;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#userSearch:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.suggestions {
|
|
||||||
background: white;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-top: none;
|
|
||||||
border-radius: 0 0 4px 4px;
|
|
||||||
max-height: 250px;
|
|
||||||
overflow-y: auto;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.suggestions:not(:empty) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.suggestion-item {
|
|
||||||
padding: 12px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.suggestion-item:hover {
|
|
||||||
background: #f0f4ff;
|
|
||||||
color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.suggestion-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-user {
|
|
||||||
color: #666;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="it">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Tutorial - API REST e Fetch</title>
|
|
||||||
<link rel="stylesheet" href="styles.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>📡 Tutorial - API REST e Fetch</h1>
|
|
||||||
<p class="subtitle">Impara a comunicare con le API REST</p>
|
|
||||||
|
|
||||||
<h2>Cos'è un'API REST?</h2>
|
|
||||||
<p>
|
|
||||||
Un'API REST (Representational State Transfer) è un insieme di regole che permette a due applicazioni di comunicare.
|
|
||||||
Attraverso una API REST puoi <strong>recuperare dati</strong>, <strong>crearli</strong>, <strong>modificarli</strong> e <strong>eliminarli</strong>
|
|
||||||
da un server usando richieste HTTP.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Metodi HTTP Principali</h2>
|
|
||||||
<div class="endpoint-list">
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>GET</strong> - Recupera dati dal server
|
|
||||||
</div>
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>POST</strong> - Crea nuovi dati sul server
|
|
||||||
</div>
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>PUT</strong> - Aggiorna completamente un dato
|
|
||||||
</div>
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>PATCH</strong> - Aggiorna parzialmente un dato
|
|
||||||
</div>
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>DELETE</strong> - Elimina un dato dal server
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>L'API che Useremo</h2>
|
|
||||||
<p>
|
|
||||||
Utilizzeremo un server JSON locale accessibile a <code>http://localhost:3000/api</code>
|
|
||||||
che contiene risorse diverse (utenti, todo, ecc.).
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="note">
|
|
||||||
<strong>⚠️ Importante:</strong> Prima di iniziare gli esercizi, assicurati che il server sia avviato!
|
|
||||||
Apri il terminale nella cartella <code>server-api</code> e digita: <code>npm start</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>1. Fetch API - GET Request</h2>
|
|
||||||
<p>La fetch API è il modo moderno per fare richieste HTTP in JavaScript.</p>
|
|
||||||
|
|
||||||
<h3>Sintassi Base:</h3>
|
|
||||||
<div class="code-block">
|
|
||||||
<code>fetch('https://api.example.com/data')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => console.log(data))
|
|
||||||
.catch(error => console.error('Errore:', error));</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3>Come Funziona:</h3>
|
|
||||||
<ol>
|
|
||||||
<li><strong>fetch(url)</strong> - Invia la richiesta al server</li>
|
|
||||||
<li><strong>.then(response => response.json())</strong> - Converte la risposta in JSON</li>
|
|
||||||
<li><strong>.then(data => ...)</strong> - Ricevi e usa i dati</li>
|
|
||||||
<li><strong>.catch(error => ...)</strong> - Gestisci gli errori</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h3>Esempio Pratico:</h3>
|
|
||||||
<div class="code-block">
|
|
||||||
<code>fetch('http://localhost:3000/api/users')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(users => {
|
|
||||||
console.log('Utenti ricevuti:', users);
|
|
||||||
users.forEach(user => {
|
|
||||||
console.log(`${user.name} - ${user.email}`);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Errore:', error));</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>2. Fetch API - POST Request</h2>
|
|
||||||
<p>Per inviare dati al server, usiamo il metodo POST.</p>
|
|
||||||
|
|
||||||
<h3>Sintassi:</h3>
|
|
||||||
<div class="code-block">
|
|
||||||
<code>fetch('https://api.example.com/data', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: 'Mario',
|
|
||||||
email: 'mario@example.com'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => console.log('Creato:', data))
|
|
||||||
.catch(error => console.error('Errore:', error));</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>3. Fetch API - DELETE Request</h2>
|
|
||||||
<p>Per eliminare un dato dal server.</p>
|
|
||||||
|
|
||||||
<div class="code-block">
|
|
||||||
<code>fetch('https://api.example.com/users/1', {
|
|
||||||
method: 'DELETE'
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => console.log('Eliminato:', data))
|
|
||||||
.catch(error => console.error('Errore:', error));</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>4. Async/Await - Sintassi Moderna</h2>
|
|
||||||
<p>Una sintassi più leggibile e facile da capire rispetto alle Promise.</p>
|
|
||||||
|
|
||||||
<h3>Sintassi:</h3>
|
|
||||||
<div class="code-block">
|
|
||||||
<code>async function fetchUsers() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('http://localhost:3000/api/users');
|
|
||||||
const users = await response.json();
|
|
||||||
console.log('Utenti:', users);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Errore:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchUsers();</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="success">
|
|
||||||
<strong>✓ Vantaggi:</strong> Il codice è più leggibile e simile al codice sincrono tradizionale.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>5. Gestione degli Errori</h2>
|
|
||||||
<p>È importante gestire gli errori quando facciamo richieste HTTP.</p>
|
|
||||||
|
|
||||||
<div class="code-block">
|
|
||||||
<code>fetch('http://localhost:3000/api/users')
|
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Errore HTTP: ${response.status}`);
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(data => console.log(data))
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Errore nella richiesta:', error.message);
|
|
||||||
});</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>6. Endpoint Disponibili sul Nostro Server</h2>
|
|
||||||
<div class="endpoint-list">
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>GET /api/users</strong> - Ottieni lista utenti<br>
|
|
||||||
<small>Esempio: http://localhost:3000/api/users</small>
|
|
||||||
</div>
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>GET /api/users/:id</strong> - Ottieni un utente<br>
|
|
||||||
<small>Esempio: http://localhost:3000/api/users/1</small>
|
|
||||||
</div>
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>POST /api/users</strong> - Crea nuovo utente<br>
|
|
||||||
<small>Body: { "name": "Mario", "email": "mario@example.com" }</small>
|
|
||||||
</div>
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>PUT /api/users/:id</strong> - Aggiorna utente<br>
|
|
||||||
<small>Body: { "name": "Luigi", "email": "luigi@example.com" }</small>
|
|
||||||
</div>
|
|
||||||
<div class="endpoint-item">
|
|
||||||
<strong>DELETE /api/users/:id</strong> - Elimina utente<br>
|
|
||||||
<small>Esempio: http://localhost:3000/api/users/1</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Ora Sei Pronto!</h2>
|
|
||||||
<p>
|
|
||||||
Hai imparato i concetti fondamentali delle API REST e della Fetch API.
|
|
||||||
Ora puoi procedere agli esercizi e metterli in pratica!
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<a href="../index.html" class="back-button">← Torna al Hub</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 40px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 40px;
|
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
text-align: center;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #667eea;
|
|
||||||
margin-top: 40px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border-bottom: 3px solid #667eea;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
font-size: 1.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
color: #555;
|
|
||||||
margin-top: 25px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
font-size: 1.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: #666;
|
|
||||||
line-height: 1.8;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
color: #d63384;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-block {
|
|
||||||
background: #f5f5f5;
|
|
||||||
border-left: 4px solid #667eea;
|
|
||||||
padding: 15px;
|
|
||||||
margin: 20px 0;
|
|
||||||
border-radius: 5px;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-block code {
|
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
color: #333;
|
|
||||||
display: block;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.example-section {
|
|
||||||
background: #e3f2fd;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
margin: 20px 0;
|
|
||||||
border-left: 5px solid #2196F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.example-section h4 {
|
|
||||||
color: #1565c0;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.endpoint-list {
|
|
||||||
background: #f9f9f9;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.endpoint-item {
|
|
||||||
margin: 15px 0;
|
|
||||||
padding: 15px;
|
|
||||||
background: white;
|
|
||||||
border-left: 4px solid #667eea;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.endpoint-item strong {
|
|
||||||
color: #667eea;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul, ol {
|
|
||||||
margin-left: 20px;
|
|
||||||
color: #666;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 12px 30px;
|
|
||||||
background: #667eea;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button:hover {
|
|
||||||
background: #764ba2;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.note {
|
|
||||||
background: #fff3cd;
|
|
||||||
border-left: 4px solid #ffc107;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note strong {
|
|
||||||
color: #856404;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success {
|
|
||||||
background: #d4edda;
|
|
||||||
border-left: 4px solid #28a745;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success strong {
|
|
||||||
color: #155724;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interactive-demo {
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interactive-demo input,
|
|
||||||
.interactive-demo button {
|
|
||||||
padding: 10px;
|
|
||||||
margin: 5px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interactive-demo button {
|
|
||||||
background: #667eea;
|
|
||||||
color: white;
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interactive-demo button:hover {
|
|
||||||
background: #764ba2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
background: white;
|
|
||||||
padding: 15px;
|
|
||||||
margin-top: 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user