From c8e1c3fa7aab88924ea6800f31a0a7174c5fc712 Mon Sep 17 00:00:00 2001 From: Divine Ifediorah Date: Sat, 31 Jan 2026 13:51:41 +0100 Subject: [PATCH] implementation of friends streak ui --- frontend/app/friends/add/page.tsx | 27 ++++ frontend/app/friends/page.tsx | 159 +++++++++++++++++++++++ frontend/components.json | 21 +++ frontend/components/friend-list-item.tsx | 43 ++++++ frontend/components/streak-calendar.tsx | 105 +++++++++++++++ 5 files changed, 355 insertions(+) create mode 100644 frontend/app/friends/add/page.tsx create mode 100644 frontend/app/friends/page.tsx create mode 100644 frontend/components.json create mode 100644 frontend/components/friend-list-item.tsx create mode 100644 frontend/components/streak-calendar.tsx diff --git a/frontend/app/friends/add/page.tsx b/frontend/app/friends/add/page.tsx new file mode 100644 index 0000000..cef6fa0 --- /dev/null +++ b/frontend/app/friends/add/page.tsx @@ -0,0 +1,27 @@ +'use client' + +import { useRouter } from 'next/navigation' +import { ChevronLeft } from 'lucide-react' +import { Button } from '@/components/ui/button' + +export default function AddFriendsPage() { + const router = useRouter() + + return ( +
+
+ + +

Add Friends

+

Find and add friends to your network.

+
+
+ ) +} diff --git a/frontend/app/friends/page.tsx b/frontend/app/friends/page.tsx new file mode 100644 index 0000000..7d7c38c --- /dev/null +++ b/frontend/app/friends/page.tsx @@ -0,0 +1,159 @@ +'use client' + +import { useState } from 'react' +import { useRouter } from 'next/navigation' +import { Flame, Plus, X } from 'lucide-react' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { FriendListItem } from '@/components/friend-list-item' +import { StreakCalendar } from '@/components/streak-calendar' +import Button from '@/components/Button' + + +// Mocked data for Following and Followers +const mockFollowing = [ + { id: '1', username: 'Aaron', avatar: '', avatarFallback: 'A' }, + { id: '2', username: 'Jordan', avatar: '', avatarFallback: 'J' }, + { id: '3', username: 'Casey', avatar: '', avatarFallback: 'C' }, + { id: '4', username: 'Morgan', avatar: '', avatarFallback: 'M' }, +] + +const mockFollowers = [ + { id: '1', username: 'Alex', avatar: '', avatarFallback: 'A' }, + { id: '2', username: 'Taylor', avatar: '', avatarFallback: 'T' }, + { id: '3', username: 'Riley', avatar: '', avatarFallback: 'R' }, +] + +export default function FriendsPage() { + const router = useRouter() + const [activeTab, setActiveTab] = useState('following') + + const handleAddFriends = () => { + router.push('/friends/add') + } + + const handleFriendClick = (username: string) => { + // Navigate to friend profile or handle friend interaction + console.log('Clicked friend:', username) + } + + return ( +
+ {/* Close Button - Top Right */} +
+ +
+ + {/* Main Content */} +
+ {/* Header with Streak */} +
+
+

0 day streak!

+

Streak

+
+ +
+ + {/* Streak Calendar */} +
+ +
+ + {/* Share Your Streak Section */} +
+
+
+ 4 +
+

I'm on a

+

4 day streak!

+

mind block

+
+ +
+ + + + + +
+
+ + {/* Tabs and Friends List */} + + + Following + Followers + + + +
+ {mockFollowing.map((friend) => ( + handleFriendClick(friend.username)} + /> + ))} +
+
+ + +
+ {mockFollowers.map((friend) => ( + handleFriendClick(friend.username)} + /> + ))} +
+
+
+ + {/* Add Friends Action */} +
+ +
+
+
+ ) +} diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 0000000..cfbafe2 --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/frontend/components/friend-list-item.tsx b/frontend/components/friend-list-item.tsx new file mode 100644 index 0000000..2519875 --- /dev/null +++ b/frontend/components/friend-list-item.tsx @@ -0,0 +1,43 @@ +'use client' + +import { ChevronRight } from 'lucide-react' +import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar' +import { cn } from '@/lib/utils' + +interface FriendListItemProps { + avatar?: string + avatarFallback?: string + username: string + onClick?: () => void + className?: string +} + +export function FriendListItem({ + avatar, + avatarFallback = '?', + username, + onClick, + className, +}: FriendListItemProps) { + return ( +
+ +
+
+ ) +} diff --git a/frontend/components/streak-calendar.tsx b/frontend/components/streak-calendar.tsx new file mode 100644 index 0000000..fecf169 --- /dev/null +++ b/frontend/components/streak-calendar.tsx @@ -0,0 +1,105 @@ +'use client' + +import { useState } from 'react' +import { ChevronLeft, ChevronRight } from 'lucide-react' + +const DAYS_OF_WEEK = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] + +interface StreakCalendarProps { + streakDays?: number[] +} + +export function StreakCalendar({ streakDays = [4, 5, 6, 7, 12, 13, 14, 15, 16, 17, 18, 20, 21, 25, 26, 27] }: StreakCalendarProps) { + const [currentDate, setCurrentDate] = useState(new Date(2022, 7, 1)) // August 2022 + + const getDaysInMonth = (date: Date) => { + return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate() + } + + const getFirstDayOfMonth = (date: Date) => { + return new Date(date.getFullYear(), date.getMonth(), 1).getDay() + } + + const monthName = currentDate.toLocaleString('default', { month: 'short' }).toUpperCase() + const year = currentDate.getFullYear() + const daysInMonth = getDaysInMonth(currentDate) + const firstDay = getFirstDayOfMonth(currentDate) + + // Create array of day numbers, accounting for empty cells at start + const calendarDays = [] + for (let i = 0; i < firstDay; i++) { + calendarDays.push(null) + } + for (let i = 1; i <= daysInMonth; i++) { + calendarDays.push(i) + } + + const handlePreviousMonth = () => { + setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)) + } + + const handleNextMonth = () => { + setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)) + } + + const isStreakDay = (day: number | null) => { + return day !== null && streakDays.includes(day) + } + + return ( +
+

Streak Calendar

+ + {/* Month Navigation */} +
+ + +
+ {monthName} {year} +
+ + +
+ + {/* Day Headers */} +
+ {DAYS_OF_WEEK.map((day) => ( +
+ {day} +
+ ))} +
+ + {/* Calendar Grid */} +
+ {calendarDays.map((day, index) => ( +
+ {day === null ? ( +
+ ) : ( +
+ {day} +
+ )} +
+ ))} +
+
+ ) +}