feat: refactor API base URL handling and update frontend components for improved data structure

This commit is contained in:
2026-06-20 02:26:37 +00:00
parent 9f2e85f60f
commit 112624fff9
8 changed files with 72 additions and 53 deletions
+4 -4
View File
@@ -3,10 +3,10 @@
# Copy this file to .env.local and fill in your values. # Copy this file to .env.local and fill in your values.
# .env.local is gitignored and never committed. # .env.local is gitignored and never committed.
# #
# VITE_CODESPACE_NAME is required when running inside a GitHub Codespace. # VITE_CODESPACE_NAME can be used to explicitly point at the backend API.
# It is used to build the backend API base URL: # When running inside a GitHub Codespace, the frontend will also auto-detect
# https://${VITE_CODESPACE_NAME}-8000.app.github.dev/api # 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 VITE_CODESPACE_NAME=your-codespace-name-here
@@ -1,9 +1,5 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { API_BASE } from '../lib/apiBase'
const codespaceName = import.meta.env.VITE_CODESPACE_NAME
const API_BASE = codespaceName
? `https://${codespaceName}-8000.app.github.dev/api`
: 'http://localhost:8000/api'
function Activities() { function Activities() {
const [activities, setActivities] = useState([]) const [activities, setActivities] = useState([])
@@ -17,7 +13,7 @@ function Activities() {
return res.json() return res.json()
}) })
.then((data) => { .then((data) => {
setActivities(Array.isArray(data) ? data : (data.results ?? [])) setActivities(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false) setLoading(false)
}) })
.catch((err) => { .catch((err) => {
@@ -36,18 +32,22 @@ function Activities() {
<thead> <thead>
<tr> <tr>
<th>User</th> <th>User</th>
<th>Team</th>
<th>Type</th> <th>Type</th>
<th>Duration (min)</th> <th>Duration (min)</th>
<th>Date</th> <th>Calories</th>
<th>Completed</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{activities.map((activity) => ( {activities.map((activity) => (
<tr key={activity._id ?? activity.id}> <tr key={activity._id ?? activity.id}>
<td>{activity.user}</td> <td>{activity.user?.username ?? activity.user}</td>
<td>{activity.activity_type}</td> <td>{activity.team?.name ?? activity.team}</td>
<td>{activity.duration}</td> <td>{activity.type}</td>
<td>{activity.date}</td> <td>{activity.durationMinutes}</td>
<td>{activity.caloriesBurned}</td>
<td>{activity.completedAt ? new Date(activity.completedAt).toLocaleString() : ''}</td>
</tr> </tr>
))} ))}
</tbody> </tbody>
@@ -1,12 +1,8 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { API_BASE } from '../lib/apiBase'
const codespaceName = import.meta.env.VITE_CODESPACE_NAME
const API_BASE = codespaceName
? `https://${codespaceName}-8000.app.github.dev/api`
: 'http://localhost:8000/api'
function Leaderboard() { function Leaderboard() {
const [entries, setEntries] = useState([]) const [leaderboards, setLeaderboards] = useState([])
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [error, setError] = useState(null) const [error, setError] = useState(null)
@@ -17,7 +13,7 @@ function Leaderboard() {
return res.json() return res.json()
}) })
.then((data) => { .then((data) => {
setEntries(Array.isArray(data) ? data : (data.results ?? [])) setLeaderboards(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false) setLoading(false)
}) })
.catch((err) => { .catch((err) => {
@@ -35,22 +31,26 @@ function Leaderboard() {
<table className="table table-striped"> <table className="table table-striped">
<thead> <thead>
<tr> <tr>
<th>Period</th>
<th>Rank</th> <th>Rank</th>
<th>User</th> <th>User</th>
<th>Score</th> <th>Score</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{entries.map((entry, index) => ( {leaderboards.flatMap((leaderboard) =>
<tr key={entry._id ?? entry.id}> leaderboard.entries.map((entry) => (
<td>{index + 1}</td> <tr key={`${leaderboard._id ?? leaderboard.id}-${entry.rank}-${entry.user?._id ?? entry.user?.id ?? entry.user}`}>
<td>{entry.user}</td> <td>{leaderboard.period}</td>
<td>{entry.score}</td> <td>{entry.rank}</td>
</tr> <td>{entry.user?.username ?? entry.user}</td>
))} <td>{entry.points}</td>
</tr>
)),
)}
</tbody> </tbody>
</table> </table>
{entries.length === 0 && <p>No leaderboard entries found.</p>} {leaderboards.length === 0 && <p>No leaderboard entries found.</p>}
</div> </div>
) )
} }
@@ -1,9 +1,5 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { API_BASE } from '../lib/apiBase'
const codespaceName = import.meta.env.VITE_CODESPACE_NAME
const API_BASE = codespaceName
? `https://${codespaceName}-8000.app.github.dev/api`
: 'http://localhost:8000/api'
function Teams() { function Teams() {
const [teams, setTeams] = useState([]) const [teams, setTeams] = useState([])
@@ -17,7 +13,7 @@ function Teams() {
return res.json() return res.json()
}) })
.then((data) => { .then((data) => {
setTeams(Array.isArray(data) ? data : (data.results ?? [])) setTeams(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false) setLoading(false)
}) })
.catch((err) => { .catch((err) => {
@@ -36,6 +32,7 @@ function Teams() {
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Captain</th>
<th>Members</th> <th>Members</th>
</tr> </tr>
</thead> </thead>
@@ -43,7 +40,12 @@ function Teams() {
{teams.map((team) => ( {teams.map((team) => (
<tr key={team._id ?? team.id}> <tr key={team._id ?? team.id}>
<td>{team.name}</td> <td>{team.name}</td>
<td>{Array.isArray(team.members) ? team.members.join(', ') : team.members}</td> <td>{team.captain?.username ?? team.captain}</td>
<td>
{Array.isArray(team.members)
? team.members.map((member) => member?.username ?? member).join(', ')
: team.members}
</td>
</tr> </tr>
))} ))}
</tbody> </tbody>
@@ -1,9 +1,5 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { API_BASE } from '../lib/apiBase'
const codespaceName = import.meta.env.VITE_CODESPACE_NAME
const API_BASE = codespaceName
? `https://${codespaceName}-8000.app.github.dev/api`
: 'http://localhost:8000/api'
function Users() { function Users() {
const [users, setUsers] = useState([]) const [users, setUsers] = useState([])
@@ -17,7 +13,7 @@ function Users() {
return res.json() return res.json()
}) })
.then((data) => { .then((data) => {
setUsers(Array.isArray(data) ? data : (data.results ?? [])) setUsers(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false) setLoading(false)
}) })
.catch((err) => { .catch((err) => {
@@ -1,9 +1,5 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { API_BASE } from '../lib/apiBase'
const codespaceName = import.meta.env.VITE_CODESPACE_NAME
const API_BASE = codespaceName
? `https://${codespaceName}-8000.app.github.dev/api`
: 'http://localhost:8000/api'
function Workouts() { function Workouts() {
const [workouts, setWorkouts] = useState([]) const [workouts, setWorkouts] = useState([])
@@ -17,7 +13,7 @@ function Workouts() {
return res.json() return res.json()
}) })
.then((data) => { .then((data) => {
setWorkouts(Array.isArray(data) ? data : (data.results ?? [])) setWorkouts(Array.isArray(data) ? data : (data.items ?? data.results ?? []))
setLoading(false) setLoading(false)
}) })
.catch((err) => { .catch((err) => {
@@ -35,15 +31,19 @@ function Workouts() {
<table className="table table-striped"> <table className="table table-striped">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Title</th>
<th>Description</th> <th>Category</th>
<th>Difficulty</th>
<th>Duration (min)</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{workouts.map((workout) => ( {workouts.map((workout) => (
<tr key={workout._id ?? workout.id}> <tr key={workout._id ?? workout.id}>
<td>{workout.name}</td> <td>{workout.title}</td>
<td>{workout.description}</td> <td>{workout.category}</td>
<td>{workout.difficulty}</td>
<td>{workout.durationMinutes}</td>
</tr> </tr>
))} ))}
</tbody> </tbody>
@@ -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'
+6
View File
@@ -5,6 +5,12 @@ import react from '@vitejs/plugin-react'
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
server: { server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
},
},
port: 5173, port: 5173,
strictPort: true, strictPort: true,
}, },