diff --git a/frontend/app/components/BottomNav/BottomNav.tsx b/frontend/app/components/BottomNav/BottomNav.tsx
new file mode 100644
index 0000000..3114f0d
--- /dev/null
+++ b/frontend/app/components/BottomNav/BottomNav.tsx
@@ -0,0 +1,179 @@
+'use client';
+
+import Link from 'next/link';
+import { useEffect, useState } from 'react';
+import { usePathname } from 'next/navigation';
+
+const leftItems = [
+ { href: '/feed', label: 'Feed', icon: '🏠' },
+ { href: '/explore', label: 'Entdecken', icon: '🔍' },
+];
+
+const rightItems = [
+ { href: '/pets', label: 'Tiere', icon: '🐾' },
+ { href: '/profile', label: 'Profil', icon: '👤' },
+];
+
+function NavItem({
+ item,
+ pathname,
+}: {
+ item: { href: string; label: string; icon: string };
+ pathname: string;
+}) {
+ const isActive =
+ item.href === '/feed'
+ ? pathname === '/feed'
+ : pathname.startsWith(item.href);
+
+ return (
+ {
+ (e.currentTarget as HTMLElement).style.transform = 'scale(0.9)';
+}}
+onMouseUp={(e) => {
+ (e.currentTarget as HTMLElement).style.transform = 'scale(1)';
+}}
+onMouseLeave={(e) => {
+ (e.currentTarget as HTMLElement).style.transform = 'scale(1)';
+}}
+
+ >
+ {item.icon}
+ {item.label}
+ {isActive && (
+
+)}
+
+
+ );
+}
+
+export default function BottomNav() {
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
+ const pathname = usePathname();
+
+ useEffect(() => {
+ const hasAuthCookie = document.cookie
+ .split('; ')
+ .some((c) => c.startsWith('onlypets-auth=true'));
+
+ setIsLoggedIn(hasAuthCookie);
+ }, []);
+
+ if (!isLoggedIn) return null;
+
+ return (
+
+
+ );
+}
diff --git a/frontend/app/components/Navbar/Navbar.tsx b/frontend/app/components/Navbar/Navbar.tsx
new file mode 100644
index 0000000..5291177
--- /dev/null
+++ b/frontend/app/components/Navbar/Navbar.tsx
@@ -0,0 +1,47 @@
+'use client';
+
+import Link from 'next/link';
+import { useEffect, useState } from 'react';
+
+export default function Navbar() {
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
+
+ useEffect(() => {
+ const hasAuthCookie = document.cookie
+ .split('; ')
+ .some((c) => c.startsWith('onlypets-auth=true'));
+
+ setIsLoggedIn(hasAuthCookie);
+ }, []);
+
+ return (
+
+ );
+}
diff --git a/frontend/app/explore/page.tsx b/frontend/app/explore/page.tsx
index ec5338f..89fa045 100644
--- a/frontend/app/explore/page.tsx
+++ b/frontend/app/explore/page.tsx
@@ -1,3 +1,472 @@
+'use client';
+
+import { useRef, useState } from 'react';
+
+type ExplorePet = {
+ id: number;
+ name: string;
+ type: 'Hund' | 'Katze';
+};
+
+type ExplorePost = {
+ id: number;
+ petId: number;
+ petName: string;
+ petType: 'Hund' | 'Katze';
+ authorName: string;
+ createdAt: string;
+ text: string;
+ likesCount: number;
+ commentsCount: number;
+};
+
+const trendingPets: ExplorePet[] = [
+ { id: 1, name: 'Luna', type: 'Katze' },
+ { id: 2, name: 'Bello', type: 'Hund' },
+ { id: 3, name: 'Milo', type: 'Katze' },
+ { id: 4, name: 'Rocky', type: 'Hund' },
+ { id: 5, name: 'Nala', type: 'Katze' },
+ { id: 6, name: 'Leo', type: 'Hund' },
+];
+
+const latestPosts: ExplorePost[] = [
+ {
+ id: 101,
+ petId: 1,
+ petName: 'Luna',
+ petType: 'Katze',
+ authorName: 'Anna',
+ createdAt: 'vor 2 Stunden',
+ text: 'Luna chillt auf dem Sofa und beobachtet alles ganz genau.',
+ likesCount: 12,
+ commentsCount: 3,
+ },
+ {
+ id: 102,
+ petId: 2,
+ petName: 'Bello',
+ petType: 'Hund',
+ authorName: 'Markus',
+ createdAt: 'heute',
+ text: 'Bello hat sein neues Lieblingsspielzeug gefunden.',
+ likesCount: 25,
+ commentsCount: 8,
+ },
+ {
+ id: 103,
+ petId: 3,
+ petName: 'Milo',
+ petType: 'Katze',
+ authorName: 'Sophie',
+ createdAt: 'gestern',
+ text: 'Milo hat wieder einen Karton erobert.',
+ likesCount: 7,
+ commentsCount: 1,
+ },
+ {
+ id: 104,
+ petId: 4,
+ petName: 'Rocky',
+ petType: 'Hund',
+ authorName: 'Timo',
+ createdAt: 'gestern',
+ text: 'Rocky war im Wald unterwegs. 10/10 würde wieder rennen.',
+ likesCount: 19,
+ commentsCount: 4,
+ },
+];
+
export default function ExplorePage() {
- return
Entdecken
;
+ const [activeFilter, setActiveFilter] = useState<
+ 'ALLE' | 'HUND' | 'KATZE'
+ >('ALLE');
+
+ const [previewPost, setPreviewPost] = useState(null);
+const [openComments, setOpenComments] = useState(false);
+
+ // Swipe-Down Handling fürs Modal
+ const startY = useRef(null);
+
+ const filteredPosts = latestPosts.filter((p) => {
+ if (activeFilter === 'HUND') return p.petType === 'Hund';
+ if (activeFilter === 'KATZE') return p.petType === 'Katze';
+ return true;
+ });
+
+ return (
+ <>
+ {/* ================= HEADER / FILTER ================= */}
+
+
+
+
+ {[
+ { key: 'ALLE', label: 'Alle' },
+ { key: 'HUND', label: 'Hunde' },
+ { key: 'KATZE', label: 'Katzen' },
+ ].map((f) => {
+ const active = activeFilter === f.key;
+ return (
+
+ );
+ })}
+
+
+
+ {/* ================= BELIEBTE TIERE ================= */}
+
+
+ Beliebte Tiere
+
+
+
+ {trendingPets.map((pet) => (
+
+
+ 🐾
+
+
+
{pet.name}
+
{pet.type}
+
+
+ ))}
+
+
+
+ {/* ================= POSTS ================= */}
+
+
+ Neueste Beiträge
+
+
+
+ {filteredPosts.map((post) => (
+
+ {/* ✅ Nur dieser Bereich öffnet das Modal */}
+ setPreviewPost(post)}
+ style={{ cursor: 'pointer' }}
+ >
+
+
+
+
+
+ {post.petName}{' '}
+
+ · {post.petType} · {post.authorName}
+
+
+
+ {post.createdAt}
+
+
+
+
{post.text}
+
+
+
+ {/* ✅ Interaktionszeile (öffnet KEIN Modal) */}
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+ {/* ================= PREVIEW MODAL ================= */}
+ {previewPost && (
+ setPreviewPost(null)}
+ style={{
+ position: 'fixed',
+ inset: 0,
+ backgroundColor: 'rgba(0,0,0,0.4)',
+ zIndex: 50,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'flex-end',
+ }}
+ >
+
e.stopPropagation()}
+ onTouchStart={(e) => (startY.current = e.touches[0].clientY)}
+ onTouchEnd={(e) => {
+ if (
+ startY.current &&
+ e.changedTouches[0].clientY - startY.current > 80
+ ) {
+ setPreviewPost(null);
+setOpenComments(false);
+
+ }
+ }}
+ style={{
+ backgroundColor: 'white',
+ width: '100%',
+ maxWidth: '720px',
+ borderTopLeftRadius: '22px',
+ borderTopRightRadius: '22px',
+ padding: '1rem',
+ animation: 'slideUp 0.25s ease',
+ paddingBottom: '5rem',
+
+ }}
+ >
+
+
+
+
+
{previewPost.petName}{' '}
+
+ · {previewPost.petType} · {previewPost.authorName}
+
+
+
{previewPost.text}
+{openComments && (
+
+
Kommentare
+
+
+ Noch keine Kommentare.
+
+
+
+
+)}
+
+
+
+ ❤️ {previewPost.likesCount}
+ 💬 {previewPost.commentsCount}
+
+
+
+ Zum Beitrag
+
+
+
+
+ )}
+ >
+ );
}
diff --git a/frontend/app/globals.css b/frontend/app/globals.css
index e41a4ba..17bb253 100644
--- a/frontend/app/globals.css
+++ b/frontend/app/globals.css
@@ -46,4 +46,54 @@ body {
.navbar a.active {
background-color: rgba(255, 255, 255, 0.35);
}
+/* Hide horizontal scrollbar but keep scroll */
+.hide-scrollbar {
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE / Edge */
+}
+
+.hide-scrollbar::-webkit-scrollbar {
+ display: none; /* Chrome / Safari */
+}
+@keyframes skeleton {
+ 0% { background-position: 100% 0; }
+ 100% { background-position: 0 0; }
+}
+@keyframes heart-pop {
+ 0% {
+ transform: scale(1);
+ }
+ 40% {
+ transform: scale(1.25);
+ }
+ 70% {
+ transform: scale(0.95);
+ }
+ 100% {
+ transform: scale(1);
+ }
+}
+
+@keyframes comment-wiggle {
+ 0% {
+ transform: translateX(0);
+ }
+ 30% {
+ transform: translateX(-2px);
+ }
+ 60% {
+ transform: translateX(2px);
+ }
+ 100% {
+ transform: translateX(0);
+ }
+}
+@keyframes slideUp {
+ from {
+ transform: translateY(100%);
+ }
+ to {
+ transform: translateY(0);
+ }
+}
diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx
index f723f9b..bb87584 100644
--- a/frontend/app/layout.tsx
+++ b/frontend/app/layout.tsx
@@ -1,5 +1,7 @@
import './globals.css';
-import Navigation from './components/Navigation';
+/*import Navigation from './components/Navigation';*/
+import BottomNav from './components/BottomNav/BottomNav';
+
export const metadata = {
title: 'OnlyPets',
@@ -14,11 +16,13 @@ export default function RootLayout({
return (
-
-
- {children}
-
-
+
+ {children}
+
+
+
+
+