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.
# .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
@@ -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() {
<thead>
<tr>
<th>User</th>
<th>Team</th>
<th>Type</th>
<th>Duration (min)</th>
<th>Date</th>
<th>Calories</th>
<th>Completed</th>
</tr>
</thead>
<tbody>
{activities.map((activity) => (
<tr key={activity._id ?? activity.id}>
<td>{activity.user}</td>
<td>{activity.activity_type}</td>
<td>{activity.duration}</td>
<td>{activity.date}</td>
<td>{activity.user?.username ?? activity.user}</td>
<td>{activity.team?.name ?? activity.team}</td>
<td>{activity.type}</td>
<td>{activity.durationMinutes}</td>
<td>{activity.caloriesBurned}</td>
<td>{activity.completedAt ? new Date(activity.completedAt).toLocaleString() : ''}</td>
</tr>
))}
</tbody>
@@ -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() {
<table className="table table-striped">
<thead>
<tr>
<th>Period</th>
<th>Rank</th>
<th>User</th>
<th>Score</th>
</tr>
</thead>
<tbody>
{entries.map((entry, index) => (
<tr key={entry._id ?? entry.id}>
<td>{index + 1}</td>
<td>{entry.user}</td>
<td>{entry.score}</td>
</tr>
))}
{leaderboards.flatMap((leaderboard) =>
leaderboard.entries.map((entry) => (
<tr key={`${leaderboard._id ?? leaderboard.id}-${entry.rank}-${entry.user?._id ?? entry.user?.id ?? entry.user}`}>
<td>{leaderboard.period}</td>
<td>{entry.rank}</td>
<td>{entry.user?.username ?? entry.user}</td>
<td>{entry.points}</td>
</tr>
)),
)}
</tbody>
</table>
{entries.length === 0 && <p>No leaderboard entries found.</p>}
{leaderboards.length === 0 && <p>No leaderboard entries found.</p>}
</div>
)
}
@@ -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() {
<thead>
<tr>
<th>Name</th>
<th>Captain</th>
<th>Members</th>
</tr>
</thead>
@@ -43,7 +40,12 @@ function Teams() {
{teams.map((team) => (
<tr key={team._id ?? team.id}>
<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>
))}
</tbody>
@@ -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) => {
@@ -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() {
<table className="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Title</th>
<th>Category</th>
<th>Difficulty</th>
<th>Duration (min)</th>
</tr>
</thead>
<tbody>
{workouts.map((workout) => (
<tr key={workout._id ?? workout.id}>
<td>{workout.name}</td>
<td>{workout.description}</td>
<td>{workout.title}</td>
<td>{workout.category}</td>
<td>{workout.difficulty}</td>
<td>{workout.durationMinutes}</td>
</tr>
))}
</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({
plugins: [react()],
server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
},
},
port: 5173,
strictPort: true,
},