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
27 changes: 27 additions & 0 deletions frontend/app/friends/add/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="min-h-screen bg-slate-950">
<div className="max-w-2xl mx-auto px-4 py-6">
<Button
variant="ghost"
onClick={() => router.back()}
className="mb-6 text-white hover:bg-slate-800"
>
<ChevronLeft className="h-5 w-5 mr-2" />
Back
</Button>

<h1 className="text-2xl font-bold text-white mb-6">Add Friends</h1>
<p className="text-slate-400">Find and add friends to your network.</p>
</div>
</div>
)
}
159 changes: 159 additions & 0 deletions frontend/app/friends/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="min-h-screen bg-slate-950">
{/* Close Button - Top Right */}
<div className="fixed top-4 right-4 z-50">
<button className="p-2 hover:bg-slate-800 rounded transition-colors">
<X className="h-6 w-6 text-white" />
</button>
</div>

{/* Main Content */}
<div className="max-w-md mx-auto px-4 py-6">
{/* Header with Streak */}
<div className="flex items-center justify-between mb-8">
<div>
<p className="text-slate-400 text-sm mb-1">0 day streak!</p>
<h1 className="text-3xl font-bold text-white">Streak</h1>
</div>
<Flame className="h-20 w-20 text-yellow-400" />
</div>

{/* Streak Calendar */}
<div className="bg-slate-800 rounded-lg p-4 mb-8 border border-slate-700">
<StreakCalendar />
</div>

{/* Share Your Streak Section */}
<div className="mb-8">
<div className="text-center mb-4">
<div className="bg-yellow-400 text-slate-900 rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-3 font-bold text-lg">
4
</div>
<p className="text-white font-semibold mb-1">I'm on a</p>
<p className="text-white text-lg font-bold">4 day streak!</p>
<p className="text-slate-400 text-xs mt-2">mind block</p>
</div>

<div className="flex gap-4 justify-center mt-6">
<button className="flex flex-col items-center gap-1 text-slate-400 hover:text-white transition-colors">
<div className="w-10 h-10 bg-slate-700 rounded-lg flex items-center justify-center">
📱
</div>
<span className="text-xs">Contacts</span>
</button>
<button className="flex flex-col items-center gap-1 text-slate-400 hover:text-white transition-colors">
<div className="w-10 h-10 bg-slate-700 rounded-lg flex items-center justify-center">
✈️
</div>
<span className="text-xs">Telegram</span>
</button>
<button className="flex flex-col items-center gap-1 text-slate-400 hover:text-white transition-colors">
<div className="w-10 h-10 bg-slate-700 rounded-lg flex items-center justify-center">
𝕏
</div>
<span className="text-xs">Twitter</span>
</button>
<button className="flex flex-col items-center gap-1 text-slate-400 hover:text-white transition-colors">
<div className="w-10 h-10 bg-slate-700 rounded-lg flex items-center justify-center">
💬
</div>
<span className="text-xs">Whatsapp</span>
</button>
<button className="flex flex-col items-center gap-1 text-slate-400 hover:text-white transition-colors">
<div className="w-10 h-10 bg-slate-700 rounded-lg flex items-center justify-center">
</div>
<span className="text-xs">More</span>
</button>
</div>
</div>

{/* Tabs and Friends List */}
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<TabsList className="grid w-full grid-cols-2 bg-slate-800 mb-6">
<TabsTrigger value="following" className="data-[state=active]:bg-blue-600 data-[state=active]:text-white">Following</TabsTrigger>
<TabsTrigger value="followers" className="data-[state=active]:bg-blue-600 data-[state=active]:text-white">Followers</TabsTrigger>
</TabsList>

<TabsContent value="following" className="mt-0">
<div className="bg-slate-800 rounded-lg border border-slate-700 overflow-hidden">
{mockFollowing.map((friend) => (
<FriendListItem
key={friend.id}
username={friend.username}
avatar={friend.avatar}
avatarFallback={friend.avatarFallback}
onClick={() => handleFriendClick(friend.username)}
/>
))}
</div>
</TabsContent>

<TabsContent value="followers" className="mt-0">
<div className="bg-slate-800 rounded-lg border border-slate-700 overflow-hidden">
{mockFollowers.map((friend) => (
<FriendListItem
key={friend.id}
username={friend.username}
avatar={friend.avatar}
avatarFallback={friend.avatarFallback}
onClick={() => handleFriendClick(friend.username)}
/>
))}
</div>
</TabsContent>
</Tabs>

{/* Add Friends Action */}
<div className="mt-6">
<Button
onClick={handleAddFriends}
variant="outline"
className="w-full text-blue-400 border-blue-700 hover:bg-slate-700 bg-transparent"
>
<Plus className="h-5 w-5 mr-2" />
Add Friends +
</Button>
</div>
</div>
</div>
)
}
21 changes: 21 additions & 0 deletions frontend/components.json
Original file line number Diff line number Diff line change
@@ -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"
}
43 changes: 43 additions & 0 deletions frontend/components/friend-list-item.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<button
onClick={onClick}
className={cn(
'w-full flex items-center justify-between px-4 py-3 hover:bg-slate-700 transition-colors',
className
)}
>
<div className="flex items-center gap-3">
<Avatar className="h-10 w-10">
{avatar && <AvatarImage src={avatar || "/placeholder.svg"} alt={username} />}
<AvatarFallback className="bg-slate-700 text-white">{avatarFallback}</AvatarFallback>
</Avatar>
<span className="font-medium text-white">{username}</span>
</div>
<ChevronRight className="h-5 w-5 text-slate-500" />
</button>
<div className="border-b border-slate-700" />
</div>
)
}
105 changes: 105 additions & 0 deletions frontend/components/streak-calendar.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="w-full">
<h3 className="text-sm font-semibold text-white mb-4">Streak Calendar</h3>

{/* Month Navigation */}
<div className="flex items-center justify-between mb-4">
<button
onClick={handlePreviousMonth}
className="p-2 hover:bg-slate-700 rounded transition-colors"
>
<ChevronLeft className="h-5 w-5 text-slate-400" />
</button>

<div className="text-sm font-medium text-white">
{monthName} {year}
</div>

<button
onClick={handleNextMonth}
className="p-2 hover:bg-slate-700 rounded transition-colors"
>
<ChevronRight className="h-5 w-5 text-slate-400" />
</button>
</div>

{/* Day Headers */}
<div className="grid grid-cols-7 gap-2 mb-3">
{DAYS_OF_WEEK.map((day) => (
<div key={day} className="text-center text-xs font-medium text-slate-500 py-2">
{day}
</div>
))}
</div>

{/* Calendar Grid */}
<div className="grid grid-cols-7 gap-2">
{calendarDays.map((day, index) => (
<div key={index} className="aspect-square flex items-center justify-center">
{day === null ? (
<div className="w-8 h-8" />
) : (
<div
className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-medium transition-colors ${
isStreakDay(day)
? 'bg-yellow-400 text-slate-900'
: 'bg-slate-700 text-slate-300 hover:bg-slate-600'
}`}
>
{day}
</div>
)}
</div>
))}
</div>
</div>
)
}
Loading