Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},
"dependencies": {
"@auth/prisma-adapter": "^2.11.0",
"@heroicons/react": "^2.2.0",
"@hookform/resolvers": "^5.2.2",
"@prisma/client": "^6.17.1",
"bcryptjs": "^3.0.2",
Expand Down
9 changes: 5 additions & 4 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import SessionProvider from "@/components/providers/SessionProvider";
import AppShell from "@/components/layout/AppShell";

const geistSans = Geist({
variable: "--font-geist-sans",
Expand All @@ -25,10 +26,10 @@ export default function RootLayout({
}>) {
return (
<html lang="pl">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<SessionProvider>{children}</SessionProvider>
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
<SessionProvider>
<AppShell>{children}</AppShell>
</SessionProvider>
</body>
</html>
);
Expand Down
34 changes: 34 additions & 0 deletions src/app/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { LoadingSpinner, Skeleton } from "@/components/ui";

export default function Loading() {
return (
<div className="mx-auto max-w-6xl p-6 space-y-6">
{/* mały spinner na górze */}
<div className="flex justify-center">
<LoadingSpinner />
</div>

{/* skeleton hero */}
<div className="space-y-3">
<Skeleton className="h-7 w-64" />
<Skeleton className="h-4 w-full max-w-xl" />
<Skeleton className="h-4 w-48" />
</div>

{/* skeleton statystyki */}
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<Skeleton className="h-24 w-full" />
<Skeleton className="h-24 w-full" />
<Skeleton className="h-24 w-full" />
</div>

{/* skeleton lista ogłoszeń */}
<div className="space-y-3">
<Skeleton className="h-6 w-40" />
<Skeleton className="h-20 w-full" />
<Skeleton className="h-20 w-full" />
<Skeleton className="h-20 w-full" />
</div>
</div>
);
}
254 changes: 144 additions & 110 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,144 +1,178 @@
import { auth } from "@/lib/auth"
import { prisma } from "@/lib/prisma"
import Link from "next/link"
import { signOut } from "@/lib/auth"
import { auth, signOut } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import Link from "next/link";

export default async function Home() {
const session = await auth()

// Fetch some posts from database
const session = await auth();

const posts = await prisma.post.findMany({
take: 3,
orderBy: { createdAt: 'desc' },
orderBy: { createdAt: "desc" },
include: {
author: {
select: {
name: true,
email: true,
}
},
},
_count: {
select: { comments: true }
}
}
})
select: { comments: true },
},
},
});

return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<header className="bg-white shadow">
<div className="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8 flex justify-between items-center">
<div>
<h1 className="text-3xl font-bold text-gray-900">🏠 LocalAid</h1>
<p className="text-sm text-gray-600">Pomoc sąsiedzka w Twojej okolicy</p>
</div>
<div className="flex gap-4 items-center">
{session ? (
<>
<span className="text-sm text-gray-700">
Cześć, <strong>{session.user?.name || session.user?.email}</strong>
</span>
<form
action={async () => {
"use server"
await signOut()
}}
>
<button
type="submit"
className="bg-red-600 text-white px-4 py-2 rounded-md hover:bg-red-700"
>
Wyloguj
</button>
</form>
</>
) : (
<>
<Link
href="/auth/signin"
className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700"
>
Zaloguj się
</Link>
<Link
href="/auth/signup"
className="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700"
>
Zarejestruj się
</Link>
</>
)}
</div>
</div>
</header>
const totalComments = posts.reduce(
(sum, post) => sum + post._count.comments,
0
);

