Cours Vibe coding avec l'IA · Module 14: Clone de Pinterest
Base de données - Prisma ORM
Dans cette leçon3
Intégration de la base de données.
Configuration de Prisma
1npm install prisma @prisma/client
2npx prisma initSchéma
1// prisma/schema.prisma
2model User {
3 id Int @id @default(autoincrement())
4 name String
5 email String @unique
6 posts Post[]
7}
8
9model Post {
10 id Int @id @default(autoincrement())
11 title String
12 content String
13 author User @relation(fields: [authorId], references: [id])
14 authorId Int
15}Utilisation
1import { PrismaClient } from '@prisma/client';
2
3const prisma = new PrismaClient();
4
5// Create
6await prisma.user.create({
7 data: {
8 name: 'Jack',
9 email: 'jack@pirates.com'
10 }
11});
12
13// Read
14const users = await prisma.user.findMany({
15 include: { posts: true }
16});À bientôt !
Code de cette leçon : App.tsx
1'use client';
2
3import { useState } from 'react';
4
5interface Board {
6 id: string;
7 name: string;
8 pinCount: number;
9 coverImage?: string;
10}
11
12const mockBoards: Board[] = [
13 { id: '1', name: 'Idées de voyage', pinCount: 247, coverImage: 'https://images.unsplash.com/photo-1488646953014-85cb44e25828?w=100&h=100&fit=crop' },
14 { id: '2', name: 'Recettes', pinCount: 156, coverImage: 'https://images.unsplash.com/photo-1495521821757-a1efb6729352?w=100&h=100&fit=crop' },
15 { id: '3', name: 'Décoration intérieure', pinCount: 89, coverImage: 'https://images.unsplash.com/photo-1616486338812-3dadae4b4ace?w=100&h=100&fit=crop' }
16];
17
18const mockPin = {
19 title: 'Magnifique coucher de soleil',
20 description: 'Superbe coucher de soleil sur l\'océan avec la lumière de l\'heure dorée',
21 imageUrl: 'https://images.unsplash.com/photo-1544551763-46a013bb70d5?w=100&h=100&fit=crop'
22};
23
24export default function SavePinModal() {
25 const [isOpen, setIsOpen] = useState(true);
26 const [selectedBoardId, setSelectedBoardId] = useState<string | null>(null);
27
28 if (!isOpen) {
29 return (
30 <div className="min-h-screen bg-gray-100 flex items-center justify-center">
31 <button
32 onClick={() => setIsOpen(true)}
33 className="px-6 py-3 bg-red-600 text-white rounded-full font-semibold"
34 >
35 Enregistrer le pin
36 </button>
37 </div>
38 );
39 }
40
41 return (
42 <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
43 <div className="bg-white rounded-2xl max-w-md w-full max-h-[80vh] flex flex-col">
44 {/* Header */}
45 <div className="p-6 border-b border-gray-200">
46 <div className="flex items-center justify-between">
47 <h2 className="text-xl font-bold">Enregistrer dans un tableau</h2>
48 <button onClick={() => setIsOpen(false)} className="text-gray-500 hover:text-black text-xl">
49 ×
50 </button>
51 </div>
52 </div>
53
54 {/* Pin Preview */}
55 <div className="p-6 border-b border-gray-200">
56 <div className="flex gap-3">
57 <img src={mockPin.imageUrl} alt={mockPin.title} className="w-16 h-16 rounded object-cover" />
58 <div className="flex-1">
59 <h3 className="font-semibold">{mockPin.title}</h3>
60 <p className="text-sm text-gray-600 line-clamp-2">{mockPin.description}</p>
61 </div>
62 </div>
63 </div>
64
65 {/* Boards */}
66 <div className="flex-1 overflow-y-auto p-6">
67 <button className="w-full p-4 border-2 border-dashed border-gray-300 rounded-2xl hover:border-gray-400 mb-4">
68 <div className="flex items-center gap-3">
69 <div className="w-12 h-12 bg-gray-200 rounded flex items-center justify-center text-2xl">
70 +
71 </div>
72 <span className="font-semibold">Créer un tableau</span>
73 </div>
74 </button>
75
76 {mockBoards.map(board => (
77 <button
78 key={board.id}
79 onClick={() => setSelectedBoardId(board.id)}
80 className={`w-full p-4 rounded-2xl hover:bg-gray-100 mb-2 ${
81 selectedBoardId === board.id ? 'bg-gray-100 ring-2 ring-blue-500' : ''
82 }`}
83 >
84 <div className="flex items-center gap-3">
85 {board.coverImage ? (
86 <img src={board.coverImage} alt={board.name} className="w-12 h-12 rounded object-cover" />
87 ) : (
88 <div className="w-12 h-12 bg-gray-200 rounded" />
89 )}
90 <div className="flex-1 text-left">
91 <div className="font-semibold">{board.name}</div>
92 <div className="text-sm text-gray-600">{board.pinCount} pins</div>
93 </div>
94 {selectedBoardId === board.id && <div className="text-blue-500 text-xl">√</div>}
95 </div>
96 </button>
97 ))}
98 </div>
99
100 {/* Save Button */}
101 <div className="p-6 border-t border-gray-200">
102 <button
103 disabled={!selectedBoardId}
104 className="w-full py-3 bg-red-600 text-white rounded-full font-semibold hover:bg-red-700 disabled:bg-gray-300"
105 >
106 Enregistrer
107 </button>
108 </div>
109 </div>
110 </div>
111 );
112}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. Qu'est-ce que Prisma dans une application ?
2. Comment récupères-tu des enregistrements liés dans findMany (par exemple les posts d'un utilisateur) ?
Tâches pratiques dans le jeu
- Clique dans l'ordre
Clique sur les éléments pour construire la commande d'installation de Prisma :
- Éditeur de code
Crée un modèle et des requêtes Prisma
- Tri vertical
Ordonne les étapes du travail avec la base de données via Prisma :