This commit is contained in:
Ty Clifford
2026-06-30 12:39:17 -04:00
parent 0fe5a92f90
commit 17ebaf21a3
18 changed files with 1789 additions and 143 deletions
+32 -15
View File
@@ -1,6 +1,8 @@
# Neon Health Tracker
# Neon Medical Tracker
A dependency-free PHP, HTML5, JavaScript, and SQLite tracker for workouts, nutrition, supplements, and body metrics.
A dependency-free PHP, HTML5, JavaScript, and SQLite web app for multi-user medical tracking, terminology lookup, medication reference links, and admin-managed profiles.
This app is a personal tracking and reference tool. It is not medical advice, diagnosis, treatment guidance, or a substitute for a clinician or pharmacist.
## Run
@@ -16,12 +18,13 @@ http://127.0.0.1:8080
## Public Routes
- `/` - public homepage for the service.
- `/workouts.php` - workout planning feature page.
- `/nutrition.php` - nutrition tracking feature page.
- `/recovery.php` - recovery, supplements, and health monitoring feature page.
- `/` - public homepage for the medical tracker service.
- `/workouts.php` - condition-tracking feature page.
- `/nutrition.php` - medicine catalog feature page.
- `/recovery.php` - NIH/NLM research links page.
- `/medical.php` - medical terminology, conditions, medicines, and study-link database.
- `/signup.php` - public profile signup.
- `/tracker.php` - the workout, nutrition, supplement, and health dashboard.
- `/tracker.php` - the medical tracker dashboard.
- `/admin.php` - admin user-management dashboard.
## Storage
@@ -32,20 +35,34 @@ SQLite is created automatically at:
data/health_tracker.sqlite
```
The app seeds two sample profiles, built-in exercise movements, common gym supplements, starter routines, planner entries, nutrition logs, supplement logs, and body metrics.
The app seeds sample profiles, medical terminology, condition summaries, a medication catalog, NIH/NLM study-link categories, medication logs, vitals, labs, symptoms, allergies, appointments, immunizations, and the older optional wellness/fitness data.
## Included
- Multi-user profiles with separate routines, logs, goals, and theme preferences.
- Multi-user profiles with separate records and theme preferences.
- Admin dashboard for creating, editing, activating/deactivating, and deleting user profiles.
- First user created in a database is automatically promoted to `admin`; later users default to `member`.
- Daily, weekly, and monthly workout planner views.
- Routine planning for weight lifting, cycling, running, swimming, conditioning, mobility, core, outdoor, and hybrid programs.
- Configurable exercise and supplement libraries.
- Nutrition goals scoped by day, week, or month, compared against actual logs.
- Supplement tracking for pre-workout, intra-workout, post-workout, daily, and evening use.
- Health metrics for weight, body fat, sleep, resting heart rate, mood, and notes.
- Daily, weekly, and monthly views for medical logs.
- Medication logging by date, dose, route, frequency, status, prescriber, and notes.
- Vital logs for blood pressure, pulse, temperature, oxygen saturation, glucose, pain score, symptoms, and notes.
- Lab result records with units, reference ranges, clinician/source notes, and collection dates.
- Symptom journal, allergy list, appointments, and immunization records.
- Medical terminology and condition database with source links.
- Medicine catalog with RxNorm, MedlinePlus, PubMed, and ClinicalTrials.gov links.
- NIH/NLM research-link categories related to the listed medicines.
- Neon dark theme by default, light mode option, and green, blue, red, pink, and orange accents.
- Optional legacy fitness, nutrition, and supplement tracking remains available as wellness context.
## Reference Sources
- RxNorm and RxNav from the U.S. National Library of Medicine: `https://www.nlm.nih.gov/research/umls/rxnorm/index.html`
- MedlinePlus condition and medication pages: `https://medlineplus.gov/`
- PubMed search links for medicine and condition research: `https://pubmed.ncbi.nlm.nih.gov/`
- ClinicalTrials.gov search links for study discovery: `https://clinicaltrials.gov/`
- NIH/NIDDK Diabetes Prevention Program: `https://www.niddk.nih.gov/about-niddk/research-areas/diabetes/diabetes-prevention-program-dpp`
- NIH/NHLBI SPRINT study page: `https://www.nhlbi.nih.gov/science/systolic-blood-pressure-intervention-trial-sprint-study`
- NIH/NHLBI asthma page: `https://www.nhlbi.nih.gov/health/asthma`
- NIH/NIMH depression page: `https://www.nimh.nih.gov/health/topics/depression`
## Photo Credits
+1069
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+4 -4
View File
@@ -4,7 +4,7 @@ declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
app_db();
$pageTitle = 'Admin Dashboard | Neon Health Tracker';
$pageTitle = 'Admin Dashboard | Neon Medical Tracker';
$activePage = 'admin';
require __DIR__ . '/partials/site-header.php';
?>
@@ -13,7 +13,7 @@ require __DIR__ . '/partials/site-header.php';
<div class="site-section-heading">
<p class="site-eyebrow">Admin dashboard</p>
<h1>Manage users and their profiles.</h1>
<p>Use an active admin profile to create users, update profile details, assign roles, activate or deactivate accounts, and remove users.</p>
<p>Use an active admin profile to create users, update profile details, assign roles, activate or deactivate accounts, and remove users with their medical tracker records.</p>
</div>
<div class="admin-access-row">
@@ -50,10 +50,10 @@ require __DIR__ . '/partials/site-header.php';
<label class="field"><span>Phone</span><input name="phone" type="tel"></label>
<label class="field"><span>Birthday</span><input name="birthday" type="date"></label>
<label class="field"><span>Height in</span><input name="height_in" type="number" min="0" step="0.5"></label>
<label class="field"><span>Training level</span><select name="training_level"><option></option><option>Beginner</option><option>Intermediate</option><option>Advanced</option><option>Competitive</option></select></label>
<label class="field"><span>Tracking comfort</span><select name="training_level"><option></option><option>Getting started</option><option>Comfortable</option><option>Advanced</option><option>Caregiver</option></select></label>
<label class="field"><span>Theme</span><select name="theme_mode"><option>dark</option><option>light</option></select></label>
<label class="field"><span>Accent</span><select name="theme_accent"><option>green</option><option>blue</option><option>red</option><option>pink</option><option>orange</option></select></label>
<label class="field wide"><span>Fitness focus</span><input name="fitness_focus" placeholder="Strength, endurance, recovery"></label>
<label class="field wide"><span>Care focus</span><input name="fitness_focus" placeholder="Blood pressure, glucose, asthma"></label>
<label class="field wide"><span>Emergency contact</span><input name="emergency_contact" placeholder="Name and phone"></label>
</div>
<button class="site-primary full" type="submit">Save profile</button>
+12
View File
@@ -12,6 +12,11 @@ try {
exit;
}
if ($method === 'GET' && $action === 'library_search') {
json_response(library_search_payload($_GET));
exit;
}
if ($method !== 'POST') {
json_response(['error' => 'Unsupported method.'], 405);
exit;
@@ -33,6 +38,13 @@ try {
'create_nutrition_log' => 'create_nutrition_log',
'create_supplement_log' => 'create_supplement_log',
'create_body_metric' => 'create_body_metric',
'create_medication_log' => 'create_medication_log',
'create_vital_log' => 'create_vital_log',
'create_lab_result' => 'create_lab_result',
'create_symptom_log' => 'create_symptom_log',
'create_allergy_record' => 'create_allergy_record',
'create_appointment' => 'create_appointment',
'create_immunization_record' => 'create_immunization_record',
];
if (!isset($routes[$action])) {
+9 -1
View File
@@ -92,6 +92,10 @@ function renderAdminStats() {
["Inactive", summary.inactive_users || 0],
["Admins", summary.admins || 0],
["Members", summary.members || 0],
["Med logs", summary.medication_logs || 0],
["Vitals", summary.vital_logs || 0],
["Labs", summary.lab_results || 0],
["Visits", summary.appointments || 0],
];
admin$("#adminStats").innerHTML = stats.map(([label, value]) => `
@@ -116,7 +120,7 @@ function renderAdminUsers() {
<div>
<h3>${adminEscape(user.name)}${isCurrent ? " (current)" : ""}</h3>
<p class="details-line">${adminEscape(user.email || "No email")}</p>
<p class="details-line">${adminEscape(user.fitness_focus || "No focus set")}</p>
<p class="details-line">${adminEscape(user.fitness_focus || "No care focus set")}</p>
<p class="timeline-meta">${adminEscape(user.phone || "No phone")}${adminEscape(training)}${adminEscape(height)}</p>
</div>
<div class="admin-badges">
@@ -130,6 +134,10 @@ function renderAdminUsers() {
<span>${adminNumber(user.nutrition_log_count)} nutrition logs</span>
<span>${adminNumber(user.supplement_log_count)} supplement logs</span>
<span>${adminNumber(user.metric_count)} metrics</span>
<span>${adminNumber(user.medication_log_count)} meds</span>
<span>${adminNumber(user.vital_log_count)} vitals</span>
<span>${adminNumber(user.lab_result_count)} labs</span>
<span>${adminNumber(user.appointment_count)} appointments</span>
</div>
<div class="status-row">
<button class="status-button" type="button" data-admin-action="edit" data-admin-user-id="${user.id}">Edit</button>
+182 -13
View File
@@ -114,6 +114,7 @@ function render() {
applyTheme();
renderControls();
renderSummaries();
renderMedical();
renderTimeline();
renderNutrition();
renderRoutines();
@@ -141,7 +142,7 @@ function applyTheme() {
function renderControls() {
const data = appState.data;
const user = data.currentUser;
$("#profileFocus").textContent = user.fitness_focus || "Multi-profile training dashboard";
$("#profileFocus").textContent = user.fitness_focus || "Multi-profile medical dashboard";
if (data.currentUserIsAdmin) {
$("#profileFocus").textContent += " / admin";
}
@@ -158,18 +159,21 @@ function renderControls() {
}
function renderSummaries() {
const { workoutSummary, nutritionSummary, latestMetric, supplementLogs } = appState.data;
const actual = nutritionSummary.actual;
const target = nutritionSummary.target;
const supplementTaken = supplementLogs.filter((item) => item.status === "taken").length;
const { latestVitals, medicationLogs, labResults, symptomLogs, allergyRecords, appointments } = appState.data;
const medicationTaken = medicationLogs.filter((item) => item.status === "taken").length;
const bp = latestVitals && Number(latestVitals.systolic) > 0 ? `${latestVitals.systolic}/${latestVitals.diastolic}` : "No BP";
const highestSeverity = symptomLogs.reduce((max, item) => Math.max(max, Number(item.severity || 0)), 0);
const activeAllergies = allergyRecords.filter((item) => item.status === "active").length;
const scheduledAppointments = appointments.filter((item) => item.status === "scheduled").length;
const cards = [
["Completion", `${number(workoutSummary.completion_rate, 1)}%`, `${workoutSummary.completed}/${workoutSummary.planned} workouts`],
["Volume", number(workoutSummary.weight_volume_lbs), "lb lifted"],
["Cardio", `${number(workoutSummary.minutes, 0)} min`, `${number(workoutSummary.distance_mi, 2)} mi`],
["Calories", number(actual.calories), `${number(target.calories)} target`],
["Protein", `${number(actual.protein_g)} g`, `${number(target.protein_g)} g target`],
["Weight", latestMetric ? `${number(latestMetric.weight_lbs, 1)} lb` : "No log", latestMetric ? `${number(latestMetric.sleep_hours, 1)} hr sleep` : `${supplementTaken} doses taken`],
["Medications", `${number(medicationLogs.length)}`, `${number(medicationTaken)} taken in range`],
["Blood Pressure", bp, latestVitals ? `${number(latestVitals.pulse)} pulse` : "No vitals log"],
["Glucose", latestVitals && Number(latestVitals.glucose_mg_dl) > 0 ? `${number(latestVitals.glucose_mg_dl)} mg/dL` : "No glucose", latestVitals ? `${number(latestVitals.oxygen_pct, 1)}% oxygen` : "No oxygen log"],
["Labs", `${number(labResults.length)}`, "results in range"],
["Symptoms", `${number(symptomLogs.length)}`, `highest severity ${number(highestSeverity)}`],
["Allergies", `${number(activeAllergies)}`, `${number(allergyRecords.length)} total records`],
["Appointments", `${number(scheduledAppointments)}`, "scheduled in range"],
];
$("#summaryGrid").innerHTML = cards
@@ -244,6 +248,143 @@ function renderWorkoutItem(workout) {
`;
}
function renderMedical() {
renderMedicationLogs();
renderVitalLogs();
renderLabResults();
renderSymptomLogs();
renderAllergyRecords();
renderAppointments();
renderImmunizationRecords();
renderStudies();
}
function renderMedicationLogs() {
const logs = appState.data.medicationLogs || [];
$("#medicationLogList").innerHTML = logs.length ? logs.map((log) => `
<article class="workout-item">
<div class="workout-top">
<div>
<h3>${escapeHtml(log.medication_name)}</h3>
<p class="details-line">${shortDate(log.taken_on)} | ${escapeHtml(log.dose || "open dose")} | ${escapeHtml(log.route || "route not set")} | ${escapeHtml(log.frequency || "frequency not set")}</p>
${log.class_name ? `<p class="timeline-meta">${escapeHtml(log.class_name)}</p>` : ""}
</div>
<span class="badge ${escapeHtml(log.status)}">${escapeHtml(log.status)}</span>
</div>
<p class="muted">${escapeHtml([log.prescriber, log.notes].filter(Boolean).join(" | ") || "No notes")}</p>
${log.rxnorm_url ? `<a class="text-button source-button" href="${escapeHtml(log.rxnorm_url)}">RxNorm</a>` : ""}
</article>
`).join("") : `<div class="empty-state">No medication logs in this range</div>`;
}
function renderVitalLogs() {
const logs = appState.data.vitalLogs || [];
$("#vitalLogList").innerHTML = logs.length ? logs.map((log) => {
const bp = Number(log.systolic) > 0 ? `${log.systolic}/${log.diastolic}` : "No BP";
return `
<article class="log-item">
<div class="log-top">
<strong>${shortDate(log.measured_on)}</strong>
<span class="badge">${bp}</span>
</div>
<p>${number(log.pulse)} pulse / ${number(log.temperature_f, 1)} F / ${number(log.oxygen_pct, 1)}% O2 / ${number(log.glucose_mg_dl)} glucose / pain ${number(log.pain_score)}</p>
<p class="muted">${escapeHtml([log.symptoms, log.notes].filter(Boolean).join(" | ") || "No symptoms noted")}</p>
</article>
`;
}).join("") : `<div class="empty-state">No vitals in this range</div>`;
}
function renderLabResults() {
const labs = appState.data.labResults || [];
$("#labResultList").innerHTML = labs.length ? labs.map((lab) => `
<article class="log-item">
<div class="log-top">
<strong>${escapeHtml(lab.test_name)}</strong>
<span class="badge">${escapeHtml(lab.result_value)} ${escapeHtml(lab.unit || "")}</span>
</div>
<p>${shortDate(lab.collected_on)} / ${escapeHtml(lab.reference_range || "No reference range")}</p>
<p class="muted">${escapeHtml([lab.ordering_clinician, lab.notes].filter(Boolean).join(" | ") || "No notes")}</p>
</article>
`).join("") : `<div class="empty-state">No lab results in this range</div>`;
}
function renderSymptomLogs() {
const logs = appState.data.symptomLogs || [];
$("#symptomLogList").innerHTML = logs.length ? logs.map((log) => `
<article class="log-item">
<div class="log-top">
<strong>${escapeHtml(log.symptom)}</strong>
<span class="badge">severity ${number(log.severity)}</span>
</div>
<p>${shortDate(log.observed_on)} / ${escapeHtml(log.body_area || "body area not set")} / ${escapeHtml(log.duration || "duration not set")}</p>
<p class="muted">${escapeHtml([log.triggers, log.action_taken, log.notes].filter(Boolean).join(" | ") || "No notes")}</p>
</article>
`).join("") : `<div class="empty-state">No symptom logs in this range</div>`;
}
function renderAllergyRecords() {
const records = appState.data.allergyRecords || [];
$("#allergyRecordList").innerHTML = records.length ? records.map((record) => `
<article class="log-item">
<div class="log-top">
<strong>${escapeHtml(record.allergen)}</strong>
<span class="badge ${escapeHtml(record.status)}">${escapeHtml(record.severity)}</span>
</div>
<p>${escapeHtml(record.reaction || "Reaction not set")} / ${escapeHtml(record.status)}</p>
<p class="muted">${escapeHtml([record.first_noted_on ? `first noted ${shortDate(record.first_noted_on)}` : "", record.notes].filter(Boolean).join(" | ") || "No notes")}</p>
</article>
`).join("") : `<div class="empty-state">No allergy records yet</div>`;
}
function renderAppointments() {
const appointments = appState.data.appointments || [];
$("#appointmentList").innerHTML = appointments.length ? appointments.map((appointment) => `
<article class="log-item">
<div class="log-top">
<strong>${escapeHtml(appointment.reason)}</strong>
<span class="badge ${escapeHtml(appointment.status)}">${escapeHtml(appointment.status)}</span>
</div>
<p>${shortDate(appointment.appointment_on)} / ${escapeHtml(appointment.clinician || "clinician not set")} / ${escapeHtml(appointment.specialty || "specialty not set")}</p>
<p class="muted">${escapeHtml([appointment.location, appointment.follow_up_notes].filter(Boolean).join(" | ") || "No follow-up notes")}</p>
</article>
`).join("") : `<div class="empty-state">No appointments in this range</div>`;
}
function renderImmunizationRecords() {
const records = appState.data.immunizationRecords || [];
$("#immunizationRecordList").innerHTML = records.length ? records.slice(0, 8).map((record) => `
<article class="log-item">
<div class="log-top">
<strong>${escapeHtml(record.vaccine_name)}</strong>
<span class="badge">${shortDate(record.administered_on)}</span>
</div>
<p>${escapeHtml(record.dose || "dose not set")} / ${escapeHtml(record.clinician_or_site || "site not set")}</p>
<p class="muted">${escapeHtml([record.next_due_on ? `next due ${shortDate(record.next_due_on)}` : "", record.lot_number ? `lot ${record.lot_number}` : "", record.notes].filter(Boolean).join(" | ") || "No notes")}</p>
</article>
`).join("") : `<div class="empty-state">No immunization records yet</div>`;
}
function renderStudies() {
const studies = (appState.data.researchStudies || []).slice(0, 6);
$("#studyList").innerHTML = studies.length ? studies.map((study) => `
<article class="supplement-item">
<div class="supplement-top">
<div>
<h3>${escapeHtml(study.title)}</h3>
<p class="details-line">${escapeHtml(study.category)} | ${escapeHtml(study.related_medicine || "general")}</p>
</div>
<span class="badge">${escapeHtml(study.study_type || "research")}</span>
</div>
<p>${escapeHtml(study.summary)}</p>
<div class="status-row">
<a class="status-button" href="${escapeHtml(study.source_url)}">NIH/NLM</a>
<a class="status-button" href="${escapeHtml(study.pubmed_url)}">PubMed</a>
<a class="status-button" href="${escapeHtml(study.clinicaltrials_url)}">ClinicalTrials.gov</a>
</div>
</article>
`).join("") : `<div class="empty-state">No study links seeded</div>`;
}
function renderNutrition() {
const { nutritionSummary, nutritionLogs } = appState.data;
const target = nutritionSummary.target;
@@ -374,7 +515,7 @@ function renderAdmin() {
<div>
<h3>${escapeHtml(user.name)}${isCurrent ? " (current)" : ""}</h3>
<p class="details-line">${escapeHtml(user.email || "No email")}</p>
<p class="details-line">${escapeHtml(user.fitness_focus || "No focus set")}</p>
<p class="details-line">${escapeHtml(user.fitness_focus || "No care focus set")}</p>
<p class="timeline-meta">${escapeHtml(user.phone || "No phone")}${escapeHtml(training)}${escapeHtml(height)}</p>
</div>
<div class="admin-badges">
@@ -388,6 +529,10 @@ function renderAdmin() {
<span>${number(user.nutrition_log_count)} nutrition logs</span>
<span>${number(user.supplement_log_count)} supplement logs</span>
<span>${number(user.metric_count)} metrics</span>
<span>${number(user.medication_log_count)} meds</span>
<span>${number(user.vital_log_count)} vitals</span>
<span>${number(user.lab_result_count)} labs</span>
<span>${number(user.appointment_count)} appointments</span>
</div>
<div class="status-row">
<button class="status-button" type="button" data-admin-action="edit" data-admin-user-id="${user.id}">Edit</button>
@@ -455,18 +600,26 @@ function option(value, label, selected = false) {
}
function populateForms() {
const { routines, exercises, supplements, nutritionGoals, range } = appState.data;
const { routines, exercises, supplements, nutritionGoals, medicationCatalog, range } = appState.data;
const routineOptions = option("", "No routine") + routines.map((routine) => option(routine.id, `${routine.name} (${routine.planner_scope})`)).join("");
const goalOptions = option("", "Separate target") + nutritionGoals.map((goal) => option(goal.id, `${goal.label} (${goal.scope})`)).join("");
const exerciseOptions = option("", "Choose exercise") + exercises.map((exercise) => option(exercise.id, `${exercise.name} (${exercise.category})`)).join("");
const supplementOptions = option("", "Choose supplement") + supplements.map((supplement) => option(supplement.id, `${supplement.name} (${supplement.timing})`)).join("");
const medicationOptions = option("", "Choose medication") + medicationCatalog.map((medicine) => option(medicine.id, `${medicine.generic_name} (${medicine.class_name})`)).join("");
$$("#workoutForm [name='routine_id']").forEach((select) => select.innerHTML = routineOptions);
$$("#routineForm [name='nutrition_goal_id']").forEach((select) => select.innerHTML = goalOptions);
$$("#workoutForm [name='exercise_id']").forEach((select) => select.innerHTML = exerciseOptions);
$$("#supplementLogForm [name='supplement_id']").forEach((select) => select.innerHTML = supplementOptions);
$$("#medicationLogForm [name='medication_id']").forEach((select) => select.innerHTML = medicationOptions);
const dateFields = [
"#medicationLogForm [name='taken_on']",
"#vitalLogForm [name='measured_on']",
"#labResultForm [name='collected_on']",
"#symptomLogForm [name='observed_on']",
"#appointmentForm [name='appointment_on']",
"#immunizationRecordForm [name='administered_on']",
"#workoutForm [name='scheduled_on']",
"#routineForm [name='start_date']",
"#nutritionGoalForm [name='start_date']",
@@ -575,6 +728,15 @@ function wireControls() {
$("#supplementLogForm [name='dose']").value = supplement.default_dose;
});
$("#medicationLogForm [name='medication_id']").addEventListener("change", (event) => {
const medicine = appState.data.medicationCatalog.find((item) => String(item.id) === event.target.value);
if (!medicine) return;
$("#medicationLogForm [name='medication_name']").value = medicine.generic_name;
if (!$("#medicationLogForm [name='notes']").value) {
$("#medicationLogForm [name='notes']").value = medicine.tracking_note;
}
});
$("#newAdminUserButton").addEventListener("click", () => {
resetAdminUserForm();
$("#adminUserForm").scrollIntoView({ behavior: "smooth", block: "start" });
@@ -642,6 +804,13 @@ async function saveTheme(mode = null, accent = null) {
}
function wireForms() {
wireForm("#medicationLogForm", "create_medication_log", "Medication logged");
wireForm("#vitalLogForm", "create_vital_log", "Vitals logged");
wireForm("#labResultForm", "create_lab_result", "Lab result logged");
wireForm("#symptomLogForm", "create_symptom_log", "Symptom logged");
wireForm("#allergyRecordForm", "create_allergy_record", "Allergy saved");
wireForm("#appointmentForm", "create_appointment", "Appointment saved");
wireForm("#immunizationRecordForm", "create_immunization_record", "Immunization saved");
wireForm("#workoutForm", "create_workout", "Workout saved");
wireForm("#routineForm", "create_routine", "Routine saved");
wireForm("#nutritionGoalForm", "create_nutrition_goal", "Nutrition target saved");
+43
View File
@@ -323,6 +323,7 @@ figcaption a {
.detail-grid article,
.site-metric-stack article,
.signup-steps article,
.term-table article,
.admin-denied,
.admin-stat,
.admin-user-card,
@@ -346,6 +347,7 @@ figcaption a {
.feature-grid p,
.detail-grid p,
.term-table p,
.site-metric-stack span,
.signup-steps span,
.details-line,
@@ -355,6 +357,43 @@ figcaption a {
line-height: 1.5;
}
.term-table {
display: grid;
gap: 10px;
}
.term-table article {
display: grid;
grid-template-columns: minmax(180px, 0.34fr) minmax(0, 0.4fr) minmax(0, 0.4fr) auto;
gap: 12px;
align-items: start;
}
.term-table span {
color: var(--accent);
font-size: 0.78rem;
}
.term-table a,
.source-links a {
color: var(--accent);
text-decoration: none;
}
.source-links {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
}
.source-links a {
border: 1px solid var(--line);
border-radius: 999px;
padding: 4px 8px;
font-size: 0.78rem;
}
.site-split,
.detail-hero,
.signup-layout {
@@ -730,6 +769,10 @@ textarea:focus {
justify-content: flex-start;
}
.term-table article {
grid-template-columns: 1fr;
}
.detail-hero img {
height: 320px;
}
+1
View File
@@ -630,6 +630,7 @@ textarea:focus {
}
.badge.skipped,
.badge.stopped,
.badge.inactive {
color: var(--danger);
border-color: color-mix(in srgb, var(--danger), transparent 50%);
+20 -20
View File
@@ -4,7 +4,7 @@ declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
app_db();
$pageTitle = 'Neon Health Tracker';
$pageTitle = 'Neon Medical Tracker';
$activePage = 'home';
$heroImage = 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/45/Jogging_Woman_in_Grass.jpg/1280px-Jogging_Woman_in_Grass.jpg';
@@ -14,12 +14,12 @@ require __DIR__ . '/partials/site-header.php';
<section class="site-hero">
<img src="<?= htmlspecialchars($heroImage, ENT_QUOTES) ?>" alt="Runner training outdoors in bright green grass">
<div class="site-hero-copy">
<p class="site-eyebrow">Workout planning, nutrition balance, supplement tracking</p>
<h1>Neon Health Tracker</h1>
<p class="site-lede">A multi-user fitness planner for people who want routines, meals, recovery, and body metrics in one fast web dashboard.</p>
<p class="site-eyebrow">Medication logs, vitals, labs, terminology, NIH/NLM research links</p>
<h1>Neon Medical Tracker</h1>
<p class="site-lede">A multi-user medical tracking workspace for people who want medicines, readings, labs, symptoms, source links, and profile administration in one web dashboard.</p>
<div class="site-actions">
<a class="site-primary" href="/signup.php">Start tracking</a>
<a class="site-secondary" href="/workouts.php">Explore features</a>
<a class="site-secondary" href="/medical.php">Explore database</a>
</div>
</div>
<p class="photo-credit">
@@ -31,50 +31,50 @@ require __DIR__ . '/partials/site-header.php';
<section class="site-band">
<div class="site-section-heading">
<p class="site-eyebrow">Built for daily use</p>
<h2>One workspace for training, food, recovery, and progress.</h2>
<h2>One workspace for personal health records and sourced reference links.</h2>
</div>
<div class="feature-grid">
<article>
<span>01</span>
<h3>Routine planning</h3>
<p>Schedule weight lifting, cycling, running, swimming, conditioning, mobility, and custom workouts by day, week, or month.</p>
<h3>Medication tracking</h3>
<p>Log prescription and over-the-counter medicines by date, dose, route, frequency, status, prescriber, and notes.</p>
</article>
<article>
<span>02</span>
<h3>Nutrition targets</h3>
<p>Compare actual logs against daily, weekly, or monthly calories, protein, carbs, fat, and hydration goals.</p>
<h3>Vitals and symptoms</h3>
<p>Track blood pressure, pulse, temperature, oxygen saturation, glucose, pain scores, symptoms, and context.</p>
</article>
<article>
<span>03</span>
<h3>Supplement timing</h3>
<p>Track pre-workout, intra-workout, post-workout, daily, and evening supplements without mixing them into meal logs.</p>
<h3>Labs and terminology</h3>
<p>Store lab values and browse plain-language medical terms connected to MedlinePlus, RxNorm, PubMed, and ClinicalTrials.gov.</p>
</article>
<article>
<span>04</span>
<h3>Health monitoring</h3>
<p>Keep weight, body fat, sleep, resting heart rate, mood, and notes connected to the training plan.</p>
<h3>NIH/NLM studies</h3>
<p>Browse NIH/NLM research categories and study-search links related to the medicines included in the catalog.</p>
</article>
</div>
</section>
<section class="site-split">
<div>
<p class="site-eyebrow">For individuals and small teams</p>
<p class="site-eyebrow">For households, caregivers, and small teams</p>
<h2>Profiles stay separate, admins stay in control.</h2>
<p>Each user gets a profile, theme preference, routines, logs, goals, and metric history. Admins can create users, edit details, activate or deactivate accounts, and remove profiles from a dedicated dashboard.</p>
<p>Each user gets a profile, theme preference, medication logs, vitals, lab results, medical notes, and research references. Admins can create users, edit details, activate or deactivate accounts, and remove profiles from a dedicated dashboard.</p>
<a class="site-secondary inline-link" href="/admin.php">Open admin dashboard</a>
</div>
<div class="site-metric-stack">
<article><strong>Daily</strong><span>Plan todays workout, meals, supplements, and body check-in.</span></article>
<article><strong>Weekly</strong><span>See volume, cardio minutes, macro targets, and completion rate.</span></article>
<article><strong>Monthly</strong><span>Compare long-range goals against real nutrition and training behavior.</span></article>
<article><strong>Daily</strong><span>Log todays medicines, readings, symptoms, and notes.</span></article>
<article><strong>Weekly</strong><span>Review medication adherence, vital trends, labs, and follow-up context.</span></article>
<article><strong>Monthly</strong><span>Keep longer-term records ready for visits with clinicians or caregivers.</span></article>
</div>
</section>
<section class="cta-strip">
<div>
<p class="site-eyebrow">Ready when you are</p>
<h2>Create your profile and open the tracker.</h2>
<h2>Create your profile and open the medical tracker.</h2>
</div>
<a class="site-primary" href="/signup.php">Sign up</a>
</section>
+103
View File
@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
$pdo = app_db();
$pageTitle = 'Medical Terminology Database | Neon Medical Tracker';
$activePage = 'terminology';
$terms = db_all($pdo, 'SELECT * FROM medical_terms ORDER BY category, term');
$conditions = db_all($pdo, 'SELECT * FROM medical_conditions ORDER BY category, name');
$medicines = db_all($pdo, 'SELECT * FROM medication_catalog ORDER BY generic_name');
$studies = db_all($pdo, 'SELECT * FROM research_studies ORDER BY category, related_medicine, title');
require __DIR__ . '/partials/site-header.php';
?>
<main>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Medical terminology database</p>
<h1>Terms, conditions, medicines, and NIH/NLM study links.</h1>
<p>Entries are concise app-authored summaries with links to NIH/NLM systems such as MedlinePlus, RxNorm, PubMed, and ClinicalTrials.gov. This is a tracking/reference aid, not medical advice.</p>
</div>
<div class="detail-grid">
<?php foreach ($conditions as $condition): ?>
<article>
<h2><?= htmlspecialchars($condition['name'], ENT_QUOTES) ?></h2>
<p><strong><?= htmlspecialchars($condition['category'], ENT_QUOTES) ?></strong></p>
<p><?= htmlspecialchars($condition['overview'], ENT_QUOTES) ?></p>
<p><?= htmlspecialchars($condition['common_tracking'], ENT_QUOTES) ?></p>
<a class="site-secondary" href="<?= htmlspecialchars($condition['source_url'], ENT_QUOTES) ?>">Source</a>
</article>
<?php endforeach; ?>
</div>
</section>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Terminology</p>
<h2>Plain-language medical terms</h2>
</div>
<div class="term-table">
<?php foreach ($terms as $term): ?>
<article>
<div>
<h3><?= htmlspecialchars($term['term'], ENT_QUOTES) ?></h3>
<span><?= htmlspecialchars($term['category'], ENT_QUOTES) ?></span>
</div>
<p><?= htmlspecialchars($term['plain_language_definition'], ENT_QUOTES) ?></p>
<p><?= htmlspecialchars($term['clinical_context'], ENT_QUOTES) ?></p>
<a href="<?= htmlspecialchars($term['source_url'], ENT_QUOTES) ?>"><?= htmlspecialchars($term['source_name'], ENT_QUOTES) ?></a>
</article>
<?php endforeach; ?>
</div>
</section>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Medication catalog</p>
<h2>Medicines with RxNorm, PubMed, and ClinicalTrials.gov links</h2>
</div>
<div class="detail-grid">
<?php foreach ($medicines as $medicine): ?>
<article>
<h2><?= htmlspecialchars(ucfirst($medicine['generic_name']), ENT_QUOTES) ?></h2>
<p><strong><?= htmlspecialchars($medicine['class_name'], ENT_QUOTES) ?></strong></p>
<p><?= htmlspecialchars($medicine['common_use'], ENT_QUOTES) ?></p>
<p><?= htmlspecialchars($medicine['tracking_note'], ENT_QUOTES) ?></p>
<div class="source-links">
<a href="<?= htmlspecialchars($medicine['rxnorm_url'], ENT_QUOTES) ?>">RxNorm</a>
<a href="<?= htmlspecialchars($medicine['medlineplus_url'], ENT_QUOTES) ?>">MedlinePlus</a>
<a href="<?= htmlspecialchars($medicine['pubmed_url'], ENT_QUOTES) ?>">PubMed</a>
<a href="<?= htmlspecialchars($medicine['clinicaltrials_url'], ENT_QUOTES) ?>">Studies</a>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">NIH/NLM study links</p>
<h2>Research categories connected to the listed medicines</h2>
</div>
<div class="term-table">
<?php foreach ($studies as $study): ?>
<article>
<div>
<h3><?= htmlspecialchars($study['title'], ENT_QUOTES) ?></h3>
<span><?= htmlspecialchars($study['category'] . ' / ' . $study['related_medicine'], ENT_QUOTES) ?></span>
</div>
<p><?= htmlspecialchars($study['summary'], ENT_QUOTES) ?></p>
<div class="source-links">
<a href="<?= htmlspecialchars($study['source_url'], ENT_QUOTES) ?>"><?= htmlspecialchars($study['nih_source'], ENT_QUOTES) ?></a>
<a href="<?= htmlspecialchars($study['pubmed_url'], ENT_QUOTES) ?>">PubMed</a>
<a href="<?= htmlspecialchars($study['clinicaltrials_url'], ENT_QUOTES) ?>">ClinicalTrials.gov</a>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
</main>
<?php require __DIR__ . '/partials/site-footer.php'; ?>
+23 -24
View File
@@ -2,47 +2,46 @@
declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
app_db();
$pdo = app_db();
$pageTitle = 'Nutrition Tracking | Neon Health Tracker';
$activePage = 'nutrition';
$pageTitle = 'Medicine Catalog | Neon Medical Tracker';
$activePage = 'medicines';
$medicines = db_all($pdo, 'SELECT * FROM medication_catalog ORDER BY generic_name');
$photo = 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/A_large_mixed_salad.jpg/1024px-A_large_mixed_salad.jpg';
require __DIR__ . '/partials/site-header.php';
?>
<main>
<section class="detail-hero reverse">
<figure>
<img src="<?= htmlspecialchars($photo, ENT_QUOTES) ?>" alt="Large mixed salad with vegetables and protein">
<img src="<?= htmlspecialchars($photo, ENT_QUOTES) ?>" alt="Large mixed salad">
<figcaption>
Photo: <a href="https://commons.wikimedia.org/wiki/File:A_large_mixed_salad.jpg">A large mixed salad</a>
by Jmabel, <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>.
</figcaption>
</figure>
<div>
<p class="site-eyebrow">Targets vs actuals</p>
<h1>Keep nutrition flexible without losing the plan.</h1>
<p>Set daily, weekly, or monthly goals for calories, protein, carbs, fat, and hydration. Log actual intake separately so deviations stay visible instead of buried.</p>
<a class="site-primary" href="/signup.php">Start a nutrition plan</a>
<p class="site-eyebrow">Medicine catalog</p>
<h1>Track medicines with source links beside every catalog entry.</h1>
<p>The starter catalog includes common medicines and links each one to RxNorm, MedlinePlus, PubMed, and ClinicalTrials.gov searches.</p>
<a class="site-primary" href="/tracker.php">Open medical tracker</a>
</div>
</section>
<section class="detail-grid">
<article>
<h2>Separate goals</h2>
<p>Nutrition goals can be attached to routines or kept independent for users who want to experiment.</p>
</article>
<article>
<h2>Macro balance</h2>
<p>Progress bars show how far actual logs are from the chosen target for the current planner range.</p>
</article>
<article>
<h2>Hydration</h2>
<p>Water targets stay beside macros so recovery and performance habits remain part of the daily flow.</p>
</article>
<article>
<h2>Range scaling</h2>
<p>Use daily, weekly, or monthly target scopes, with daily goals scaling when a broader target is not set.</p>
</article>
<?php foreach ($medicines as $medicine): ?>
<article>
<h2><?= htmlspecialchars(ucfirst($medicine['generic_name']), ENT_QUOTES) ?></h2>
<p><strong><?= htmlspecialchars($medicine['class_name'], ENT_QUOTES) ?></strong></p>
<p><?= htmlspecialchars($medicine['common_use'], ENT_QUOTES) ?></p>
<p><?= htmlspecialchars($medicine['tracking_note'], ENT_QUOTES) ?></p>
<div class="source-links">
<a href="<?= htmlspecialchars($medicine['rxnorm_url'], ENT_QUOTES) ?>">RxNorm</a>
<a href="<?= htmlspecialchars($medicine['medlineplus_url'], ENT_QUOTES) ?>">MedlinePlus</a>
<a href="<?= htmlspecialchars($medicine['pubmed_url'], ENT_QUOTES) ?>">PubMed</a>
<a href="<?= htmlspecialchars($medicine['clinicaltrials_url'], ENT_QUOTES) ?>">Studies</a>
</div>
</article>
<?php endforeach; ?>
</section>
</main>
<?php require __DIR__ . '/partials/site-footer.php'; ?>
+3 -3
View File
@@ -1,11 +1,11 @@
<footer class="site-footer">
<div>
<strong>Neon Health Tracker</strong>
<p>Workout planning, nutrition tracking, supplement timing, health metrics, and admin-managed profiles.</p>
<strong>Neon Medical Tracker</strong>
<p>Medication logs, vitals, lab results, terminology, NIH/NLM study links, and admin-managed profiles.</p>
</div>
<div class="footer-links">
<a href="/signup.php">Create profile</a>
<a href="/tracker.php">Open tracker</a>
<a href="/tracker.php">Open medical tracker</a>
<a href="/admin.php">Admin dashboard</a>
</div>
</footer>
+6 -5
View File
@@ -1,5 +1,5 @@
<?php
$pageTitle = $pageTitle ?? 'Neon Health Tracker';
$pageTitle = $pageTitle ?? 'Neon Medical Tracker';
$activePage = $activePage ?? '';
function site_active(string $page, string $activePage): string
{
@@ -18,13 +18,14 @@ function site_active(string $page, string $activePage): string
<header class="site-header">
<a class="site-logo" href="/">
<span class="site-logo-bars" aria-hidden="true"><i></i><i></i><i></i></span>
<span>Neon Health Tracker</span>
<span>Neon Medical Tracker</span>
</a>
<nav class="site-nav" aria-label="Primary navigation">
<a<?= site_active('home', $activePage) ?> href="/">Home</a>
<a<?= site_active('workouts', $activePage) ?> href="/workouts.php">Workouts</a>
<a<?= site_active('nutrition', $activePage) ?> href="/nutrition.php">Nutrition</a>
<a<?= site_active('recovery', $activePage) ?> href="/recovery.php">Recovery</a>
<a<?= site_active('conditions', $activePage) ?> href="/workouts.php">Conditions</a>
<a<?= site_active('medicines', $activePage) ?> href="/nutrition.php">Medicines</a>
<a<?= site_active('research', $activePage) ?> href="/recovery.php">Research</a>
<a<?= site_active('terminology', $activePage) ?> href="/medical.php">Terminology</a>
<a<?= site_active('signup', $activePage) ?> href="/signup.php">Sign up</a>
<a href="/tracker.php">Tracker</a>
<a href="/admin.php">Admin</a>
+26 -24
View File
@@ -2,20 +2,21 @@
declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
app_db();
$pdo = app_db();
$pageTitle = 'Recovery and Supplements | Neon Health Tracker';
$activePage = 'recovery';
$pageTitle = 'NIH/NLM Research Links | Neon Medical Tracker';
$activePage = 'research';
$studies = db_all($pdo, 'SELECT * FROM research_studies ORDER BY category, related_medicine, title');
$photo = 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/45/Jogging_Woman_in_Grass.jpg/1280px-Jogging_Woman_in_Grass.jpg';
require __DIR__ . '/partials/site-header.php';
?>
<main>
<section class="detail-hero">
<div>
<p class="site-eyebrow">Recovery stack</p>
<h1>Track supplements and body signals beside the training plan.</h1>
<p>Plan pre-workout, intra-workout, post-workout, daily, and evening supplements. Pair that with weight, body fat, sleep, resting heart rate, mood, and notes.</p>
<a class="site-primary" href="/signup.php">Build a recovery log</a>
<p class="site-eyebrow">NIH/NLM research links</p>
<h1>Connect medications and conditions to study-search starting points.</h1>
<p>Use curated categories and links into NIH/NLM systems to begin research on studies related to the medicines listed in the catalog.</p>
<a class="site-primary" href="/medical.php">Open terminology database</a>
</div>
<figure>
<img src="<?= htmlspecialchars($photo, ENT_QUOTES) ?>" alt="Runner training outdoors">
@@ -26,23 +27,24 @@ require __DIR__ . '/partials/site-header.php';
</figure>
</section>
<section class="detail-grid">
<article>
<h2>Supplement library</h2>
<p>Start with creatine, caffeine, citrulline, electrolytes, whey, casein, magnesium, omega-3s, greens, and more.</p>
</article>
<article>
<h2>Timing windows</h2>
<p>Separate pre-workout, intra-workout, post-workout, daily, and evening routines so supplement habits stay clear.</p>
</article>
<article>
<h2>Health metrics</h2>
<p>Log weight, sleep, resting heart rate, body fat, mood, and notes to understand how training is landing.</p>
</article>
<article>
<h2>Custom additions</h2>
<p>Add custom supplements and doses as your recovery plan evolves.</p>
</article>
<section class="admin-public-shell">
<div class="term-table">
<?php foreach ($studies as $study): ?>
<article>
<div>
<h3><?= htmlspecialchars($study['title'], ENT_QUOTES) ?></h3>
<span><?= htmlspecialchars($study['category'] . ' / ' . $study['related_medicine'], ENT_QUOTES) ?></span>
</div>
<p><?= htmlspecialchars($study['summary'], ENT_QUOTES) ?></p>
<p><?= htmlspecialchars($study['study_type'], ENT_QUOTES) ?></p>
<div class="source-links">
<a href="<?= htmlspecialchars($study['source_url'], ENT_QUOTES) ?>"><?= htmlspecialchars($study['nih_source'], ENT_QUOTES) ?></a>
<a href="<?= htmlspecialchars($study['pubmed_url'], ENT_QUOTES) ?>">PubMed</a>
<a href="<?= htmlspecialchars($study['clinicaltrials_url'], ENT_QUOTES) ?>">ClinicalTrials.gov</a>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
</main>
<?php require __DIR__ . '/partials/site-footer.php'; ?>
+6 -6
View File
@@ -4,7 +4,7 @@ declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
app_db();
$pageTitle = 'Sign Up | Neon Health Tracker';
$pageTitle = 'Sign Up | Neon Medical Tracker';
$activePage = 'signup';
require __DIR__ . '/partials/site-header.php';
?>
@@ -12,12 +12,12 @@ require __DIR__ . '/partials/site-header.php';
<section class="signup-layout">
<div class="signup-copy">
<p class="site-eyebrow">Create your profile</p>
<h1>Start with the basics. Tune the plan later.</h1>
<p>Your profile keeps workouts, nutrition goals, supplement logs, health metrics, and theme preferences separate from every other user.</p>
<h1>Start with the basics. Add records as care changes.</h1>
<p>Your profile keeps medications, vitals, labs, symptoms, allergies, appointments, vaccines, notes, and theme preferences separate from every other user.</p>
<div class="signup-steps">
<article><strong>1</strong><span>Create profile</span></article>
<article><strong>2</strong><span>Open tracker</span></article>
<article><strong>3</strong><span>Add routines and targets</span></article>
<article><strong>3</strong><span>Add medical records</span></article>
</div>
</div>
@@ -29,12 +29,12 @@ require __DIR__ . '/partials/site-header.php';
<label class="site-field"><span>Name</span><input name="name" required autocomplete="name"></label>
<label class="site-field"><span>Email</span><input name="email" type="email" autocomplete="email"></label>
<label class="site-field"><span>Phone</span><input name="phone" type="tel" autocomplete="tel"></label>
<label class="site-field"><span>Fitness focus</span><input name="fitness_focus" placeholder="Strength, endurance, recovery"></label>
<label class="site-field"><span>Care focus</span><input name="fitness_focus" placeholder="Blood pressure, glucose, asthma"></label>
<div class="signup-row">
<label class="site-field"><span>Birthday</span><input name="birthday" type="date"></label>
<label class="site-field"><span>Height in</span><input name="height_in" type="number" min="0" step="0.5"></label>
</div>
<label class="site-field"><span>Training level</span><select name="training_level"><option></option><option>Beginner</option><option>Intermediate</option><option>Advanced</option><option>Competitive</option></select></label>
<label class="site-field"><span>Tracking comfort</span><select name="training_level"><option></option><option>Getting started</option><option>Comfortable</option><option>Advanced</option><option>Caregiver</option></select></label>
<div class="signup-row">
<label class="site-field"><span>Theme</span><select name="theme_mode"><option>dark</option><option>light</option></select></label>
<label class="site-field"><span>Accent</span><select name="theme_accent"><option>green</option><option>blue</option><option>red</option><option>pink</option><option>orange</option></select></label>
+233 -5
View File
@@ -9,7 +9,7 @@ app_db();
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Neon Health Tracker</title>
<title>Neon Medical Tracker</title>
<link rel="stylesheet" href="/assets/styles.css">
</head>
<body data-theme="dark" data-accent="green">
@@ -22,7 +22,7 @@ app_db();
<span></span>
</div>
<div>
<h1>Neon Health Tracker</h1>
<h1>Neon Medical Tracker</h1>
<p id="profileFocus">Loading profile</p>
</div>
</div>
@@ -64,6 +64,96 @@ app_db();
<section class="summary-grid" id="summaryGrid" aria-live="polite"></section>
<section class="workspace-grid medical-workspace">
<div class="panel planner-panel">
<div class="panel-heading">
<div>
<p class="eyebrow">Medication Plan</p>
<h2>Medication Log</h2>
</div>
<button class="text-button" type="button" data-jump="#medicationLogForm">Log medication</button>
</div>
<div class="timeline" id="medicationLogList"></div>
</div>
<div class="panel">
<div class="panel-heading">
<div>
<p class="eyebrow">Vitals</p>
<h2>Latest Readings</h2>
</div>
<button class="text-button" type="button" data-jump="#vitalLogForm">Log vitals</button>
</div>
<div class="nutrition-list" id="vitalLogList"></div>
</div>
<div class="panel">
<div class="panel-heading">
<div>
<p class="eyebrow">Labs</p>
<h2>Lab Results</h2>
</div>
<button class="text-button" type="button" data-jump="#labResultForm">Log lab</button>
</div>
<div class="nutrition-list" id="labResultList"></div>
</div>
<div class="panel">
<div class="panel-heading">
<div>
<p class="eyebrow">Symptoms</p>
<h2>Symptom Journal</h2>
</div>
<button class="text-button" type="button" data-jump="#symptomLogForm">Log symptom</button>
</div>
<div class="nutrition-list" id="symptomLogList"></div>
</div>
<div class="panel">
<div class="panel-heading">
<div>
<p class="eyebrow">Safety</p>
<h2>Allergies</h2>
</div>
<button class="text-button" type="button" data-jump="#allergyRecordForm">Add allergy</button>
</div>
<div class="nutrition-list" id="allergyRecordList"></div>
</div>
<div class="panel">
<div class="panel-heading">
<div>
<p class="eyebrow">Care Plan</p>
<h2>Appointments</h2>
</div>
<button class="text-button" type="button" data-jump="#appointmentForm">Add appointment</button>
</div>
<div class="nutrition-list" id="appointmentList"></div>
</div>
<div class="panel">
<div class="panel-heading">
<div>
<p class="eyebrow">Prevention</p>
<h2>Immunizations</h2>
</div>
<button class="text-button" type="button" data-jump="#immunizationRecordForm">Add vaccine</button>
</div>
<div class="nutrition-list" id="immunizationRecordList"></div>
</div>
<div class="panel">
<div class="panel-heading">
<div>
<p class="eyebrow">Reference</p>
<h2>NIH/NLM Studies</h2>
</div>
<a class="text-button" href="/medical.php">Open database</a>
</div>
<div class="supplement-list" id="studyList"></div>
</div>
</section>
<section class="panel admin-panel" id="adminPanel" hidden>
<div class="panel-heading">
<div>
@@ -92,10 +182,10 @@ app_db();
<label class="field"><span>Phone</span><input name="phone" type="tel"></label>
<label class="field"><span>Birthday</span><input name="birthday" type="date"></label>
<label class="field"><span>Height in</span><input name="height_in" type="number" min="0" step="0.5"></label>
<label class="field"><span>Training level</span><select name="training_level"><option></option><option>Beginner</option><option>Intermediate</option><option>Advanced</option><option>Competitive</option></select></label>
<label class="field"><span>Tracking comfort</span><select name="training_level"><option></option><option>Getting started</option><option>Comfortable</option><option>Advanced</option><option>Caregiver</option></select></label>
<label class="field"><span>Theme</span><select name="theme_mode"><option>dark</option><option>light</option></select></label>
<label class="field"><span>Accent</span><select name="theme_accent"><option>green</option><option>blue</option><option>red</option><option>pink</option><option>orange</option></select></label>
<label class="field wide"><span>Fitness focus</span><input name="fitness_focus" placeholder="Strength, endurance, recovery"></label>
<label class="field wide"><span>Care focus</span><input name="fitness_focus" placeholder="Blood pressure, glucose, asthma"></label>
<label class="field wide"><span>Emergency contact</span><input name="emergency_contact" placeholder="Name and phone"></label>
</div>
<button class="primary-button" type="submit">Save profile</button>
@@ -107,7 +197,7 @@ app_db();
<div class="panel planner-panel">
<div class="panel-heading">
<div>
<p class="eyebrow">Planner</p>
<p class="eyebrow">Legacy Fitness</p>
<h2>Workout Schedule</h2>
</div>
<button class="text-button" type="button" data-jump="#workoutForm">Add workout</button>
@@ -150,6 +240,144 @@ app_db();
</section>
<section class="forms-grid" aria-label="Tracker forms">
<form class="panel form-panel" id="medicationLogForm">
<div class="panel-heading">
<div>
<p class="eyebrow">Medication</p>
<h2>Log Medication</h2>
</div>
</div>
<div class="form-grid two">
<label class="field"><span>Date</span><input name="taken_on" type="date" required></label>
<label class="field"><span>Medication</span><select name="medication_id"></select></label>
<label class="field"><span>Name</span><input name="medication_name" placeholder="Custom medicine"></label>
<label class="field"><span>Dose</span><input name="dose" placeholder="500 mg"></label>
<label class="field"><span>Route</span><input name="route" placeholder="oral"></label>
<label class="field"><span>Frequency</span><input name="frequency" placeholder="twice daily"></label>
<label class="field"><span>Status</span><select name="status"><option>planned</option><option>taken</option><option>skipped</option><option>stopped</option></select></label>
<label class="field"><span>Prescriber</span><input name="prescriber"></label>
<label class="field wide"><span>Notes</span><textarea name="notes" rows="3"></textarea></label>
</div>
<button class="primary-button" type="submit">Save medication</button>
</form>
<form class="panel form-panel" id="vitalLogForm">
<div class="panel-heading">
<div>
<p class="eyebrow">Vitals</p>
<h2>Log Vitals</h2>
</div>
</div>
<div class="form-grid two">
<label class="field"><span>Date</span><input name="measured_on" type="date" required></label>
<label class="field"><span>Blood pressure</span><input name="systolic" type="number" min="0" placeholder="Systolic"></label>
<label class="field"><span>Diastolic</span><input name="diastolic" type="number" min="0"></label>
<label class="field"><span>Pulse</span><input name="pulse" type="number" min="0"></label>
<label class="field"><span>Temp F</span><input name="temperature_f" type="number" min="0" step="0.1"></label>
<label class="field"><span>Oxygen %</span><input name="oxygen_pct" type="number" min="0" max="100" step="0.1"></label>
<label class="field"><span>Glucose mg/dL</span><input name="glucose_mg_dl" type="number" min="0" step="1"></label>
<label class="field"><span>Pain 0-10</span><input name="pain_score" type="number" min="0" max="10" step="1"></label>
<label class="field wide"><span>Symptoms</span><input name="symptoms" placeholder="Cough, dizziness, none"></label>
<label class="field wide"><span>Notes</span><textarea name="notes" rows="3"></textarea></label>
</div>
<button class="primary-button" type="submit">Save vitals</button>
</form>
<form class="panel form-panel" id="labResultForm">
<div class="panel-heading">
<div>
<p class="eyebrow">Labs</p>
<h2>Log Lab Result</h2>
</div>
</div>
<div class="form-grid two">
<label class="field"><span>Date</span><input name="collected_on" type="date" required></label>
<label class="field"><span>Test</span><input name="test_name" placeholder="A1C" required></label>
<label class="field"><span>Result</span><input name="result_value" placeholder="6.2" required></label>
<label class="field"><span>Unit</span><input name="unit" placeholder="%"></label>
<label class="field"><span>Reference range</span><input name="reference_range"></label>
<label class="field"><span>Clinician</span><input name="ordering_clinician"></label>
<label class="field wide"><span>Notes</span><textarea name="notes" rows="3"></textarea></label>
</div>
<button class="primary-button" type="submit">Save lab</button>
</form>
<form class="panel form-panel" id="symptomLogForm">
<div class="panel-heading">
<div>
<p class="eyebrow">Symptoms</p>
<h2>Log Symptom</h2>
</div>
</div>
<div class="form-grid two">
<label class="field"><span>Date</span><input name="observed_on" type="date" required></label>
<label class="field"><span>Symptom</span><input name="symptom" placeholder="Headache" required></label>
<label class="field"><span>Body area</span><input name="body_area" placeholder="Head, chest, abdomen"></label>
<label class="field"><span>Severity 0-10</span><input name="severity" type="number" min="0" max="10" step="1"></label>
<label class="field"><span>Duration</span><input name="duration" placeholder="2 hours"></label>
<label class="field"><span>Triggers</span><input name="triggers" placeholder="Food, activity, stress"></label>
<label class="field wide"><span>Action taken</span><input name="action_taken" placeholder="Rested, called clinic, took medicine"></label>
<label class="field wide"><span>Notes</span><textarea name="notes" rows="3"></textarea></label>
</div>
<button class="primary-button" type="submit">Save symptom</button>
</form>
<form class="panel form-panel" id="allergyRecordForm">
<div class="panel-heading">
<div>
<p class="eyebrow">Safety</p>
<h2>Add Allergy</h2>
</div>
</div>
<div class="form-grid two">
<label class="field"><span>Allergen</span><input name="allergen" placeholder="Penicillin" required></label>
<label class="field"><span>Reaction</span><input name="reaction" placeholder="Rash, hives, swelling"></label>
<label class="field"><span>Severity</span><select name="severity"><option>unknown</option><option>mild</option><option>moderate</option><option>severe</option><option>life-threatening</option></select></label>
<label class="field"><span>First noted</span><input name="first_noted_on" type="date"></label>
<label class="field"><span>Status</span><select name="status"><option>active</option><option>resolved</option></select></label>
<label class="field wide"><span>Notes</span><textarea name="notes" rows="3"></textarea></label>
</div>
<button class="primary-button" type="submit">Save allergy</button>
</form>
<form class="panel form-panel" id="appointmentForm">
<div class="panel-heading">
<div>
<p class="eyebrow">Care Plan</p>
<h2>Add Appointment</h2>
</div>
</div>
<div class="form-grid two">
<label class="field"><span>Date</span><input name="appointment_on" type="date" required></label>
<label class="field"><span>Clinician</span><input name="clinician" placeholder="Dr. Morgan"></label>
<label class="field"><span>Specialty</span><input name="specialty" placeholder="Primary care"></label>
<label class="field"><span>Location</span><input name="location" placeholder="Clinic or telehealth"></label>
<label class="field wide"><span>Reason</span><input name="reason" placeholder="Medication follow-up" required></label>
<label class="field"><span>Status</span><select name="status"><option>scheduled</option><option>completed</option><option>canceled</option></select></label>
<label class="field wide"><span>Follow-up notes</span><textarea name="follow_up_notes" rows="3"></textarea></label>
</div>
<button class="primary-button" type="submit">Save appointment</button>
</form>
<form class="panel form-panel" id="immunizationRecordForm">
<div class="panel-heading">
<div>
<p class="eyebrow">Prevention</p>
<h2>Add Immunization</h2>
</div>
</div>
<div class="form-grid two">
<label class="field"><span>Vaccine</span><input name="vaccine_name" placeholder="Influenza" required></label>
<label class="field"><span>Administered</span><input name="administered_on" type="date" required></label>
<label class="field"><span>Dose</span><input name="dose" placeholder="0.5 mL"></label>
<label class="field"><span>Lot number</span><input name="lot_number"></label>
<label class="field"><span>Site</span><input name="clinician_or_site" placeholder="Pharmacy, clinic"></label>
<label class="field"><span>Next due</span><input name="next_due_on" type="date"></label>
<label class="field wide"><span>Notes</span><textarea name="notes" rows="3"></textarea></label>
</div>
<button class="primary-button" type="submit">Save immunization</button>
</form>
<form class="panel form-panel" id="workoutForm">
<div class="panel-heading">
<div>
+17 -23
View File
@@ -2,23 +2,24 @@
declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
app_db();
$pdo = app_db();
$pageTitle = 'Workout Planning | Neon Health Tracker';
$activePage = 'workouts';
$pageTitle = 'Condition Tracking | Neon Medical Tracker';
$activePage = 'conditions';
$conditions = db_all($pdo, 'SELECT * FROM medical_conditions ORDER BY category, name');
$photo = 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Cycling_in_Amsterdam_%28893%29.jpg/1280px-Cycling_in_Amsterdam_%28893%29.jpg';
require __DIR__ . '/partials/site-header.php';
?>
<main>
<section class="detail-hero">
<div>
<p class="site-eyebrow">Daily, weekly, monthly</p>
<h1>Plan training without losing the details that matter.</h1>
<p>Build routines around weight lifting, cycling, running, swimming, mobility, conditioning, or anything custom. Track volume, duration, distance, intensity, status, and notes from one dashboard.</p>
<p class="site-eyebrow">Condition tracking</p>
<h1>Track readings, symptoms, labs, and medications by condition.</h1>
<p>Use structured logs for blood pressure, glucose, pain, oxygen, temperature, lab results, medications, and notes. The condition library links back to NIH/NLM patient-information sources.</p>
<a class="site-primary" href="/signup.php">Create a profile</a>
</div>
<figure>
<img src="<?= htmlspecialchars($photo, ENT_QUOTES) ?>" alt="Cyclists riding through Amsterdam">
<img src="<?= htmlspecialchars($photo, ENT_QUOTES) ?>" alt="People cycling outdoors">
<figcaption>
Photo: <a href="https://commons.wikimedia.org/wiki/File:Cycling_in_Amsterdam_(893).jpg">Cycling in Amsterdam (893)</a>
by FaceMePLS, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC BY-SA 2.0</a>.
@@ -27,22 +28,15 @@ require __DIR__ . '/partials/site-header.php';
</section>
<section class="detail-grid">
<article>
<h2>Routine templates</h2>
<p>Create recurring programs for strength blocks, endurance weeks, hybrid plans, or recovery cycles.</p>
</article>
<article>
<h2>Workout library</h2>
<p>Start with known movements like squats, deadlifts, bench press, pull-ups, runs, rides, swims, rows, HIIT, and mobility work.</p>
</article>
<article>
<h2>Completion tracking</h2>
<p>Mark workouts as planned, complete, or skipped and watch completion rate update across the selected range.</p>
</article>
<article>
<h2>Volume and cardio totals</h2>
<p>Lift volume, training minutes, and distance are summarized automatically for the current day, week, or month.</p>
</article>
<?php foreach ($conditions as $condition): ?>
<article>
<h2><?= htmlspecialchars($condition['name'], ENT_QUOTES) ?></h2>
<p><strong><?= htmlspecialchars($condition['category'], ENT_QUOTES) ?></strong></p>
<p><?= htmlspecialchars($condition['overview'], ENT_QUOTES) ?></p>
<p><?= htmlspecialchars($condition['common_tracking'], ENT_QUOTES) ?></p>
<a class="site-secondary" href="<?= htmlspecialchars($condition['source_url'], ENT_QUOTES) ?>">MedlinePlus</a>
</article>
<?php endforeach; ?>
</section>
</main>
<?php require __DIR__ . '/partials/site-footer.php'; ?>