Kurs vibe coding z AI · Moduł 12: Medium Clone
Performance Monitoring - Sentry/Analytics
W tej lekcji2
Monitorowanie błędów i wydajności w produkcji.
Sentry Setup
npm install @sentry/nextjs// sentry.client.config.ts
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
environment: process.env.NODE_ENV
});Error Boundaries
'use client';
import { Component, ReactNode } from 'react';
import * as Sentry from '@sentry/nextjs';
class ErrorBoundary extends Component<
{ children: ReactNode },
{ hasError: boolean }
> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: any) {
Sentry.captureException(error, { contexts: { react: errorInfo } });
}
render() {
if (this.state.hasError) {
return <div>Something went wrong</div>;
}
return this.props.children;
}
}Do zobaczenia!
Kod do tej lekcji: App.tsx
'use client';
import { useState } from 'react';
interface Response {
id: string;
author: {
name: string;
avatar: string;
};
content: string;
claps: number;
createdAt: Date;
replies: Response[];
}
const mockResponses: Response[] = [
{
id: '1',
author: {
name: 'Kapitan Redbeard',
avatar: 'https://i.pravatar.cc/150?img=12'
},
content: 'Świetny artykuł! Czytało się go z prawdziwą przyjemnością. Szczególnie pomogły mi przykłady.',
claps: 24,
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 2),
replies: [
{
id: '2',
author: {
name: 'Marynarz Sam',
avatar: 'https://i.pravatar.cc/150?img=33'
},
content: 'Zgadzam się! Dzięki praktycznym przykładom łatwo to zrozumieć.',
claps: 8,
createdAt: new Date(Date.now() - 1000 * 60 * 30),
replies: []
}
]
},
{
id: '3',
author: {
name: 'Nawigatorka Nancy',
avatar: 'https://i.pravatar.cc/150?img=45'
},
content: 'Chętnie przeczytam więcej takich tekstów. Dzięki, że się dzielisz!',
claps: 15,
createdAt: new Date(Date.now() - 1000 * 60 * 60),
replies: []
}
];
function ResponseItem({ response }: { response: Response }) {
return (
<div className="border-b border-gray-200 pb-6">
<div className="flex items-start gap-3">
<img
src={response.author.avatar}
alt={response.author.name}
className="w-10 h-10 rounded-full"
/>
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<span className="font-semibold">{response.author.name}</span>
<span className="text-sm text-gray-500">2 godz. temu</span>
</div>
<p className="text-gray-800 leading-relaxed mb-3">
{response.content}
</p>
<div className="flex items-center gap-4 text-sm text-gray-500">
<button className="flex items-center gap-1 hover:text-green-600">
<span>Brawo</span>
<span>{response.claps}</span>
</button>
<button className="hover:text-gray-700">Odpowiedz</button>
</div>
{response.replies.length > 0 && (
<div className="mt-4 pl-6 border-l-2 border-gray-100 space-y-4">
{response.replies.map(reply => (
<ResponseItem key={reply.id} response={reply} />
))}
</div>
)}
</div>
</div>
</div>
);
}
export default function ResponseSection() {
const [content, setContent] = useState('');
const [responses, setResponses] = useState(mockResponses);
const handleSubmit = () => {
if (!content.trim()) return;
alert(`Opublikowano odpowiedź: ${content}`);
setContent('');
};
return (
<div className="min-h-screen bg-white">
<div className="max-w-2xl mx-auto px-8 py-12">
<h2 className="text-2xl font-bold mb-6">
Odpowiedzi ({responses.length})
</h2>
{/* Napisz odpowiedź */}
<div className="mb-8 border border-gray-200 rounded-lg p-4">
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder="Co o tym myślisz?"
className="w-full outline-none resize-none text-lg"
rows={3}
/>
<div className="flex justify-end mt-2">
<button
onClick={handleSubmit}
disabled={!content.trim()}
className="px-6 py-2 bg-green-600 text-white rounded-full font-semibold hover:bg-green-700 disabled:bg-gray-300 disabled:cursor-not-allowed"
>
Odpowiedz
</button>
</div>
</div>
{/* Odpowiedzi */}
<div className="space-y-6">
{responses.map(response => (
<ResponseItem key={response.id} response={response} />
))}
</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. Do czego służy Sentry w aplikacji Next.js?
2. Która metoda Error Boundary służy do zaraportowania błędu do Sentry?
Zadania praktyczne w grze
- Edytor kodu
Error Boundary z raportowaniem do Sentry
- Układanie w pionie
Uporządkuj kroki obsługi błędu przez Error Boundary: