- Implemented the symptom database as a full searchable reference area, parallel to terminology.

Added:
New medical_symptoms SQLite catalog with 242 seeded symptom records, 
categories, body systems, tracking notes, urgency-context notes, search 
terms, and MedlinePlus links: [app/bootstrap.php (line 
186)](/Users/tyemeclifford/Documents/GH/workout/app/bootstrap.php:186), 
[seed logic (line 
635)](/Users/tyemeclifford/Documents/GH/workout/app/bootstrap.php:635)
New searchable symptom directory: [public/symptoms.php (line 
1)](/Users/tyemeclifford/Documents/GH/workout/public/symptoms.php:1)
New dynamic per-symptom page, e.g. /symptom.php?id=24: 
[public/symptom.php (line 
1)](/Users/tyemeclifford/Documents/GH/workout/public/symptom.php:1)
Symptom helper/link lookup functions: [app/bootstrap.php (line 
1473)](/Users/tyemeclifford/Documents/GH/workout/app/bootstrap.php:1473)
medicalSymptoms added to the combined library search/API: 
[app/bootstrap.php (line 
2024)](/Users/tyemeclifford/Documents/GH/workout/app/bootstrap.php:2024)
Symptoms added to nav, homepage, and combined medical search: 
[site-header.php (line 
27)](/Users/tyemeclifford/Documents/GH/workout/public/partials/site-header.php:27), 
[index.php (line 
11)](/Users/tyemeclifford/Documents/GH/workout/public/index.php:11), 
[medical.php (line 
13)](/Users/tyemeclifford/Documents/GH/workout/public/medical.php:13)
This commit is contained in:
Ty Clifford
2026-06-30 16:19:09 -04:00
parent b8045eb068
commit 0d492451c7
14 changed files with 638 additions and 352 deletions
-1
View File
@@ -25,7 +25,6 @@ try {
$data = json_input();
$routes = [
'create_user' => 'create_user',
'public_signup' => 'public_signup',
'update_user' => 'update_user',
'delete_user' => 'delete_user',
'update_theme' => 'update_theme',
+4 -155
View File
@@ -105,7 +105,6 @@ p {
.site-logo,
.site-nav,
.site-actions,
.admin-access-row,
.admin-user-main,
.admin-badges,
@@ -206,48 +205,7 @@ p {
width: 100%;
}
.site-hero {
width: min(1440px, calc(100% - 32px));
min-height: min(760px, calc(100vh - 44px));
margin: 12px auto 0;
position: relative;
display: flex;
align-items: flex-end;
overflow: hidden;
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: var(--shadow);
}
.site-hero::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(90deg, rgba(8, 9, 13, 0.92), rgba(8, 9, 13, 0.35) 55%, rgba(8, 9, 13, 0.2));
}
.site-hero img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.site-hero-copy {
position: relative;
z-index: 1;
width: min(780px, 100%);
padding: clamp(24px, 6vw, 72px);
display: grid;
gap: 18px;
}
.site-lede,
.site-hero-copy p:not(.site-eyebrow),
.detail-hero p,
.signup-copy p,
.site-split p,
.site-section-heading p {
color: var(--muted-2);
}
@@ -259,43 +217,18 @@ p {
line-height: 1.1;
}
.site-actions {
gap: 10px;
flex-wrap: wrap;
}
.photo-credit {
position: absolute;
right: 14px;
bottom: 10px;
z-index: 1;
max-width: min(460px, calc(100% - 28px));
color: rgba(255, 255, 255, 0.78);
font-size: 0.75rem;
line-height: 1.35;
}
.photo-credit a,
figcaption a {
color: var(--accent);
}
.site-band,
.site-split,
.cta-strip,
.detail-hero,
.detail-grid,
.signup-layout,
.admin-public-shell,
.site-footer {
width: min(1180px, calc(100% - 32px));
margin: 14px auto 0;
}
.site-band,
.site-split,
.cta-strip,
.signup-layout,
.admin-public-shell,
.site-footer {
border: 1px solid var(--line);
@@ -321,8 +254,6 @@ figcaption a {
.feature-grid article,
.detail-grid article,
.site-metric-stack article,
.signup-steps article,
.term-table article,
.library-row,
.record-field,
@@ -363,8 +294,6 @@ figcaption a {
.feature-grid p,
.detail-grid p,
.term-table p,
.site-metric-stack span,
.signup-steps span,
.details-line,
.timeline-meta {
color: var(--muted);
@@ -566,7 +495,7 @@ figcaption a {
.library-stats {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 10px;
margin-bottom: 18px;
}
@@ -589,39 +518,18 @@ figcaption a {
font-size: 0.82rem;
}
.site-split,
.detail-hero,
.signup-layout {
.detail-hero {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(360px, 0.8fr);
gap: 18px;
align-items: center;
}
.site-split > div:first-child,
.detail-hero > div,
.signup-copy {
.detail-hero > div {
display: grid;
gap: 14px;
}
.site-metric-stack {
display: grid;
gap: 10px;
}
.site-metric-stack article {
display: grid;
gap: 5px;
}
.cta-strip {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
}
.detail-hero {
align-items: center;
}
@@ -647,48 +555,6 @@ figcaption {
padding: 10px;
}
.signup-card {
display: grid;
gap: 12px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: color-mix(in srgb, var(--panel-2), transparent 2%);
padding: 18px;
}
.signup-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.signup-steps {
display: grid;
gap: 9px;
}
.signup-steps article {
display: flex;
align-items: center;
gap: 10px;
}
.signup-steps strong {
width: 30px;
height: 30px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
color: #06100b;
background: var(--accent);
}
.signup-result {
min-height: 24px;
color: var(--muted-2);
}
.site-field,
.field {
display: grid;
@@ -936,7 +802,7 @@ textarea:focus {
@media (max-width: 1020px) {
.site-header,
.site-footer,
.cta-strip {
.footer-links {
align-items: stretch;
flex-direction: column;
}
@@ -951,9 +817,7 @@ textarea:focus {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.site-split,
.detail-hero,
.signup-layout,
.admin-layout,
.admin-source-form {
grid-template-columns: 1fr;
@@ -966,32 +830,17 @@ textarea:focus {
@media (max-width: 640px) {
.site-header,
.site-hero,
.site-band,
.site-split,
.cta-strip,
.detail-hero,
.detail-grid,
.signup-layout,
.admin-public-shell,
.site-footer {
width: min(100% - 20px, 620px);
}
.site-hero {
min-height: 720px;
}
.site-hero-copy {
padding: 22px;
padding-bottom: 74px;
}
.feature-grid,
.detail-grid,
.admin-stats,
.library-stats,
.signup-row,
.form-grid.two {
grid-template-columns: 1fr;
}
-43
View File
@@ -1,43 +0,0 @@
const siteForm = document.querySelector("#publicSignupForm");
const siteResult = document.querySelector("#signupResult");
function showSignupMessage(message) {
if (siteResult) {
siteResult.textContent = message;
}
}
async function siteApi(action, payload) {
const response = await fetch(`/api.php?action=${encodeURIComponent(action)}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
const json = await response.json();
if (!response.ok || json.error) {
throw new Error(json.error || "Request failed.");
}
return json;
}
if (siteForm) {
siteForm.addEventListener("submit", async (event) => {
event.preventDefault();
const payload = Object.fromEntries(new FormData(siteForm).entries());
showSignupMessage("Creating profile...");
try {
const result = await siteApi("public_signup", payload);
if (result.user?.id) {
localStorage.setItem("nht:userId", String(result.user.id));
}
siteForm.reset();
const adminCopy = result.first_admin ? " You are the first user, so this profile is an admin." : "";
showSignupMessage(`Profile created.${adminCopy} Opening tracker...`);
window.setTimeout(() => {
window.location.href = "/tracker.php";
}, 900);
} catch (error) {
showSignupMessage(error.message);
}
});
}
+76 -50
View File
@@ -2,81 +2,107 @@
declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
app_db();
$pdo = app_db();
$pageTitle = 'Neon Medical Tracker';
$pageTitle = 'Medical Reference Library | 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';
$totalMedicines = (int) db_value($pdo, 'SELECT COUNT(*) FROM medication_catalog');
$totalTerms = (int) db_value($pdo, 'SELECT COUNT(*) FROM medical_terms');
$totalSymptoms = (int) db_value($pdo, 'SELECT COUNT(*) FROM medical_symptoms');
$totalConditions = (int) db_value($pdo, 'SELECT COUNT(*) FROM medical_conditions');
$totalStudies = (int) db_value($pdo, 'SELECT COUNT(*) FROM research_studies');
require __DIR__ . '/partials/site-header.php';
?>
<main>
<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">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="/medical.php">Explore database</a>
</div>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Medical reference</p>
<h1>Medicines, symptoms, terminology, conditions, and NIH/NLM study links.</h1>
<p>Search the reference database or open the tracker/admin tools for profile-managed medical logs.</p>
</div>
<form class="library-search" method="get" action="/medical.php">
<label>
<span>Search medical library</span>
<input name="q" placeholder="Try warfarin, CBC, blood pressure, metformin, asthma">
</label>
<button class="site-primary" type="submit">Search</button>
</form>
<div class="library-stats" aria-label="Library totals">
<article><strong><?= number_format($totalMedicines) ?></strong><span>medicines</span></article>
<article><strong><?= number_format($totalSymptoms) ?></strong><span>symptoms</span></article>
<article><strong><?= number_format($totalTerms) ?></strong><span>terms</span></article>
<article><strong><?= number_format($totalConditions) ?></strong><span>conditions</span></article>
<article><strong><?= number_format($totalStudies) ?></strong><span>study categories</span></article>
</div>
<p class="photo-credit">
Photo: <a href="https://commons.wikimedia.org/wiki/File:Jogging_Woman_in_Grass.jpg">Jogging Woman in Grass</a>
by Mike Baird, <a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a>.
</p>
</section>
<section class="site-band">
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Built for daily use</p>
<h2>One workspace for personal health records and sourced reference links.</h2>
<p class="site-eyebrow">Reference sections</p>
<h2>Open a database area.</h2>
</div>
<div class="feature-grid">
<article>
<span>01</span>
<h3>Medication tracking</h3>
<p>Log prescription and over-the-counter medicines by date, dose, route, frequency, status, prescriber, and notes.</p>
<span>Rx</span>
<h3>Medicines</h3>
<p>Search by generic name, chemical name, active ingredient, brand name, or medication class.</p>
<a class="site-secondary" href="/nutrition.php">Open medicines</a>
</article>
<article>
<span>02</span>
<h3>Vitals and symptoms</h3>
<p>Track blood pressure, pulse, temperature, oxygen saturation, glucose, pain scores, symptoms, and context.</p>
<span>ABC</span>
<h3>Terminology</h3>
<p>Browse plain-language medical terms with clinical context and reference links.</p>
<a class="site-secondary" href="/medical.php">Open terminology</a>
</article>
<article>
<span>03</span>
<h3>Labs and terminology</h3>
<p>Store lab values and browse plain-language medical terms connected to MedlinePlus, RxNorm, PubMed, and ClinicalTrials.gov.</p>
<span>Sx</span>
<h3>Symptoms</h3>
<p>Search symptom names, body systems, tracking notes, and urgency-context references.</p>
<a class="site-secondary" href="/symptoms.php">Open symptoms</a>
</article>
<article>
<span>04</span>
<h3>NIH/NLM studies</h3>
<p>Browse NIH/NLM research categories and study-search links related to the medicines included in the catalog.</p>
<span>NIH</span>
<h3>Research</h3>
<p>Open NIH/NLM, PubMed, and ClinicalTrials.gov study categories tied to medicines.</p>
<a class="site-secondary" href="/recovery.php">Open research</a>
</article>
<article>
<span>Dx</span>
<h3>Conditions</h3>
<p>Review condition-tracking topics for vitals, labs, symptoms, medications, and notes.</p>
<a class="site-secondary" href="/workouts.php">Open conditions</a>
</article>
</div>
</section>
<section class="site-split">
<div>
<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, 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>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Profile tools</p>
<h2>Manage medical logs through Tracker and Admin.</h2>
</div>
<div class="site-metric-stack">
<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 class="library-list">
<article class="library-row">
<div class="library-row-main">
<h3>Tracker</h3>
<p>Use existing profiles for medications, vitals, labs, symptoms, allergies, appointments, immunizations, and notes.</p>
</div>
<div class="library-row-actions">
<a class="site-primary" href="/tracker.php">Open tracker</a>
</div>
</article>
<article class="library-row">
<div class="library-row-main">
<h3>Admin</h3>
<p>Create, update, activate, deactivate, and remove profiles from the admin dashboard.</p>
</div>
<div class="library-row-actions">
<a class="site-secondary" href="/admin.php">Manage profiles</a>
</div>
</article>
</div>
</section>
<section class="cta-strip">
<div>
<p class="site-eyebrow">Ready when you are</p>
<h2>Create your profile and open the medical tracker.</h2>
</div>
<a class="site-primary" href="/signup.php">Sign up</a>
</section>
</main>
<?php require __DIR__ . '/partials/site-footer.php'; ?>
+36 -2
View File
@@ -10,9 +10,11 @@ $searchQuery = trim((string) ($_GET['q'] ?? ''));
$library = library_search_payload(['q' => $searchQuery, 'limit' => 500]);
$terms = $library['medicalTerms'];
$conditions = $library['medicalConditions'];
$symptoms = $library['medicalSymptoms'];
$medicines = $library['medicationCatalog'];
$studies = $library['researchStudies'];
$totalMedicines = (int) db_value($pdo, 'SELECT COUNT(*) FROM medication_catalog');
$totalSymptoms = (int) db_value($pdo, 'SELECT COUNT(*) FROM medical_symptoms');
require __DIR__ . '/partials/site-header.php';
?>
@@ -20,14 +22,14 @@ require __DIR__ . '/partials/site-header.php';
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Searchable medical library</p>
<h1>Search terms, conditions, medicines, chemical names, and brands.</h1>
<h1>Search symptoms, terms, conditions, medicines, chemical names, and brands.</h1>
<p>Entries are concise app-authored summaries with links to NIH/NLM systems such as MedlinePlus, RxNorm, DailyMed, PubChem, PubMed, and ClinicalTrials.gov. This is a tracking/reference aid, not medical advice.</p>
</div>
<form class="library-search" method="get" action="/medical.php">
<label>
<span>Search medical library</span>
<input name="q" value="<?= htmlspecialchars($searchQuery, ENT_QUOTES) ?>" placeholder="Try Lipitor, atorvastatin, blood pressure, CBC, metformin">
<input name="q" value="<?= htmlspecialchars($searchQuery, ENT_QUOTES) ?>" placeholder="Try chest pain, rash, Lipitor, blood pressure, CBC, metformin">
</label>
<button class="site-primary" type="submit">Search</button>
<?php if ($searchQuery !== ''): ?>
@@ -37,6 +39,7 @@ require __DIR__ . '/partials/site-header.php';
<div class="library-stats" aria-label="Search result counts">
<article><strong><?= count($medicines) ?></strong><span><?= $searchQuery === '' ? 'medicines shown of ' . number_format($totalMedicines) : 'medicine results' ?></span></article>
<article><strong><?= count($symptoms) ?></strong><span><?= $searchQuery === '' ? 'symptoms shown of ' . number_format($totalSymptoms) : 'symptom results' ?></span></article>
<article><strong><?= count($terms) ?></strong><span>terms</span></article>
<article><strong><?= count($conditions) ?></strong><span>conditions</span></article>
<article><strong><?= count($studies) ?></strong><span>study links</span></article>
@@ -61,6 +64,37 @@ require __DIR__ . '/partials/site-header.php';
<?php endif; ?>
</section>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Symptoms</p>
<h2>Symptom reference records</h2>
</div>
<?php if (!$symptoms): ?>
<article class="empty-state">
<h3>No symptoms found.</h3>
<p>Try a body system, symptom name, sign, or tracking phrase.</p>
</article>
<?php else: ?>
<div class="library-list">
<?php foreach ($symptoms as $symptom): ?>
<article class="library-row">
<div class="library-row-main">
<h3><?= htmlspecialchars((string) $symptom['name'], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars((string) $symptom['plain_language_description'], ENT_QUOTES) ?></p>
<div class="library-row-meta">
<span><?= htmlspecialchars((string) $symptom['category'], ENT_QUOTES) ?></span>
<span><?= htmlspecialchars((string) ($symptom['body_system'] ?: 'Symptom'), ENT_QUOTES) ?></span>
</div>
</div>
<div class="library-row-actions">
<a class="site-primary" href="<?= htmlspecialchars(symptom_detail_path($symptom), ENT_QUOTES) ?>">View symptom</a>
</div>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Terminology</p>
-2
View File
@@ -4,11 +4,9 @@
<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 medical tracker</a>
<a href="/admin.php">Admin dashboard</a>
</div>
</footer>
<script src="/assets/site.js" defer></script>
</body>
</html>
+1 -1
View File
@@ -24,9 +24,9 @@ function site_active(string $page, string $activePage): string
<a<?= site_active('home', $activePage) ?> href="/">Home</a>
<a<?= site_active('conditions', $activePage) ?> href="/workouts.php">Conditions</a>
<a<?= site_active('medicines', $activePage) ?> href="/nutrition.php">Medicines</a>
<a<?= site_active('symptoms', $activePage) ?> href="/symptoms.php">Symptoms</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>
</nav>
+2 -44
View File
@@ -1,47 +1,5 @@
<?php
declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
app_db();
$pageTitle = 'Sign Up | Neon Medical Tracker';
$activePage = 'signup';
require __DIR__ . '/partials/site-header.php';
?>
<main>
<section class="signup-layout">
<div class="signup-copy">
<p class="site-eyebrow">Create your profile</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 medical records</span></article>
</div>
</div>
<form class="signup-card" id="publicSignupForm">
<div>
<p class="site-eyebrow">Sign up</p>
<h2>New profile</h2>
</div>
<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>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>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>
</div>
<button class="site-primary full" type="submit">Create profile</button>
<p class="signup-result" id="signupResult" role="status" aria-live="polite"></p>
</form>
</section>
</main>
<?php require __DIR__ . '/partials/site-footer.php'; ?>
header('Location: /tracker.php', true, 302);
exit;
+149
View File
@@ -0,0 +1,149 @@
<?php
declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
$pdo = app_db();
$symptom = find_medical_symptom_item($pdo, $_GET);
$pageTitle = $symptom
? (string) $symptom['name'] . ' | Symptom Detail | Neon Medical Tracker'
: 'Symptom Not Found | Neon Medical Tracker';
$activePage = 'symptoms';
$relatedTerms = [];
$relatedConditions = [];
$relatedMedicines = [];
$relatedStudies = [];
if ($symptom) {
$related = library_search_payload(['q' => (string) $symptom['name'], 'limit' => 8]);
$relatedTerms = $related['medicalTerms'];
$relatedConditions = $related['medicalConditions'];
$relatedMedicines = $related['medicationCatalog'];
$relatedStudies = $related['researchStudies'];
} else {
http_response_code(404);
}
require __DIR__ . '/partials/site-header.php';
?>
<main>
<?php if (!$symptom): ?>
<section class="admin-public-shell record-shell">
<p class="site-eyebrow">Symptom database</p>
<h1>Symptom not found.</h1>
<p>The requested symptom link does not match a current database record.</p>
<a class="site-primary" href="/symptoms.php">Search symptoms</a>
</section>
<?php else: ?>
<section class="admin-public-shell record-shell">
<div class="record-heading">
<div>
<p class="site-eyebrow">Symptom detail</p>
<h1><?= htmlspecialchars((string) $symptom['name'], ENT_QUOTES) ?></h1>
<p><?= htmlspecialchars((string) $symptom['plain_language_description'], ENT_QUOTES) ?></p>
</div>
<div class="record-actions">
<a class="site-primary" href="/tracker.php">Open tracker</a>
<a class="site-secondary" href="/symptoms.php?q=<?= rawurlencode((string) $symptom['name']) ?>">Symptom search</a>
</div>
</div>
<div class="record-meta">
<span><?= htmlspecialchars((string) $symptom['category'], ENT_QUOTES) ?></span>
<span><?= htmlspecialchars((string) ($symptom['body_system'] ?: 'Symptom'), ENT_QUOTES) ?></span>
<span>ID <?= htmlspecialchars((string) $symptom['id'], ENT_QUOTES) ?></span>
</div>
</section>
<section class="admin-public-shell">
<div class="record-detail-grid">
<article class="record-field wide">
<span>Common Tracking</span>
<p><?= htmlspecialchars((string) $symptom['common_tracking'], ENT_QUOTES) ?></p>
</article>
<article class="record-field wide">
<span>Urgency Context</span>
<p><?= htmlspecialchars((string) $symptom['urgency_note'], ENT_QUOTES) ?></p>
</article>
<article class="record-field wide">
<span>Source</span>
<div class="source-links">
<a href="<?= htmlspecialchars((string) $symptom['source_url'], ENT_QUOTES) ?>"><?= htmlspecialchars((string) $symptom['source_name'], ENT_QUOTES) ?></a>
</div>
</article>
</div>
</section>
<?php if ($relatedTerms || $relatedConditions || $relatedMedicines || $relatedStudies): ?>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Connected records</p>
<h2>Database matches for this symptom</h2>
</div>
<div class="library-list">
<?php foreach ($relatedTerms as $term): ?>
<article class="library-row">
<div class="library-row-main">
<h3><?= htmlspecialchars((string) $term['term'], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars((string) $term['plain_language_definition'], ENT_QUOTES) ?></p>
<div class="library-row-meta">
<span><?= htmlspecialchars((string) $term['category'], ENT_QUOTES) ?></span>
</div>
</div>
<div class="library-row-actions">
<a class="site-secondary" href="<?= htmlspecialchars(term_detail_path($term), ENT_QUOTES) ?>">View term</a>
</div>
</article>
<?php endforeach; ?>
<?php foreach ($relatedConditions as $condition): ?>
<article class="library-row">
<div class="library-row-main">
<h3><?= htmlspecialchars((string) $condition['name'], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars((string) $condition['overview'], ENT_QUOTES) ?></p>
<div class="library-row-meta">
<span><?= htmlspecialchars((string) $condition['category'], ENT_QUOTES) ?></span>
</div>
</div>
<div class="library-row-actions">
<a class="site-secondary" href="<?= htmlspecialchars((string) $condition['source_url'], ENT_QUOTES) ?>">Source</a>
</div>
</article>
<?php endforeach; ?>
<?php foreach ($relatedMedicines as $medicine): ?>
<article class="library-row">
<div class="library-row-main">
<h3><?= htmlspecialchars(ucfirst((string) $medicine['generic_name']), ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars((string) ($medicine['common_use'] ?: $medicine['class_name']), ENT_QUOTES) ?></p>
<div class="library-row-meta">
<span><?= htmlspecialchars((string) ($medicine['class_name'] ?: 'Medication'), ENT_QUOTES) ?></span>
</div>
</div>
<div class="library-row-actions">
<a class="site-secondary" href="<?= htmlspecialchars(medicine_detail_path($medicine), ENT_QUOTES) ?>">View medicine</a>
</div>
</article>
<?php endforeach; ?>
<?php foreach ($relatedStudies as $study): ?>
<article class="library-row">
<div class="library-row-main">
<h3><?= htmlspecialchars((string) $study['title'], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars((string) $study['summary'], ENT_QUOTES) ?></p>
<div class="library-row-meta">
<span><?= htmlspecialchars((string) $study['category'], ENT_QUOTES) ?></span>
<span><?= htmlspecialchars((string) $study['related_medicine'], ENT_QUOTES) ?></span>
</div>
</div>
<div class="library-row-actions">
<a class="site-secondary" href="<?= htmlspecialchars((string) $study['source_url'], ENT_QUOTES) ?>">Study link</a>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<?php endif; ?>
</main>
<?php require __DIR__ . '/partials/site-footer.php'; ?>
+72
View File
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
require __DIR__ . '/../app/bootstrap.php';
$pdo = app_db();
$pageTitle = 'Symptom Database | Neon Medical Tracker';
$activePage = 'symptoms';
$searchQuery = trim((string) ($_GET['q'] ?? ''));
$library = library_search_payload(['q' => $searchQuery, 'limit' => 500]);
$symptoms = $library['medicalSymptoms'];
$totalSymptoms = (int) db_value($pdo, 'SELECT COUNT(*) FROM medical_symptoms');
require __DIR__ . '/partials/site-header.php';
?>
<main>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Symptom database</p>
<h1>Search symptoms by name, body system, tracking context, and urgency notes.</h1>
<p>These entries are reference summaries with MedlinePlus source links. They support tracking and discussion, not diagnosis or treatment decisions.</p>
</div>
<form class="library-search" method="get" action="/symptoms.php">
<label>
<span>Search symptoms</span>
<input name="q" value="<?= htmlspecialchars($searchQuery, ENT_QUOTES) ?>" placeholder="Try chest pain, rash, nausea, dizziness, urinary, sleep">
</label>
<button class="site-primary" type="submit">Search</button>
<?php if ($searchQuery !== ''): ?>
<a class="site-secondary" href="/symptoms.php">Clear</a>
<?php endif; ?>
</form>
<div class="library-stats" aria-label="Symptom result count">
<article><strong><?= count($symptoms) ?></strong><span><?= $searchQuery === '' ? 'shown of ' . number_format($totalSymptoms) . ' symptoms' : 'symptom results' ?></span></article>
</div>
</section>
<section class="admin-public-shell">
<div class="site-section-heading">
<p class="site-eyebrow">Catalog directory</p>
<h2>Symptom records</h2>
</div>
<?php if (!$symptoms): ?>
<article class="empty-state">
<h3>No symptoms found.</h3>
<p>Try a body system, symptom name, sign, or tracking phrase.</p>
</article>
<?php else: ?>
<div class="library-list">
<?php foreach ($symptoms as $symptom): ?>
<article class="library-row">
<div class="library-row-main">
<h3><?= htmlspecialchars((string) $symptom['name'], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars((string) $symptom['plain_language_description'], ENT_QUOTES) ?></p>
<div class="library-row-meta">
<span><?= htmlspecialchars((string) $symptom['category'], ENT_QUOTES) ?></span>
<span><?= htmlspecialchars((string) ($symptom['body_system'] ?: 'Symptom'), ENT_QUOTES) ?></span>
</div>
</div>
<div class="library-row-actions">
<a class="site-primary" href="<?= htmlspecialchars(symptom_detail_path($symptom), ENT_QUOTES) ?>">View symptom</a>
</div>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>
</main>
<?php require __DIR__ . '/partials/site-footer.php'; ?>
+1 -1
View File
@@ -16,7 +16,7 @@ require __DIR__ . '/partials/site-header.php';
<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>
<a class="site-primary" href="/tracker.php">Open tracker</a>
</div>
<figure>
<img src="<?= htmlspecialchars($photo, ENT_QUOTES) ?>" alt="People cycling outdoors">