{/* Main Content */}
<main className="max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8">
{/* Welcome Section */}
<div className="bg-white rounded-lg shadow p-6 mb-8">
<h2 className="text-2xl font-bold mb-4">
{session ? `Witaj ponownie! 👋` : 'Witamy w LocalAid! 🎉'}
</h2>
<p className="text-gray-600 mb-4">
LocalAid to platforma łącząca sąsiadów, którzy potrzebują pomocy z tymi, którzy mogą jej udzielić.
Pożycz narzędzie, pomóż w zakupach, lub znajdź kogoś kto pomoże w transporcie.
return (
<div className="space-y-8">
{/* Welcome / hero + akcje */}
<section className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
<div>
<h1 className="text-2xl font-semibold text-slate-900">
{session ? "Witaj ponownie! 👋" : "Witamy w LocalAid! 🎉"}
</h1>
<p className="mt-2 text-sm text-slate-600 max-w-xl">
LocalAid to platforma łącząca sąsiadów, którzy potrzebują pomocy z
tymi, którzy mogą jej udzielić. Pożycz narzędzie, pomóż w zakupach,
albo znajdź kogoś, kto pomoże w transporcie.
</p>

{!session && (
<div className="bg-blue-50 border border-blue-200 rounded-md p-4">
<p className="text-sm font-semibold text-blue-900 mb-2">🧪 Demo - konta testowe:</p>
<p className="text-sm text-blue-800">Email: <code className="bg-white px-2 py-1 rounded">jan.kowalski@example.com</code></p>
<p className="text-sm text-blue-800">Hasło: <code className="bg-white px-2 py-1 rounded">password123</code></p>
<div className="mt-4 rounded-lg border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm text-indigo-900">
<p className="font-semibold mb-1">🧪 Konto demo:</p>
<p>
Email:{" "}
<code className="rounded bg-white px-2 py-1">
jan.kowalski@example.com
</code>
</p>
<p>
Hasło:{" "}
<code className="rounded bg-white px-2 py-1">
password123
</code>
</p>
</div>
)}
</div>

{/* Stats */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div className="bg-white rounded-lg shadow p-6 text-center">
<div className="text-3xl font-bold text-blue-600">{posts.length}</div>
<div className="text-gray-600">Aktywnych ogłoszeń</div>
</div>
<div className="bg-white rounded-lg shadow p-6 text-center">
<div className="text-3xl font-bold text-green-600">
{posts.reduce((sum, post) => sum + post._count.comments, 0)}
</div>
<div className="text-gray-600">Komentarzy</div>
<div className="flex flex-wrap gap-3">
{session ? (
<>
<form
action={async () => {
"use server";
await signOut();
}}
>
<button
type="submit"
className="inline-flex items-center justify-center rounded-lg bg-rose-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-rose-500 transition"
>
Wyloguj
</button>
</form>
</>
) : (
<>
<Link
href="/auth/signin"
className="inline-flex items-center justify-center rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-500 transition"
>
Zaloguj się
</Link>
<Link
href="/auth/signup"
className="inline-flex items-center justify-center rounded-lg border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-800 hover:bg-slate-50 transition"
>
Zarejestruj się
</Link>
</>
)}
</div>
</section>

{/* Statystyki */}
<section className="grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="rounded-xl border border-slate-200 bg-white p-5 text-center shadow-sm">
<div className="text-3xl font-bold text-indigo-600">
{posts.length}
</div>
<div className="bg-white rounded-lg shadow p-6 text-center">
<div className="text-3xl font-bold text-purple-600">3</div>
<div className="text-gray-600">Użytkowników</div>
<div className="mt-1 text-sm text-slate-600">Aktywnych ogłoszeń</div>
</div>
<div className="rounded-xl border border-slate-200 bg-white p-5 text-center shadow-sm">
<div className="text-3xl font-bold text-emerald-600">
{totalComments}
</div>
<div className="mt-1 text-sm text-slate-600">Komentarzy</div>
</div>
<div className="rounded-xl border border-slate-200 bg-white p-5 text-center shadow-sm">
<div className="text-3xl font-bold text-purple-600">3</div>
<div className="mt-1 text-sm text-slate-600">Użytkowników (demo)</div>
</div>
</section>

{/* Ostatnie ogłoszenia */}
<section className="rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
<div className="mb-4 flex items-center justify-between gap-3">
<h2 className="text-lg font-semibold text-slate-900">
📋 Najnowsze ogłoszenia
</h2>
<Link
href="/posts"
className="text-sm font-medium text-indigo-600 hover:text-indigo-500"
>
Zobacz wszystkie
</Link>
</div>

{/* Recent Posts */}
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-xl font-bold mb-4">📋 Najnowsze ogłoszenia</h3>
{posts.length === 0 ? (
<p className="text-sm text-slate-500">
Brak ogłoszeń. Dodaj pierwsze ogłoszenie, aby zacząć.
</p>
) : (
<div className="space-y-4">
{posts.map((post) => (
<div key={post.id} className="border-b pb-4 last:border-b-0">
<h4 className="font-semibold text-lg">{post.title}</h4>
<p className="text-gray-600 text-sm mb-2">{post.description}</p>
<div className="flex gap-4 text-xs text-gray-500">
<span>👤 {post.author.name}</span>
<div
key={post.id}
className="border-b border-slate-100 pb-4 last:border-b-0"
>
<h3 className="text-base font-semibold text-slate-900">
{post.title}
</h3>
<p className="mt-1 text-sm text-slate-600">
{post.description}
</p>
<div className="mt-2 flex flex-wrap gap-3 text-xs text-slate-500">
<span>👤 {post.author.name ?? post.author.email}</span>
<span>📁 {post.category}</span>
<span>💬 {post._count.comments} komentarzy</span>
<span>🕒 {new Date(post.createdAt).toLocaleDateString('pl-PL')}</span>
<span>
🕒{" "}
{new Date(post.createdAt).toLocaleDateString("pl-PL", {
day: "2-digit",
month: "2-digit",
year: "numeric",
})}
</span>
</div>
</div>
))}
</div>
</div>
)}
</section>

{/* Database Connection Test */}
<div className="mt-8 bg-green-50 border border-green-200 rounded-lg p-4">
<p className="text-sm font-semibold text-green-900">
✅ Baza danych działa poprawnie!
</p>
<p className="text-xs text-green-700 mt-1">
Połączenie z SQLite zostało nawiązane. Załadowano {posts.length} ogłoszenia.
</p>
</div>
</main>
{/* Info o bazie – jako mały banner */}
<section className="rounded-lg border border-emerald-100 bg-emerald-50 px-4 py-3 text-xs text-emerald-900">
<p className="font-semibold">✅ Baza danych działa poprawnie</p>
<p className="mt-1">
Połączenie z SQLite zostało nawiązane. Załadowano {posts.length}{" "}
ogłoszenia.
</p>
</section>
</div>
)
);
}
Loading