frontend 1

This commit is contained in:
matt committed 2026-10-15 04:42:30 -07:00
1 parent 168c817324
commit 0bc92cdb8b
4 files changed
+148 -33

No files matched your search

+1
View File
@@ -1,2 +1,3 @@
This app is intended to use with tracking personal fitness providers and visits to them. This will allow for tracking the providers, clients, and visits, and is intended to be used by the client for record-keeping. Providers will have photos and hourly rates. Visits will have duration and provider info. The client using the app should have a dashboard that shows a summary of all visits, and areas to view past visits, add new providers, and add visits. This will require a web front-end and a database back-end. This app is intended to use with tracking personal fitness providers and visits to them. This will allow for tracking the providers, clients, and visits, and is intended to be used by the client for record-keeping. Providers will have photos and hourly rates. Visits will have duration and provider info. The client using the app should have a dashboard that shows a summary of all visits, and areas to view past visits, add new providers, and add visits. This will require a web front-end and a database back-end.
+86 -12
View File
@@ -1,14 +1,88 @@
// Simple fetch to populate visit list // ---------------------------------------------------------------------
const visitList = document.getElementById('visit-list'); // Helper: call the API relative to the current host (no hard‑coded host)
async function loadVisits() { const api = async (method, endpoint, data) => {
const resp = await fetch('http://localhost:8000/visits/'); const res = await fetch(`/${endpoint}`, {
const data = await resp.json(); method,
visitList.innerHTML = ''; headers: { 'Content-Type': 'application/json' },
data.forEach(v => { body: data ? JSON.stringify(data) : null,
const li = document.createElement('li');
li.textContent = `${new Date(v.start_time).toLocaleString()} - ${v.duration_minutes} min`;
visitList.appendChild(li);
}); });
if (!res.ok) throw await res.json();
return res.json();
};
// ---------------------------------------------------------------------
// Load Providers
const providersList = document.getElementById('providers-list');
const providerForm = document.getElementById('provider-form');
const providerSelect = document.querySelector('#service-form select[name="provider_id"]');
const visitProviderSelect = document.querySelector('#visit-form select[name="provider_id"]');
async function loadProviders() {
const providers = await api('GET', 'providers/');
providersList.innerHTML = `<ul>${providers.map(p => `<li>${p.name} (${p.hourly_rate || 'n/a'})</li>`).join('')}</ul>`;
const opts = providers.map(p => `<option value="${p.id}">${p.name}</option>`).join('');
providerSelect.innerHTML = opts;
visitProviderSelect.innerHTML = opts;
} }
loadVisits(); providerForm.addEventListener('submit', async e => {
// Could add intervals or form handling later e.preventDefault();
const data = Object.fromEntries(new FormData(e.target).entries());
if (!data.hourly_rate) delete data.hourly_rate;
await api('POST', 'providers/', data);
await loadProviders();
});
// ---------------------------------------------------------------------
// Load Clients
const clientsList = document.getElementById('clients-list');
const clientForm = document.getElementById('client-form');
const clientSelect = document.querySelector('#visit-form select[name="client_id"]');
async function loadClients() {
const clients = await api('GET', 'clients/');
clientsList.innerHTML = `<ul>${clients.map(c => `<li>${c.name}</li>`).join('')}</ul>`;
clientSelect.innerHTML = clients.map(c => `<option value="${c.id}">${c.name}</option>`).join('');
}
clientForm.addEventListener('submit', async e => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target).entries());
await api('POST', 'clients/', data);
await loadClients();
});
// ---------------------------------------------------------------------
// Load Services
const servicesList = document.getElementById('services-list');
const serviceForm = document.getElementById('service-form');
async function loadServices() {
const services = await api('GET', 'services/');
servicesList.innerHTML = `<ul>${services.map(s => `<li>${s.name} @ $${s.price} (${s.duration_minutes || 'n/a'}min)</li>`).join('')}</ul>`;
}
serviceForm.addEventListener('submit', async e => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target).entries());
if (!data.duration_minutes) delete data.duration_minutes;
await api('POST', 'services/', data);
await loadServices();
});
// ---------------------------------------------------------------------
// Load Visits
const visitsList = document.getElementById('visits-list');
const visitForm = document.getElementById('visit-form');
async function loadVisits() {
const visits = await api('GET', 'visits/');
visitsList.innerHTML = `<ul>${visits.map(v => `<li>${new Date(v.start_time).toLocaleString()} – ${v.duration_minutes}min (Provider #${v.provider_id} • Client #${v.client_id})</li>`).join('')}</ul>`;
}
visitForm.addEventListener('submit', async e => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target).entries());
if (data.start_time) data.start_time = new Date(data.start_time).toISOString();
await api('POST', 'visits/', data);
await loadVisits();
});
// ---------------------------------------------------------------------
// Initial load
async function init() {
await Promise.all([loadProviders(), loadClients(), loadServices(), loadVisits()]);
}
init();
+55 -20
View File
@@ -1,20 +1,55 @@
<!DOCTYPE html> @@
<html lang="en"> - <h1>Fitness Tracker Dashboard</h1>
<head> - <div id="visits" class="card">
<meta charset="UTF-8"> - <h2>Visits</h2>
<title>Fitness Tracker Dashboard</title> - <ul id="visit-list"></ul>
<style> - </div>
body {font-family: Arial, sans-serif; margin: 20px;} - <script src="app.js"></script>
.card {border: 1px solid #ddd; padding: 10px; margin-bottom: 10px;} + <h1>Fitness Tracker Dashboard</h1>
h2 {margin-top: 0;} +
</style> + <div class="card">
</head> + <h2>Providers</h2>
<body> + <form id="provider-form">
<h1>Fitness Tracker Dashboard</h1> + <div><label>Name: <input name="name" required></label></div>
<div id="visits" class="card"> + <div><label>Photo URL: <input name="photo_url"></label></div>
<h2>Visits</h2> + <div><label>Hourly Rate: <input name="hourly_rate" type="number" step="0.01"></label></div>
<ul id="visit-list"></ul> + <button type="submit">Add Provider</button>
</div> + </form>
<script src="app.js"></script> + <div class="list" id="providers-list"></div>
</body> + </div>
</html> +
+ <div class="card">
+ <h2>Clients</h2>
+ <form id="client-form">
+ <div><label>Name: <input name="name" required></label></div>
+ <button type="submit">Add Client</button>
+ </form>
+ <div class="list" id="clients-list"></div>
+ </div>
+
+ <div class="card">
+ <h2>Services</h2>
+ <form id="service-form">
+ <div><label>Provider: <select name="provider_id" required></select></label></div>
+ <div><label>Name: <input name="name" required></label></div>
+ <div><label>Price: <input name="price" type="number" step="0.01" required></label></div>
+ <div><label>Duration (min): <input name="duration_minutes" type="number"></label></div>
+ <button type="submit">Add Service</button>
+ </form>
+ <div class="list" id="services-list"></div>
+ </div>
+
+ <div class="card">
+ <h2>Visits</h2>
+ <form id="visit-form">
+ <div><label>Provider: <select name="provider_id" required></select></label></div>
+ <div><label>Client: <select name="client_id" required></select></label></div>
+ <div><label>Start Time: <input name="start_time" type="datetime-local"></label></div>
+ <div><label>Duration (min): <input name="duration_minutes" type="number" required></label></div>
+ <button type="submit">Add Visit</button>
+ </form>
+ <div class="list" id="visits-list"></div>
+ </div>
+
+ <script src="app.js"></script>
*** End Patch
+6 -1
View File
@@ -1,6 +1,7 @@
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.responses import RedirectResponse
import models, database import models, database
from routers import providers, clients, visits, services from routers import providers, clients, visits, services
@@ -26,6 +27,10 @@ app.include_router(services.router, prefix="/services", tags=["services"])
@app.on_event("startup") @app.on_event("startup")
async def startup(): async def startup():
database.Base.metadata.create_all(bind=database.engine) database.Base.metadata.create_all(bind=database.engine)
# Mount frontend static files # Mount frontend static files at /frontend
app.mount("/frontend", StaticFiles(directory="frontend"), name="frontend") app.mount("/frontend", StaticFiles(directory="frontend"), name="frontend")
# Simple redirect from root to /frontend for convenience
@app.get("/")
def root():
return RedirectResponse(url="/frontend/")