Kurs vibe coding z AI · Moduł 14: Pinterest Clone
Database - Prisma ORM
W tej lekcji3
Integracja z bazą danych.
Prisma Setup
npm install prisma @prisma/client
npx prisma initSchema
// prisma/schema.prisma
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String
author User @relation(fields: [authorId], references: [id])
authorId Int
}Usage
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// Create
await prisma.user.create({
data: {
name: 'Jack',
email: 'jack@pirates.com'
}
});
// Read
const users = await prisma.user.findMany({
include: { posts: true }
});Do zobaczenia!
Kod do tej lekcji: App.tsx
'use client';
import { useState } from 'react';
interface Board {
id: string;
name: string;
pinCount: number;
coverImage?: string;
}
const mockBoards: Board[] = [
{ id: '1', name: 'Pomysły na podróże', pinCount: 247, coverImage: 'https://images.unsplash.com/photo-1488646953014-85cb44e25828?w=100&h=100&fit=crop' },
{ id: '2', name: 'Przepisy', pinCount: 156, coverImage: 'https://images.unsplash.com/photo-1495521821757-a1efb6729352?w=100&h=100&fit=crop' },
{ id: '3', name: 'Wystrój wnętrz', pinCount: 89, coverImage: 'https://images.unsplash.com/photo-1616486338812-3dadae4b4ace?w=100&h=100&fit=crop' }
];
const mockPin = {
title: 'Piękny zachód słońca',
description: 'Niesamowity zachód słońca nad oceanem w świetle złotej godziny',
imageUrl: 'https://images.unsplash.com/photo-1544551763-46a013bb70d5?w=100&h=100&fit=crop'
};
export default function SavePinModal() {
const [isOpen, setIsOpen] = useState(true);
const [selectedBoardId, setSelectedBoardId] = useState<string | null>(null);
if (!isOpen) {
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center">
<button
onClick={() => setIsOpen(true)}
className="px-6 py-3 bg-red-600 text-white rounded-full font-semibold"
>
Zapisz pin
</button>
</div>
);
}
return (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
<div className="bg-white rounded-2xl max-w-md w-full max-h-[80vh] flex flex-col">
{/* Nagłówek */}
<div className="p-6 border-b border-gray-200">
<div className="flex items-center justify-between">
<h2 className="text-xl font-bold">Zapisz na tablicy</h2>
<button onClick={() => setIsOpen(false)} className="text-gray-500 hover:text-black text-xl">
×
</button>
</div>
</div>
{/* Podgląd pinu */}
<div className="p-6 border-b border-gray-200">
<div className="flex gap-3">
<img src={mockPin.imageUrl} alt={mockPin.title} className="w-16 h-16 rounded object-cover" />
<div className="flex-1">
<h3 className="font-semibold">{mockPin.title}</h3>
<p className="text-sm text-gray-600 line-clamp-2">{mockPin.description}</p>
</div>
</div>
</div>
{/* Tablice */}
<div className="flex-1 overflow-y-auto p-6">
<button className="w-full p-4 border-2 border-dashed border-gray-300 rounded-2xl hover:border-gray-400 mb-4">
<div className="flex items-center gap-3">
<div className="w-12 h-12 bg-gray-200 rounded flex items-center justify-center text-2xl">
+
</div>
<span className="font-semibold">Utwórz tablicę</span>
</div>
</button>
{mockBoards.map(board => (
<button
key={board.id}
onClick={() => setSelectedBoardId(board.id)}
className={`w-full p-4 rounded-2xl hover:bg-gray-100 mb-2 ${
selectedBoardId === board.id ? 'bg-gray-100 ring-2 ring-blue-500' : ''
}`}
>
<div className="flex items-center gap-3">
{board.coverImage ? (
<img src={board.coverImage} alt={board.name} className="w-12 h-12 rounded object-cover" />
) : (
<div className="w-12 h-12 bg-gray-200 rounded" />
)}
<div className="flex-1 text-left">
<div className="font-semibold">{board.name}</div>
<div className="text-sm text-gray-600">{board.pinCount} pinów</div>
</div>
{selectedBoardId === board.id && <div className="text-blue-500 text-xl">√</div>}
</div>
</button>
))}
</div>
{/* Przycisk zapisu */}
<div className="p-6 border-t border-gray-200">
<button
disabled={!selectedBoardId}
className="w-full py-3 bg-red-600 text-white rounded-full font-semibold hover:bg-red-700 disabled:bg-gray-300"
>
Zapisz
</button>
</div>
</div>
</div>
);
}Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Czym jest Prisma w aplikacji?
2. Jak w findMany pobrać powiązane rekordy (np. posty użytkownika)?
Zadania praktyczne w grze
- Klikanie w kolejności
Kliknij elementy, aby zbudować komendę instalacji Prisma:
- Edytor kodu
Stwórz model i zapytania Prisma
- Układanie w pionie
Uporządkuj etapy pracy z bazą przez Prisma: