diff --git a/app/layout.tsx b/app/layout.tsx
index 9ceedf8..59e178d 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -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'] })
@@ -18,7 +19,9 @@ export default function RootLayout({
return (
- {children}
+
+ {children}
+
)
diff --git a/app/page.tsx b/app/page.tsx
index 221f09c..a152fbd 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -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(null)
- const [loading, setLoading] = useState(false)
+const fetchPotentialMatches = async (): Promise => {
+ 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 (
+
+
Please log in to find your matches.
+
+ )
+ }
+
+ return (
+
+
+ Welcome, {user?.name}!
+
+
+
+ )
+}
- 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 (
-
- Chordially Dev Home
+
+ )
+}
-
+export default function Home() {
+ const { data, isLoading, isError, error } = useQuery({
+ queryKey: ['potentialMatches'],
+ queryFn: fetchPotentialMatches,
+ })
+
+ return (
+
+
- {data && (
-
-
Mock Data Received:
-
+
+
Swipe Deck (Mock Data)
+ {isLoading &&
Finding users...
}
+ {isError &&
Error: {error.message}
}
+ {data && (
+
{JSON.stringify(data, null, 2)}
-
- )}
+ )}
+
)
}
diff --git a/app/query-provider.tsx b/app/query-provider.tsx
new file mode 100644
index 0000000..0f97694
--- /dev/null
+++ b/app/query-provider.tsx
@@ -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 (
+ {children}
+ )
+}
diff --git a/package-lock.json b/package-lock.json
index 197751f..a86ff37 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,9 +8,11 @@
"name": "chordially",
"version": "0.1.0",
"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",
@@ -1436,6 +1438,32 @@
"tailwindcss": "4.1.12"
}
},
+ "node_modules/@tanstack/query-core": {
+ "version": "5.85.5",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.85.5.tgz",
+ "integrity": "sha512-KO0WTob4JEApv69iYp1eGvfMSUkgw//IpMnq+//cORBzXf0smyRwPLrUvEe5qtAEGjwZTXrjxg+oJNP/C00t6w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.85.5",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.85.5.tgz",
+ "integrity": "sha512-/X4EFNcnPiSs8wM2v+b6DqS5mmGeuJQvxBglmDxl6ZQb5V26ouD2SJYAcC3VjbNwqhY2zjxVD15rDA5nGbMn3A==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.85.5"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz",
@@ -1489,7 +1517,7 @@
"version": "19.1.11",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.11.tgz",
"integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
@@ -2669,7 +2697,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/damerau-levenshtein": {
@@ -6825,6 +6853,35 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zustand": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz",
+ "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18.0.0",
+ "immer": ">=9.0.6",
+ "react": ">=18.0.0",
+ "use-sync-external-store": ">=1.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "use-sync-external-store": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index 03df915..cfd30c3 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/stores/auth-store.ts b/stores/auth-store.ts
new file mode 100644
index 0000000..15fae8a
--- /dev/null
+++ b/stores/auth-store.ts
@@ -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((set) => ({
+ user: null,
+ token: null,
+ isAuthenticated: false,
+
+ login: (user, token) =>
+ set({
+ user,
+ token,
+ isAuthenticated: true,
+ }),
+
+ logout: () =>
+ set({
+ user: null,
+ token: null,
+ isAuthenticated: false,
+ }),
+}))