diff --git a/octofit-tracker/frontend/.env.local.example b/octofit-tracker/frontend/.env.local.example
index 33d9170..1b68758 100644
--- a/octofit-tracker/frontend/.env.local.example
+++ b/octofit-tracker/frontend/.env.local.example
@@ -3,10 +3,10 @@
# Copy this file to .env.local and fill in your values.
# .env.local is gitignored and never committed.
#
-# VITE_CODESPACE_NAME is required when running inside a GitHub Codespace.
-# It is used to build the backend API base URL:
-# https://${VITE_CODESPACE_NAME}-8000.app.github.dev/api
+# VITE_CODESPACE_NAME can be used to explicitly point at the backend API.
+# When running inside a GitHub Codespace, the frontend will also auto-detect
+# the backend host from the current app.github.dev URL.
#
-# If unset, the frontend falls back to http://localhost:8000/api.
+# If unset outside Codespaces, the frontend falls back to http://localhost:8000/api.
VITE_CODESPACE_NAME=your-codespace-name-here
diff --git a/octofit-tracker/frontend/src/components/Activities.jsx b/octofit-tracker/frontend/src/components/Activities.jsx
index 64fdb11..fcb390e 100644
--- a/octofit-tracker/frontend/src/components/Activities.jsx
+++ b/octofit-tracker/frontend/src/components/Activities.jsx
@@ -1,9 +1,5 @@
import { useEffect, useState } from 'react'
-
-const codespaceName = import.meta.env.VITE_CODESPACE_NAME
-const API_BASE = codespaceName
- ? `https://${codespaceName}-8000.app.github.dev/api`
- : 'http://localhost:8000/api'
+import { API_BASE } from '../lib/apiBase'
function Activities() {
const [activities, setActivities] = useState([])
@@ -17,7 +13,7 @@ function Activities() {
return res.json()
})
.then((data) => {
- setActivities(Array.isArray(data) ? data : (data.results ?? []))
+ setActivities(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false)
})
.catch((err) => {
@@ -36,18 +32,22 @@ function Activities() {
| User |
+ Team |
Type |
Duration (min) |
- Date |
+ Calories |
+ Completed |
{activities.map((activity) => (
- | {activity.user} |
- {activity.activity_type} |
- {activity.duration} |
- {activity.date} |
+ {activity.user?.username ?? activity.user} |
+ {activity.team?.name ?? activity.team} |
+ {activity.type} |
+ {activity.durationMinutes} |
+ {activity.caloriesBurned} |
+ {activity.completedAt ? new Date(activity.completedAt).toLocaleString() : ''} |
))}
diff --git a/octofit-tracker/frontend/src/components/Leaderboard.jsx b/octofit-tracker/frontend/src/components/Leaderboard.jsx
index 431b91f..342378f 100644
--- a/octofit-tracker/frontend/src/components/Leaderboard.jsx
+++ b/octofit-tracker/frontend/src/components/Leaderboard.jsx
@@ -1,12 +1,8 @@
import { useEffect, useState } from 'react'
-
-const codespaceName = import.meta.env.VITE_CODESPACE_NAME
-const API_BASE = codespaceName
- ? `https://${codespaceName}-8000.app.github.dev/api`
- : 'http://localhost:8000/api'
+import { API_BASE } from '../lib/apiBase'
function Leaderboard() {
- const [entries, setEntries] = useState([])
+ const [leaderboards, setLeaderboards] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
@@ -17,7 +13,7 @@ function Leaderboard() {
return res.json()
})
.then((data) => {
- setEntries(Array.isArray(data) ? data : (data.results ?? []))
+ setLeaderboards(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false)
})
.catch((err) => {
@@ -35,22 +31,26 @@ function Leaderboard() {
+ | Period |
Rank |
User |
Score |
- {entries.map((entry, index) => (
-
- | {index + 1} |
- {entry.user} |
- {entry.score} |
-
- ))}
+ {leaderboards.flatMap((leaderboard) =>
+ leaderboard.entries.map((entry) => (
+
+ | {leaderboard.period} |
+ {entry.rank} |
+ {entry.user?.username ?? entry.user} |
+ {entry.points} |
+
+ )),
+ )}
- {entries.length === 0 && No leaderboard entries found.
}
+ {leaderboards.length === 0 && No leaderboard entries found.
}
)
}
diff --git a/octofit-tracker/frontend/src/components/Teams.jsx b/octofit-tracker/frontend/src/components/Teams.jsx
index 6f27d63..35de16f 100644
--- a/octofit-tracker/frontend/src/components/Teams.jsx
+++ b/octofit-tracker/frontend/src/components/Teams.jsx
@@ -1,9 +1,5 @@
import { useEffect, useState } from 'react'
-
-const codespaceName = import.meta.env.VITE_CODESPACE_NAME
-const API_BASE = codespaceName
- ? `https://${codespaceName}-8000.app.github.dev/api`
- : 'http://localhost:8000/api'
+import { API_BASE } from '../lib/apiBase'
function Teams() {
const [teams, setTeams] = useState([])
@@ -17,7 +13,7 @@ function Teams() {
return res.json()
})
.then((data) => {
- setTeams(Array.isArray(data) ? data : (data.results ?? []))
+ setTeams(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false)
})
.catch((err) => {
@@ -36,6 +32,7 @@ function Teams() {
| Name |
+ Captain |
Members |
@@ -43,7 +40,12 @@ function Teams() {
{teams.map((team) => (
| {team.name} |
- {Array.isArray(team.members) ? team.members.join(', ') : team.members} |
+ {team.captain?.username ?? team.captain} |
+
+ {Array.isArray(team.members)
+ ? team.members.map((member) => member?.username ?? member).join(', ')
+ : team.members}
+ |
))}
diff --git a/octofit-tracker/frontend/src/components/Users.jsx b/octofit-tracker/frontend/src/components/Users.jsx
index d429790..c3e7a41 100644
--- a/octofit-tracker/frontend/src/components/Users.jsx
+++ b/octofit-tracker/frontend/src/components/Users.jsx
@@ -1,9 +1,5 @@
import { useEffect, useState } from 'react'
-
-const codespaceName = import.meta.env.VITE_CODESPACE_NAME
-const API_BASE = codespaceName
- ? `https://${codespaceName}-8000.app.github.dev/api`
- : 'http://localhost:8000/api'
+import { API_BASE } from '../lib/apiBase'
function Users() {
const [users, setUsers] = useState([])
@@ -17,7 +13,7 @@ function Users() {
return res.json()
})
.then((data) => {
- setUsers(Array.isArray(data) ? data : (data.results ?? []))
+ setUsers(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false)
})
.catch((err) => {
diff --git a/octofit-tracker/frontend/src/components/Workouts.jsx b/octofit-tracker/frontend/src/components/Workouts.jsx
index 8aacc78..cfb4c1a 100644
--- a/octofit-tracker/frontend/src/components/Workouts.jsx
+++ b/octofit-tracker/frontend/src/components/Workouts.jsx
@@ -1,9 +1,5 @@
import { useEffect, useState } from 'react'
-
-const codespaceName = import.meta.env.VITE_CODESPACE_NAME
-const API_BASE = codespaceName
- ? `https://${codespaceName}-8000.app.github.dev/api`
- : 'http://localhost:8000/api'
+import { API_BASE } from '../lib/apiBase'
function Workouts() {
const [workouts, setWorkouts] = useState([])
@@ -17,7 +13,7 @@ function Workouts() {
return res.json()
})
.then((data) => {
- setWorkouts(Array.isArray(data) ? data : (data.results ?? []))
+ setWorkouts(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false)
})
.catch((err) => {
@@ -35,15 +31,19 @@ function Workouts() {
- | Name |
- Description |
+ Title |
+ Category |
+ Difficulty |
+ Duration (min) |
{workouts.map((workout) => (
- | {workout.name} |
- {workout.description} |
+ {workout.title} |
+ {workout.category} |
+ {workout.difficulty} |
+ {workout.durationMinutes} |
))}
diff --git a/octofit-tracker/frontend/src/lib/apiBase.js b/octofit-tracker/frontend/src/lib/apiBase.js
new file mode 100644
index 0000000..f2afe58
--- /dev/null
+++ b/octofit-tracker/frontend/src/lib/apiBase.js
@@ -0,0 +1,15 @@
+const codespaceName = import.meta.env.VITE_CODESPACE_NAME
+const browserHost = typeof window !== 'undefined' ? window.location.hostname : ''
+const isDevelopment = import.meta.env.DEV
+
+const derivedCodespaceHost = browserHost.endsWith('.app.github.dev')
+ ? browserHost.replace(/-\d+\.app\.github\.dev$/, '-8000.app.github.dev')
+ : ''
+
+export const API_BASE = isDevelopment
+ ? '/api'
+ : codespaceName
+ ? `https://${codespaceName}-8000.app.github.dev/api`
+ : derivedCodespaceHost
+ ? `https://${derivedCodespaceHost}/api`
+ : 'http://localhost:8000/api'
\ No newline at end of file
diff --git a/octofit-tracker/frontend/vite.config.js b/octofit-tracker/frontend/vite.config.js
index d3668a3..da3c1f6 100644
--- a/octofit-tracker/frontend/vite.config.js
+++ b/octofit-tracker/frontend/vite.config.js
@@ -5,6 +5,12 @@ import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
+ proxy: {
+ '/api': {
+ target: 'http://127.0.0.1:8000',
+ changeOrigin: true,
+ },
+ },
port: 5173,
strictPort: true,
},