Skip to content
Merged
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
5 changes: 4 additions & 1 deletion 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 { Inter } from 'next/font/google'
import './globals.css'
import { MswProvider } from './msw-provider'
import { QueryProvider } from './query-provider'

const inter = Inter({ subsets: ['latin'] })

Expand All @@ -18,7 +19,9 @@ export default function RootLayout({
return (
<html lang='en'>
<body className={inter.className}>
<MswProvider>{children}</MswProvider>
<MswProvider>
<QueryProvider>{children}</QueryProvider>
</MswProvider>
</body>
</html>
)
Expand Down
106 changes: 78 additions & 28 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,95 @@
'use client'

import { useState } from 'react'
import type { UserProfile } from '@/lib/api-schema'
import { useQuery } from '@tanstack/react-query'
import type { PotentialMatchesResponse, UserProfile } from '@/lib/api-schema'
import { useAuthStore } from '@/stores/auth-store'

export default function Home() {
const [data, setData] = useState<UserProfile[] | null>(null)
const [loading, setLoading] = useState(false)
const fetchPotentialMatches = async (): Promise<UserProfile[]> => {
const res = await fetch('/api/users/potential-matches')
if (!res.ok) {
throw new Error('Network response was not ok')
}
const data: PotentialMatchesResponse = await res.json()
return data.users
}

function UserProfileHeader() {
const { user, isAuthenticated, logout } = useAuthStore()

if (!isAuthenticated) {
return (
<div className='text-center'>
<p>Please log in to find your matches.</p>
</div>
)
}

return (
<div className='text-center'>
<p className='text-xl'>
Welcome, <span className='font-bold'>{user?.name}!</span>
</p>
<button
onClick={logout}
className='mt-2 bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-3 rounded text-sm'
>
Logout
</button>
</div>
)
}

const fetchData = async () => {
setLoading(true)
function LoginButton() {
const { login, isAuthenticated } = useAuthStore()

const handleLogin = async () => {
try {
const res = await fetch('/api/users/potential-matches')
const json = await res.json()
setData(json.users)
const res = await fetch('/api/auth/login', { method: 'POST' })
const data = await res.json()
login(data.user, data.token)
} catch (error) {
console.error('Failed to fetch mock data:', error)
} finally {
setLoading(false)
console.error('Login failed:', error)
}
}

if (isAuthenticated) return null

return (
<main className='flex min-h-screen flex-col items-center p-24'>
<h1 className='text-4xl font-bold mb-8'>Chordially Dev Home</h1>
<button
onClick={handleLogin}
className='bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded'
>
Log In with Mock User
</button>
)
}

<button
onClick={fetchData}
disabled={loading}
className='bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded disabled:bg-gray-400'
>
{loading ? 'Loading...' : 'Fetch Mock User Data'}
</button>
export default function Home() {
const { data, isLoading, isError, error } = useQuery({
queryKey: ['potentialMatches'],
queryFn: fetchPotentialMatches,
})

return (
<main className='flex min-h-screen flex-col items-center p-12 md:p-24'>
<div className='w-full max-w-2xl text-center'>
<h1 className='text-4xl font-bold mb-4'>Chordially</h1>
<UserProfileHeader />
<div className='my-8'>
<LoginButton />
</div>
</div>

{data && (
<div className='mt-8 p-4 border rounded-lg w-full max-w-2xl bg-gray-50 dark:bg-gray-800'>
<h2 className='text-2xl font-semibold mb-4'>Mock Data Received:</h2>
<pre className='text-xs overflow-auto'>
<div className='mt-8 p-4 border rounded-lg w-full max-w-2xl bg-gray-50 dark:bg-gray-800'>
<h2 className='text-2xl font-semibold mb-4'>Swipe Deck (Mock Data)</h2>
{isLoading && <p>Finding users...</p>}
{isError && <p>Error: {error.message}</p>}
{data && (
<pre className='text-xs overflow-auto h-96'>
{JSON.stringify(data, null, 2)}
</pre>
</div>
)}
)}
</div>
</main>
)
}
12 changes: 12 additions & 0 deletions app/query-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use client'

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactNode, useState } from 'react'

export function QueryProvider({ children }: { children: ReactNode }) {
const [queryClient] = useState(() => new QueryClient())

return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)
}
63 changes: 60 additions & 3 deletions package-lock.json

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

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
"prepare": "husky"
},
"dependencies": {
"@tanstack/react-query": "^5.85.5",
"next": "15.5.0",
"react": "19.1.0",
"react-dom": "19.1.0"
"react-dom": "19.1.0",
"zustand": "^5.0.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
Expand Down
30 changes: 30 additions & 0 deletions stores/auth-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { create } from 'zustand'
import type { UserProfile } from '@/lib/api-schema'

interface AuthState {
user: UserProfile | null
token: string | null
isAuthenticated: boolean
login: (user: UserProfile, token: string) => void
logout: () => void
}

export const useAuthStore = create<AuthState>((set) => ({
user: null,
token: null,
isAuthenticated: false,

login: (user, token) =>
set({
user,
token,
isAuthenticated: true,
}),

logout: () =>
set({
user: null,
token: null,
isAuthenticated: false,
}),
}))