diff --git a/client/index.html b/client/index.html index 56f3d1f..747262d 100644 --- a/client/index.html +++ b/client/index.html @@ -1,13 +1,16 @@ - + Catstagram + + +
- + \ No newline at end of file diff --git a/client/src/components/CatCard.tsx b/client/src/components/CatCard.tsx index 6062c3a..8d5d0e3 100644 --- a/client/src/components/CatCard.tsx +++ b/client/src/components/CatCard.tsx @@ -4,7 +4,7 @@ import { useAuth } from '@/hooks/useAuth'; import { useLikeCat, useUnlikeCat } from '@/hooks/useLikes'; import { useToast } from '@/components/Toast'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; -import { Heart } from 'lucide-react'; +import { Heart, MessageCircle, Trophy } from 'lucide-react'; interface CatCardProps { cat: Cat; @@ -20,17 +20,18 @@ export default function CatCard({ cat, onOpen }: CatCardProps) { const unlikeMutation = useUnlikeCat(cat.id); const isOwner = user && cat.user_id === user.id; - const toggleLike = async () => { + const toggleLike = async (e: React.MouseEvent) => { + e.stopPropagation(); if (isOwner || !user) return; if (liked) { - setLiked(false); setLikeCount((c) => c - 1); + setLiked(false); setLikeCount(c => c - 1); try { await unlikeMutation.mutateAsync(); } - catch { setLiked(true); setLikeCount((c) => c + 1); } + catch { setLiked(true); setLikeCount(c => c + 1); } } else { - setLiked(true); setLikeCount((c) => c + 1); + setLiked(true); setLikeCount(c => c + 1); toast('❤️'); try { await likeMutation.mutateAsync(); } - catch { setLiked(false); setLikeCount((c) => c - 1); } + catch { setLiked(false); setLikeCount(c => c - 1); } } }; @@ -39,24 +40,21 @@ export default function CatCard({ cat, onOpen }: CatCardProps) { }); return ( -
-
-
onOpen(cat.id)} - > - - +
+
onOpen(cat.id)}> +
+ + {cat.username.charAt(0).toUpperCase()} -
-

@{cat.username}

+
+

@{cat.username}

-
onOpen(cat.id)}> +
{cat.caption
- {cat.caption && ( -
onOpen(cat.id)}> -

- @{cat.username} +

+ {cat.caption && ( +

+ @{cat.username} {cat.caption}

-
- )} + )} -
-
- + > + + {likeCount > 0 && {likeCount}} + - + +
+ + + + {cat.user_points} +
- - - 🏆 - {cat.user_points} -
diff --git a/client/src/components/CatModal.tsx b/client/src/components/CatModal.tsx index dfdb12c..3fe1773 100644 --- a/client/src/components/CatModal.tsx +++ b/client/src/components/CatModal.tsx @@ -4,7 +4,7 @@ import { useLikeStatus, useLikeCat, useUnlikeCat } from '@/hooks/useLikes'; import { useAuth } from '@/hooks/useAuth'; import { useToast } from '@/components/Toast'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; -import { X, Heart, Trash2 } from 'lucide-react'; +import { X, Heart, Trash2, Trophy } from 'lucide-react'; interface CatModalProps { catId: number; @@ -39,14 +39,14 @@ export default function CatModal({ catId, onClose }: CatModalProps) { const toggleLike = async () => { if (isOwner || !user || !cat) return; if (liked) { - setLiked(false); setLikeCount((c) => c - 1); + setLiked(false); setLikeCount(c => c - 1); try { await unlikeMutation.mutateAsync(); refetchLike(); } - catch { setLiked(true); setLikeCount((c) => c + 1); } + catch { setLiked(true); setLikeCount(c => c + 1); } } else { - setLiked(true); setLikeCount((c) => c + 1); + setLiked(true); setLikeCount(c => c + 1); toast('❤️'); try { await likeMutation.mutateAsync(); refetchLike(); } - catch { setLiked(false); setLikeCount((c) => c - 1); } + catch { setLiked(false); setLikeCount(c => c - 1); } } }; @@ -58,9 +58,9 @@ export default function CatModal({ catId, onClose }: CatModalProps) { if (isLoading || !cat) { return ( -
-
-
+
+
+
Загрузка...
@@ -72,15 +72,17 @@ export default function CatModal({ catId, onClose }: CatModalProps) { }); return ( -
+
e.stopPropagation()} + className="w-full sm:max-w-lg bg-white sm:rounded-2xl max-h-[92vh] flex flex-col overflow-hidden shadow-xl animate-slide-up sm:animate-scale-in" + onClick={e => e.stopPropagation()} > -
+
- - {cat.username.charAt(0).toUpperCase()} + + + {cat.username.charAt(0).toUpperCase()} +
@{cat.username} @@ -89,20 +91,20 @@ export default function CatModal({ catId, onClose }: CatModalProps) {
{isOwner && ( - )} -
-
+
{!imageLoaded && (
-
+
)}
- - {cat.username.charAt(0).toUpperCase()} + + + {cat.username.charAt(0).toUpperCase()} + -
-

- @{cat.username} - {cat.caption || 'Без подписи'} -

-
+

+ @{cat.username} + {cat.caption || 'Без подписи'} +

@@ -133,16 +135,16 @@ export default function CatModal({ catId, onClose }: CatModalProps) { onClick={toggleLike} disabled={isOwner || !user} className={`flex items-center gap-1.5 text-sm font-medium transition-all ${ - isOwner ? 'text-[var(--fg-tertiary)] cursor-not-allowed' : liked ? 'text-[var(--danger)]' : 'text-[var(--fg-secondary)] hover:text-[var(--fg)]' + isOwner ? 'text-[var(--fg-tertiary)] cursor-not-allowed' : liked ? 'text-red-500' : 'text-[var(--fg-secondary)] hover:text-red-400' }`} > - - {likeCount > 0 && {likeCount}} + + {likeCount > 0 && {likeCount}} - - 🏆 - {cat.user_points} - баллов + + + {cat.user_points} + баллов
diff --git a/client/src/components/FeedSidebar.tsx b/client/src/components/FeedSidebar.tsx index 8132b7a..d1a328b 100644 --- a/client/src/components/FeedSidebar.tsx +++ b/client/src/components/FeedSidebar.tsx @@ -2,14 +2,13 @@ import { useAuth } from '@/hooks/useAuth'; import { useCats } from '@/hooks/useCats'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; import { Link } from 'react-router-dom'; +import { Trophy, Crown, Medal } from 'lucide-react'; -function Medal({ rank }: { rank: number }) { - const colors = ['bg-blue-100 text-blue-700', 'bg-stone-100 text-stone-500', 'bg-amber-100 text-amber-700']; - return ( - - {rank + 1} - - ); +function RankBadge({ rank }: { rank: number }) { + if (rank === 0) return ; + if (rank === 1) return ; + if (rank === 2) return ; + return {rank + 1}; } export default function FeedSidebar() { @@ -29,45 +28,49 @@ export default function FeedSidebar() { .slice(0, 5); return ( -
+
{user && ( - - - + + + {user.username.charAt(0).toUpperCase()}
-

{user.username}

-

🏆 {user.points} баллов

+

{user.username}

+

+ {user.points} баллов +

)} -
-

- 🏆 Лидеры +
+

+ Лидеры

{topUsers.length === 0 ? ( -

Пока нет пользователей

+

Пока никого нет

) : ( -
+
{topUsers.map(([id, u], i) => ( - - - - {u.username.charAt(0).toUpperCase()} + + + + + {u.username.charAt(0).toUpperCase()} + - {u.username} - {u.points} + {u.username} + {u.points} ))}
)}
-

Catstagram · сообщество котоводов

+

Catstagram · сообщество котоводов

); } \ No newline at end of file diff --git a/client/src/components/Navbar.tsx b/client/src/components/Navbar.tsx index ca89250..851d5a5 100644 --- a/client/src/components/Navbar.tsx +++ b/client/src/components/Navbar.tsx @@ -1,7 +1,7 @@ import { Link, useLocation } from 'react-router-dom'; import { useAuth } from '@/hooks/useAuth'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; -import { Shield } from 'lucide-react'; +import { Home, PlusCircle, User, Shield } from 'lucide-react'; export default function Navbar() { const { user } = useAuth(); @@ -10,25 +10,29 @@ export default function Navbar() { if (!user) return null; return ( -