Cours Vibe coding avec l'IA · Module 11: Clone de Spotify
Déploiement - Vercel/Netlify
Dans cette leçon3
Déployer une application Next.js en production.
Déploiement sur Vercel
1. Intégration GitHub
- Pousse ton code sur GitHub
- Rends-toi sur vercel.com
- Importe le dépôt GitHub
- Clique sur Deploy
2. Variables d'environnement
Dans le tableau de bord Vercel → Settings → Environment Variables :
1DATABASE_URL=your_db_url
2OPENAI_API_KEY=your_key
3NEXTAUTH_SECRET=your_secret3. Domaine personnalisé
Settings → Domains → Add domain → Vérifier le DNS
Déploiement en CLI
1npm i -g vercel
2vercel login
3vercelOptimisation du build
1// next.config.js
2module.exports = {
3 images: {
4 domains: ['yourdomain.com'],
5 formats: ['image/avif', 'image/webp']
6 },
7 compress: true,
8 poweredByHeader: false
9}À très bientôt !
Code de cette leçon : App.tsx
1'use client';
2
3import { useState, useEffect } from 'react';
4
5interface Track {
6 id: string;
7 title: string;
8 artist: string;
9 coverArt: string;
10 duration: number;
11}
12
13interface Artist {
14 id: string;
15 name: string;
16 avatar: string;
17}
18
19const mockTracks: Track[] = [
20 {
21 id: '1',
22 title: "The Pirate's Anthem",
23 artist: 'Captain Redbeard',
24 coverArt: 'https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=200&h=200&fit=crop',
25 duration: 240
26 },
27 {
28 id: '2',
29 title: 'Sailing Adventure',
30 artist: 'Captain Redbeard',
31 coverArt: 'https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?w=200&h=200&fit=crop',
32 duration: 195
33 }
34];
35
36const mockArtists: Artist[] = [
37 {
38 id: '1',
39 name: 'Captain Redbeard',
40 avatar: 'https://i.pravatar.cc/150?img=12'
41 },
42 {
43 id: '2',
44 name: 'Crew Council',
45 avatar: 'https://i.pravatar.cc/150?img=33'
46 }
47];
48
49export default function SearchPage() {
50 const [query, setQuery] = useState('');
51 const [activeTab, setActiveTab] = useState<'all' | 'tracks' | 'artists'>('all');
52 const [filteredTracks, setFilteredTracks] = useState<Track[]>([]);
53 const [filteredArtists, setFilteredArtists] = useState<Artist[]>([]);
54
55 useEffect(() => {
56 if (query.length > 0) {
57 const timeout = setTimeout(() => {
58 setFilteredTracks(
59 mockTracks.filter(t =>
60 t.title.toLowerCase().includes(query.toLowerCase()) ||
61 t.artist.toLowerCase().includes(query.toLowerCase())
62 )
63 );
64 setFilteredArtists(
65 mockArtists.filter(a =>
66 a.name.toLowerCase().includes(query.toLowerCase())
67 )
68 );
69 }, 300);
70
71 return () => clearTimeout(timeout);
72 } else {
73 setFilteredTracks([]);
74 setFilteredArtists([]);
75 }
76 }, [query]);
77
78 return (
79 <div className="min-h-screen bg-black text-white p-8">
80 {/* Champ de recherche */}
81 <div className="mb-6">
82 <div className="relative max-w-md">
83 <input
84 type="text"
85 value={query}
86 onChange={(e) => setQuery(e.target.value)}
87 placeholder="Que veux-tu écouter ?"
88 className="w-full bg-gray-800 text-white px-12 py-3 rounded-full outline-none focus:ring-2 focus:ring-white"
89 autoFocus
90 />
91 <span className="absolute left-4 top-1/2 transform -translate-y-1/2 text-xl">
92 ⌕
93 </span>
94 </div>
95 </div>
96
97 {!query ? (
98 <div>
99 <h2 className="text-2xl font-bold mb-4">Tout parcourir</h2>
100 <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
101 {['Sea Shanties', 'Battle Anthems', 'Treasure Hunting', 'Calm Seas', 'Epic Adventures', 'Ship Repairs'].map(genre => (
102 <button
103 key={genre}
104 className="aspect-square bg-gradient-to-br from-purple-600 to-pink-600 rounded-lg p-4 font-bold text-lg hover:scale-105 transition"
105 >
106 {genre}
107 </button>
108 ))}
109 </div>
110 </div>
111 ) : (
112 <>
113 {/* Onglets */}
114 <div className="flex gap-4 mb-6 border-b border-gray-800">
115 {[
116 { key: 'all', label: 'Tout' },
117 { key: 'tracks', label: 'Titres' },
118 { key: 'artists', label: 'Artistes' }
119 ].map(tab => (
120 <button
121 key={tab.key}
122 onClick={() => setActiveTab(tab.key as any)}
123 className={`pb-2 px-4 font-semibold ${
124 activeTab === tab.key
125 ? 'border-b-2 border-green-500'
126 : 'text-gray-400'
127 }`}
128 >
129 {tab.label}
130 </button>
131 ))}
132 </div>
133
134 {/* Résultats */}
135 <div className="space-y-6">
136 {(activeTab === 'all' || activeTab === 'tracks') && filteredTracks.length > 0 && (
137 <div>
138 <h3 className="text-xl font-bold mb-4">Titres</h3>
139 <div className="space-y-2">
140 {filteredTracks.map(track => (
141 <div
142 key={track.id}
143 className="flex items-center gap-4 p-2 rounded hover:bg-gray-800 cursor-pointer"
144 >
145 <img src={track.coverArt} alt={track.title} className="w-12 h-12 rounded" />
146 <div className="flex-1">
147 <div className="font-semibold">{track.title}</div>
148 <div className="text-sm text-gray-400">{track.artist}</div>
149 </div>
150 <span className="text-sm text-gray-400">
151 {Math.floor(track.duration / 60)}:{(track.duration % 60).toString().padStart(2, '0')}
152 </span>
153 </div>
154 ))}
155 </div>
156 </div>
157 )}
158
159 {(activeTab === 'all' || activeTab === 'artists') && filteredArtists.length > 0 && (
160 <div>
161 <h3 className="text-xl font-bold mb-4">Artistes</h3>
162 <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
163 {filteredArtists.map(artist => (
164 <div
165 key={artist.id}
166 className="bg-gray-900 p-4 rounded hover:bg-gray-800 cursor-pointer"
167 >
168 <img src={artist.avatar} alt={artist.name} className="w-full aspect-square rounded-full mb-4" />
169 <div className="font-semibold">{artist.name}</div>
170 <div className="text-sm text-gray-400">Artiste</div>
171 </div>
172 ))}
173 </div>
174 </div>
175 )}
176 </div>
177 </>
178 )}
179 </div>
180 );
181}Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Comment déployer une application Next.js sur Vercel via l'intégration GitHub ?
2. Où configure-t-on les variables d'environnement (par exemple DATABASE_URL) dans Vercel ?
Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Clique dans l'ordre
Clique pour construire la commande d'installation du CLI Vercel :
- Tri vertical
Ordonne les étapes de déploiement de l'application via le CLI Vercel :