diff --git a/octofit-tracker/frontend/.env.local.example b/octofit-tracker/frontend/.env.local.example new file mode 100644 index 0000000..33d9170 --- /dev/null +++ b/octofit-tracker/frontend/.env.local.example @@ -0,0 +1,12 @@ +# Vite environment variables for OctoFit Tracker +# +# 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 +# +# If unset, the frontend falls back to http://localhost:8000/api. + +VITE_CODESPACE_NAME=your-codespace-name-here diff --git a/octofit-tracker/frontend/src/App.jsx b/octofit-tracker/frontend/src/App.jsx index 4f03aa1..245e5e9 100644 --- a/octofit-tracker/frontend/src/App.jsx +++ b/octofit-tracker/frontend/src/App.jsx @@ -1,121 +1,41 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' -import heroImg from './assets/hero.png' -import './App.css' +import { BrowserRouter, Routes, Route, NavLink } from 'react-router-dom' +import Activities from './components/Activities' +import Leaderboard from './components/Leaderboard' +import Teams from './components/Teams' +import Users from './components/Users' +import Workouts from './components/Workouts' function App() { - const [count, setCount] = useState(0) - return ( - <> -
-
- - React logo - Vite logo + +
- -
- -
-
- -

Documentation

-

Your questions, answered

- -
-
- -

Connect with us

-

Join the Vite community

- -
-
- -
-
- + +
+ + } /> + } /> + } /> + } /> + } /> + +

Welcome to OctoFit Tracker

+

Use the navigation above to explore activities, leaderboard, teams, users, and workouts.

+
+ } /> + + + ) } diff --git a/octofit-tracker/frontend/src/components/Activities.jsx b/octofit-tracker/frontend/src/components/Activities.jsx new file mode 100644 index 0000000..64fdb11 --- /dev/null +++ b/octofit-tracker/frontend/src/components/Activities.jsx @@ -0,0 +1,60 @@ +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' + +function Activities() { + const [activities, setActivities] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + fetch(`${API_BASE}/activities/`) + .then((res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() + }) + .then((data) => { + setActivities(Array.isArray(data) ? data : (data.results ?? [])) + setLoading(false) + }) + .catch((err) => { + setError(err.message) + setLoading(false) + }) + }, []) + + if (loading) return

Loading activities…

+ if (error) return

Error: {error}

+ + return ( +
+

Activities

+ + + + + + + + + + + {activities.map((activity) => ( + + + + + + + ))} + +
UserTypeDuration (min)Date
{activity.user}{activity.activity_type}{activity.duration}{activity.date}
+ {activities.length === 0 &&

No activities found.

} +
+ ) +} + +export default Activities diff --git a/octofit-tracker/frontend/src/components/Leaderboard.jsx b/octofit-tracker/frontend/src/components/Leaderboard.jsx new file mode 100644 index 0000000..431b91f --- /dev/null +++ b/octofit-tracker/frontend/src/components/Leaderboard.jsx @@ -0,0 +1,58 @@ +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' + +function Leaderboard() { + const [entries, setEntries] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + fetch(`${API_BASE}/leaderboard/`) + .then((res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() + }) + .then((data) => { + setEntries(Array.isArray(data) ? data : (data.results ?? [])) + setLoading(false) + }) + .catch((err) => { + setError(err.message) + setLoading(false) + }) + }, []) + + if (loading) return

Loading leaderboard…

+ if (error) return

Error: {error}

+ + return ( +
+

Leaderboard

+ + + + + + + + + + {entries.map((entry, index) => ( + + + + + + ))} + +
RankUserScore
{index + 1}{entry.user}{entry.score}
+ {entries.length === 0 &&

No leaderboard entries found.

} +
+ ) +} + +export default Leaderboard diff --git a/octofit-tracker/frontend/src/components/Teams.jsx b/octofit-tracker/frontend/src/components/Teams.jsx new file mode 100644 index 0000000..6f27d63 --- /dev/null +++ b/octofit-tracker/frontend/src/components/Teams.jsx @@ -0,0 +1,56 @@ +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' + +function Teams() { + const [teams, setTeams] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + fetch(`${API_BASE}/teams/`) + .then((res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() + }) + .then((data) => { + setTeams(Array.isArray(data) ? data : (data.results ?? [])) + setLoading(false) + }) + .catch((err) => { + setError(err.message) + setLoading(false) + }) + }, []) + + if (loading) return

Loading teams…

+ if (error) return

Error: {error}

+ + return ( +
+

Teams

+ + + + + + + + + {teams.map((team) => ( + + + + + ))} + +
NameMembers
{team.name}{Array.isArray(team.members) ? team.members.join(', ') : team.members}
+ {teams.length === 0 &&

No teams found.

} +
+ ) +} + +export default Teams diff --git a/octofit-tracker/frontend/src/components/Users.jsx b/octofit-tracker/frontend/src/components/Users.jsx new file mode 100644 index 0000000..d429790 --- /dev/null +++ b/octofit-tracker/frontend/src/components/Users.jsx @@ -0,0 +1,56 @@ +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' + +function Users() { + const [users, setUsers] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + fetch(`${API_BASE}/users/`) + .then((res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() + }) + .then((data) => { + setUsers(Array.isArray(data) ? data : (data.results ?? [])) + setLoading(false) + }) + .catch((err) => { + setError(err.message) + setLoading(false) + }) + }, []) + + if (loading) return

Loading users…

+ if (error) return

Error: {error}

+ + return ( +
+

Users

+ + + + + + + + + {users.map((user) => ( + + + + + ))} + +
UsernameEmail
{user.username}{user.email}
+ {users.length === 0 &&

No users found.

} +
+ ) +} + +export default Users diff --git a/octofit-tracker/frontend/src/components/Workouts.jsx b/octofit-tracker/frontend/src/components/Workouts.jsx new file mode 100644 index 0000000..8aacc78 --- /dev/null +++ b/octofit-tracker/frontend/src/components/Workouts.jsx @@ -0,0 +1,56 @@ +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' + +function Workouts() { + const [workouts, setWorkouts] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + fetch(`${API_BASE}/workouts/`) + .then((res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() + }) + .then((data) => { + setWorkouts(Array.isArray(data) ? data : (data.results ?? [])) + setLoading(false) + }) + .catch((err) => { + setError(err.message) + setLoading(false) + }) + }, []) + + if (loading) return

Loading workouts…

+ if (error) return

Error: {error}

+ + return ( +
+

Workouts

+ + + + + + + + + {workouts.map((workout) => ( + + + + + ))} + +
NameDescription
{workout.name}{workout.description}
+ {workouts.length === 0 &&

No workouts found.

} +
+ ) +} + +export default Workouts diff --git a/octofit-tracker/frontend/src/main.jsx b/octofit-tracker/frontend/src/main.jsx index b9a1a6d..a08ba09 100644 --- a/octofit-tracker/frontend/src/main.jsx +++ b/octofit-tracker/frontend/src/main.jsx @@ -1,5 +1,6 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' +import 'bootstrap/dist/css/bootstrap.min.css' import './index.css' import App from './App.jsx'