mb v1
This commit is contained in:
parent
bb9eef0fb8
commit
1cb0064488
@ -9,10 +9,15 @@
|
||||
--success: #10b981;
|
||||
--info: #06b6d4;
|
||||
--warning: #f59e0b;
|
||||
--radius: 12px;
|
||||
--radius: 16px;
|
||||
--card-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
||||
background-color: var(--bg);
|
||||
@ -20,19 +25,87 @@ body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
overflow-x: hidden;
|
||||
/* Prevent rubber-banding on iOS if needed, but usually better to let it be */
|
||||
}
|
||||
|
||||
.container-mobile {
|
||||
/* App Shell Structure */
|
||||
.app-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 16px;
|
||||
padding-bottom: 80px; /* footer space */
|
||||
background: var(--bg);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 24px;
|
||||
.app-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
background: rgba(15, 23, 42, 0.8);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding-top: calc(8px + env(safe-area-inset-top));
|
||||
}
|
||||
|
||||
.app-content {
|
||||
flex: 1;
|
||||
padding-bottom: calc(90px + env(safe-area-inset-bottom)) !important;
|
||||
}
|
||||
|
||||
/* Bottom Navigation */
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
height: calc(70px + env(safe-area-inset-bottom));
|
||||
background: rgba(30, 41, 59, 0.95);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
z-index: 1050;
|
||||
}
|
||||
|
||||
.bottom-nav .nav-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.65rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.bottom-nav .nav-item svg {
|
||||
margin-bottom: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.bottom-nav .nav-item.active {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.bottom-nav .nav-item.active svg {
|
||||
transform: translateY(-2px);
|
||||
stroke: var(--primary);
|
||||
}
|
||||
|
||||
/* Cards & Stats */
|
||||
.card-stat {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
@ -40,7 +113,6 @@ header {
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: var(--card-shadow);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
@ -59,9 +131,9 @@ header {
|
||||
}
|
||||
|
||||
.progress-thin {
|
||||
height: 6px;
|
||||
height: 8px;
|
||||
background-color: var(--border);
|
||||
border-radius: 4px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
margin-top: 8px;
|
||||
}
|
||||
@ -69,7 +141,7 @@ header {
|
||||
.progress-bar-inner {
|
||||
height: 100%;
|
||||
background-color: var(--primary);
|
||||
transition: width 0.6s cubic-bezier(0.34, 1.56, 0.64, 1), background-color 0.3s ease;
|
||||
transition: width 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.btn-primary-custom {
|
||||
@ -77,114 +149,76 @@ header {
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
padding: 14px 24px;
|
||||
font-weight: 600;
|
||||
padding: 16px 24px;
|
||||
font-weight: 700;
|
||||
width: 100%;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary-custom:hover {
|
||||
background-color: var(--primary-hover);
|
||||
.btn-primary-custom:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.log-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px;
|
||||
padding: 16px;
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius);
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.log-details {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: var(--surface);
|
||||
color: var(--text);
|
||||
border-radius: 20px;
|
||||
border-radius: 24px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background-color: var(--bg);
|
||||
border-radius: var(--radius);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
padding: 12px 16px;
|
||||
padding: 14px 16px;
|
||||
color: var(--text);
|
||||
font-size: 1rem; /* Prevent zoom on iOS */
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
background-color: var(--bg);
|
||||
border-color: var(--primary);
|
||||
color: var(--text);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Tabs styling */
|
||||
.nav-pills {
|
||||
background: var(--surface);
|
||||
padding: 4px;
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
color: var(--text-muted);
|
||||
border-radius: calc(var(--radius) - 4px);
|
||||
font-weight: 600;
|
||||
transition: all 0.2s;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active {
|
||||
background-color: var(--bg);
|
||||
color: var(--primary);
|
||||
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.btn-outline-secondary { border-color: var(--border); color: var(--text-muted); }
|
||||
.btn-outline-secondary:hover { background-color: var(--border); color: var(--text); }
|
||||
|
||||
.bg-success-subtle { background-color: rgba(16, 185, 129, 0.1) !important; }
|
||||
.bg-secondary-subtle { background-color: rgba(51, 65, 85, 0.3) !important; }
|
||||
|
||||
#reminders-row::-webkit-scrollbar { display: none; }
|
||||
|
||||
.btn-xs { padding: 0.25rem 0.5rem; font-size: 0.7rem; }
|
||||
|
||||
/* Custom colors for stats */
|
||||
/* Custom Utilities */
|
||||
.x-small { font-size: 0.75rem; }
|
||||
.text-info { color: var(--info) !important; }
|
||||
.text-warning { color: var(--warning) !important; }
|
||||
.bg-info { background-color: var(--info) !important; }
|
||||
.bg-warning { background-color: var(--warning) !important; }
|
||||
|
||||
canvas {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.x-small { font-size: 0.65rem; }
|
||||
|
||||
#gallery-container .card {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
#gallery-container .card:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
#reminders-row::-webkit-scrollbar { display: none; }
|
||||
|
||||
/* Supplement buttons */
|
||||
.supplement-toggle-btn {
|
||||
transition: all 0.2s;
|
||||
font-size: 0.8rem !important;
|
||||
text-align: center;
|
||||
border-radius: 12px !important;
|
||||
font-weight: 600 !important;
|
||||
padding: 8px 16px !important;
|
||||
}
|
||||
|
||||
.supplement-toggle-btn.btn-success {
|
||||
background-color: var(--success);
|
||||
border-color: var(--success) !important;
|
||||
color: white;
|
||||
/* Gallery adjustments */
|
||||
#gallery-container .card {
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
/* Hide Bootstrap Tab Nav as we use Bottom Nav now */
|
||||
#mainTab {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (min-width: 481px) {
|
||||
body {
|
||||
background-color: #000;
|
||||
}
|
||||
.app-shell {
|
||||
box-shadow: 0 0 40px rgba(0,0,0,0.5);
|
||||
}
|
||||
}
|
||||
@ -14,14 +14,43 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const aiBtnSpinner = document.getElementById('aiBtnSpinner');
|
||||
|
||||
// Tab Elements
|
||||
const analysisTab = document.getElementById('analysis-tab');
|
||||
const galleryTab = document.getElementById('gallery-tab');
|
||||
const periodWeekly = document.getElementById('periodWeekly');
|
||||
const periodMonthly = document.getElementById('periodMonthly');
|
||||
|
||||
let weightChart = null;
|
||||
let caloriesChart = null;
|
||||
|
||||
// --- PWA SERVICE WORKER ---
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('sw.js').then(() => {
|
||||
console.log('Service Worker Registered');
|
||||
});
|
||||
}
|
||||
|
||||
// --- BOTTOM NAV & TAB HANDLING ---
|
||||
const bottomNavItems = document.querySelectorAll('.bottom-nav .nav-item');
|
||||
bottomNavItems.forEach(item => {
|
||||
item.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const tabId = this.dataset.tab;
|
||||
|
||||
// Update UI
|
||||
bottomNavItems.forEach(i => i.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
// Show Tab
|
||||
const tabTrigger = new bootstrap.Tab(document.querySelector(`#${tabId}-tab`));
|
||||
tabTrigger.show();
|
||||
|
||||
// Special handling for specific tabs
|
||||
if (tabId === 'gallery') fetchPhotos();
|
||||
if (tabId === 'analysis') renderAnalysis();
|
||||
|
||||
// Scroll to top
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
});
|
||||
|
||||
// Initial fetch
|
||||
refreshAll();
|
||||
|
||||
@ -243,9 +272,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
|
||||
container.innerHTML = data.map(photo => `
|
||||
<div class="col-6 col-md-4">
|
||||
<div class="col-6">
|
||||
<div class="card bg-dark border-secondary overflow-hidden h-100">
|
||||
<img src="${photo.photo_path}" class="card-img-top" style="height: 180px; object-fit: cover;" alt="Progress">
|
||||
<img src="${photo.photo_path}" class="card-img-top" style="height: 160px; object-fit: cover;" alt="Progress">
|
||||
<div class="card-body p-2">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="x-small fw-bold">${photo.logged_at}</span>
|
||||
@ -306,10 +335,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
}
|
||||
|
||||
// --- TABS SWITCHING ---
|
||||
analysisTab.addEventListener('shown.bs.tab', renderAnalysis);
|
||||
galleryTab.addEventListener('shown.bs.tab', fetchPhotos);
|
||||
|
||||
// --- ANALYSIS ---
|
||||
periodWeekly.addEventListener('change', renderAnalysis);
|
||||
periodMonthly.addEventListener('change', renderAnalysis);
|
||||
|
||||
|
||||
394
index.php
394
index.php
@ -13,15 +13,25 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<html lang="en" data-bs-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover" />
|
||||
<title>Nutrition Pulse</title>
|
||||
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
|
||||
<!-- Mobile / PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="Pulse">
|
||||
<meta name="theme-color" content="#0f172a">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
@ -31,193 +41,170 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container-mobile">
|
||||
<header class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h1 class="h3 fw-bold mb-1">Nutrition Pulse</h1>
|
||||
<p class="text-muted small mb-0"><?= date('l, M d') ?></p>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary btn-sm rounded-circle p-2 border-0" type="button" data-bs-toggle="dropdown">
|
||||
<svg width="20" height="20" fill="currentColor" viewBox="0 0 16 16"><path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/></svg>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end shadow-lg border-0">
|
||||
<li><a class="dropdown-item small py-2" href="#" data-bs-toggle="modal" data-bs-target="#goalsModal">Daily Goals</a></li>
|
||||
<li><a class="dropdown-item small py-2" href="#" data-bs-toggle="modal" data-bs-target="#weightModal">Log Weight</a></li>
|
||||
<li><a class="dropdown-item small py-2" href="#" data-bs-toggle="modal" data-bs-target="#photoModal">Upload Progress Photo</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><p class="dropdown-item-text text-muted mb-0" style="font-size: 0.7rem;">Bulgarian AI support enabled</p></li>
|
||||
</ul>
|
||||
<div class="app-shell">
|
||||
<header class="app-bar px-3 py-2">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h1 class="h5 fw-bold mb-0">Nutrition Pulse</h1>
|
||||
<p class="text-muted x-small mb-0"><?= date('l, F d') ?></p>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary btn-sm rounded-circle p-1 border-0" type="button" data-bs-toggle="dropdown">
|
||||
<svg width="24" height="24" fill="currentColor" viewBox="0 0 16 16"><path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/></svg>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end shadow-lg border-0">
|
||||
<li><a class="dropdown-item small py-2" href="#" data-bs-toggle="modal" data-bs-target="#goalsModal">Daily Goals</a></li>
|
||||
<li><a class="dropdown-item small py-2" href="#" data-bs-toggle="modal" data-bs-target="#weightModal">Log Weight</a></li>
|
||||
<li><a class="dropdown-item small py-2" href="#" data-bs-toggle="modal" data-bs-target="#photoModal">Upload Progress Photo</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><p class="dropdown-item-text text-muted mb-0" style="font-size: 0.7rem;">Bulgarian AI support enabled</p></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<ul class="nav nav-pills mb-4 w-100 p-1" id="mainTab" role="tablist">
|
||||
<li class="nav-item flex-fill" role="presentation">
|
||||
<button class="nav-link active w-100" id="today-tab" data-bs-toggle="pill" data-bs-target="#today" type="button" role="tab">Today</button>
|
||||
</li>
|
||||
<li class="nav-item flex-fill" role="presentation">
|
||||
<button class="nav-link w-100" id="health-tab" data-bs-toggle="pill" data-bs-target="#health" type="button" role="tab">Health</button>
|
||||
</li>
|
||||
<li class="nav-item flex-fill" role="presentation">
|
||||
<button class="nav-link w-100" id="gallery-tab" data-bs-toggle="pill" data-bs-target="#gallery" type="button" role="tab">Gallery</button>
|
||||
</li>
|
||||
<li class="nav-item flex-fill" role="presentation">
|
||||
<button class="nav-link w-100" id="analysis-tab" data-bs-toggle="pill" data-bs-target="#analysis" type="button" role="tab">Trends</button>
|
||||
</li>
|
||||
<!-- Hidden tab triggers for Bootstrap API -->
|
||||
<ul class="nav nav-pills d-none" id="mainTab" role="tablist">
|
||||
<li class="nav-item" role="presentation"><button class="nav-link active" id="today-tab" data-bs-toggle="pill" data-bs-target="#today" type="button" role="tab"></button></li>
|
||||
<li class="nav-item" role="presentation"><button class="nav-link" id="health-tab" data-bs-toggle="pill" data-bs-target="#health" type="button" role="tab"></button></li>
|
||||
<li class="nav-item" role="presentation"><button class="nav-link" id="gallery-tab" data-bs-toggle="pill" data-bs-target="#gallery" type="button" role="tab"></button></li>
|
||||
<li class="nav-item" role="presentation"><button class="nav-link" id="analysis-tab" data-bs-toggle="pill" data-bs-target="#analysis" type="button" role="tab"></button></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="mainTabContent">
|
||||
<!-- TODAY TAB -->
|
||||
<div class="tab-pane fade show active" id="today" role="tabpanel">
|
||||
<!-- Reminders / Quick Status -->
|
||||
<div id="reminders-row" class="d-flex gap-2 mb-3 overflow-auto pb-2" style="scrollbar-width: none;">
|
||||
<!-- Reminders filled by JS -->
|
||||
</div>
|
||||
|
||||
<!-- Stats Section -->
|
||||
<div id="stats-container">
|
||||
<!-- Calories -->
|
||||
<div class="card-stat">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="stat-label">Calories</span>
|
||||
<span class="text-muted small"><span id="cal-left" class="fw-bold text-white">0</span> kcal left</span>
|
||||
<main class="app-content px-3 pt-3 pb-5">
|
||||
<div class="tab-content" id="mainTabContent">
|
||||
<!-- TODAY TAB -->
|
||||
<div class="tab-pane fade show active" id="today" role="tabpanel">
|
||||
<div id="reminders-row" class="d-flex gap-2 mb-3 overflow-auto pb-2" style="scrollbar-width: none;"></div>
|
||||
<div id="stats-container">
|
||||
<div class="card-stat">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="stat-label">Calories</span>
|
||||
<span class="text-muted small"><span id="cal-left" class="fw-bold text-white">0</span> kcal left</span>
|
||||
</div>
|
||||
<div class="stat-value"><span id="cal-consumed">0</span> <span class="fs-6 fw-normal text-muted">/ <span id="cal-goal">0</span></span></div>
|
||||
<div class="progress-thin"><div id="cal-progress" class="progress-bar-inner" style="width: 0%"></div></div>
|
||||
</div>
|
||||
<div class="stat-value"><span id="cal-consumed">0</span> <span class="fs-6 fw-normal text-muted">/ <span id="cal-goal">0</span></span></div>
|
||||
<div class="progress-thin"><div id="cal-progress" class="progress-bar-inner" style="width: 0%"></div></div>
|
||||
</div>
|
||||
|
||||
<!-- Protein -->
|
||||
<div class="card-stat">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="stat-label">Protein</span>
|
||||
<span class="text-muted small"><span id="pro-left" class="fw-bold text-white">0</span>g left</span>
|
||||
<div class="card-stat">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="stat-label">Protein</span>
|
||||
<span class="text-muted small"><span id="pro-left" class="fw-bold text-white">0</span>g left</span>
|
||||
</div>
|
||||
<div class="stat-value"><span id="pro-consumed">0</span>g <span class="fs-6 fw-normal text-muted">/ <span id="pro-goal">0</span>g</span></div>
|
||||
<div class="progress-thin"><div id="pro-progress" class="progress-bar-inner" style="width: 0%"></div></div>
|
||||
</div>
|
||||
<div class="stat-value"><span id="pro-consumed">0</span>g <span class="fs-6 fw-normal text-muted">/ <span id="pro-goal">0</span>g</span></div>
|
||||
<div class="progress-thin"><div id="pro-progress" class="progress-bar-inner" style="width: 0%"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-stat mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="stat-label text-info">Water Intake</span>
|
||||
<span class="text-muted small"><span class="today-water-consumed fw-bold text-white">0</span> / <span class="today-water-goal">0</span> L</span>
|
||||
</div>
|
||||
<div class="progress-thin mb-3"><div class="today-water-progress-bar progress-bar-inner bg-info" style="width: 0%"></div></div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-outline-info flex-fill py-2 small water-btn" data-amount="0.25">+250ml</button>
|
||||
<button class="btn btn-outline-info flex-fill py-2 small water-btn" data-amount="0.5">+500ml</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-stat mb-4">
|
||||
<span class="stat-label mb-3 d-block">Supplements</span>
|
||||
<div id="today-supplements-list" class="d-flex flex-wrap gap-2"></div>
|
||||
</div>
|
||||
<button class="btn btn-primary-custom mb-4 shadow-sm" data-bs-toggle="modal" data-bs-target="#addLogModal">Log Food / Supplement</button>
|
||||
<div class="recent-logs">
|
||||
<h2 class="h6 fw-bold mb-3">Today's History</h2>
|
||||
<div id="recent-logs-list"><div class="text-center py-4"><p class="text-muted small mb-0">No entries today yet.</p></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Water Intake (Quick Access) -->
|
||||
<div class="card-stat mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="stat-label text-info">Water Intake</span>
|
||||
<span class="text-muted small"><span class="today-water-consumed fw-bold text-white">0</span> / <span class="today-water-goal">0</span> L</span>
|
||||
</div>
|
||||
<div class="progress-thin mb-3"><div class="today-water-progress-bar progress-bar-inner bg-info" style="width: 0%"></div></div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-outline-info flex-fill py-2 small water-btn" data-amount="0.25">+250ml</button>
|
||||
<button class="btn btn-outline-info flex-fill py-2 small water-btn" data-amount="0.5">+500ml</button>
|
||||
</div>
|
||||
<!-- HEALTH TAB -->
|
||||
<div class="tab-pane fade" id="health" role="tabpanel">
|
||||
<div class="card-stat mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="stat-label text-info">Water Intake</span>
|
||||
<span class="text-muted small"><span id="water-consumed">0</span> / <span id="water-goal">0</span> L</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="flex-grow-1"><div class="progress-thin"><div id="water-progress" class="progress-bar-inner bg-info" style="width: 0%"></div></div></div>
|
||||
<button id="btnAddWater" class="btn btn-info btn-sm rounded-circle p-2 text-white" style="width:36px; height:36px;">+</button>
|
||||
</div>
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button class="btn btn-outline-info btn-xs py-1 px-2 small water-btn" data-amount="0.25">250ml</button>
|
||||
<button class="btn btn-outline-info btn-xs py-1 px-2 small water-btn" data-amount="0.5">500ml</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-stat mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="stat-label text-warning">Current Weight</span>
|
||||
<span class="text-muted small" id="weight-last-date">Last logged: --</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-baseline gap-2">
|
||||
<span class="stat-value text-warning mb-0" id="weight-current">0</span>
|
||||
<span class="text-muted">kg</span>
|
||||
</div>
|
||||
<button class="btn btn-outline-warning w-100 mt-3 btn-sm" data-bs-toggle="modal" data-bs-target="#weightModal">Update Weight</button>
|
||||
</div>
|
||||
<div class="card-stat">
|
||||
<span class="stat-label">Creatine Status</span>
|
||||
<div class="mt-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<span class="small">Creatine</span>
|
||||
<span id="cre-status" class="badge bg-danger">Not taken</span>
|
||||
</div>
|
||||
<div class="progress-thin"><div id="cre-progress-health" class="progress-bar-inner" style="width: 0%"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Supplements (Quick Check) -->
|
||||
<div class="card-stat mb-4">
|
||||
<span class="stat-label mb-3 d-block">Supplements</span>
|
||||
<div id="today-supplements-list" class="d-flex flex-wrap gap-2">
|
||||
<!-- Filled by JS -->
|
||||
</div>
|
||||
<!-- GALLERY TAB -->
|
||||
<div class="tab-pane fade" id="gallery" role="tabpanel">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="h6 fw-bold mb-0">Progress Gallery</h2>
|
||||
<button class="btn btn-primary-custom btn-sm w-auto py-2 px-3" data-bs-toggle="modal" data-bs-target="#photoModal">+ Add Photo</button>
|
||||
</div>
|
||||
<div id="gallery-container" class="row g-3"></div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary-custom mb-4" data-bs-toggle="modal" data-bs-target="#addLogModal">Log Food / Supplement</button>
|
||||
|
||||
<!-- Recent History -->
|
||||
<div class="recent-logs">
|
||||
<h2 class="h6 fw-bold mb-3">Today's History</h2>
|
||||
<div id="recent-logs-list">
|
||||
<div class="text-center py-4">
|
||||
<p class="text-muted small mb-0">No entries today yet.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ANALYSIS TAB -->
|
||||
<div class="tab-pane fade" id="analysis" role="tabpanel">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="h6 fw-bold mb-0">Performance Trends</h2>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<input type="radio" class="btn-check" name="analysisPeriod" id="periodWeekly" checked>
|
||||
<label class="btn btn-outline-secondary" for="periodWeekly">Week</label>
|
||||
<input type="radio" class="btn-check" name="analysisPeriod" id="periodMonthly">
|
||||
<label class="btn btn-outline-secondary" for="periodMonthly">Month</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-stat mb-4">
|
||||
<span class="stat-label">Weight (kg)</span>
|
||||
<div class="mt-2" style="height: 200px;"><canvas id="weightChart"></canvas></div>
|
||||
</div>
|
||||
<div class="card-stat mb-4">
|
||||
<span class="stat-label">Calories vs Goal</span>
|
||||
<div class="mt-2" style="height: 200px;"><canvas id="caloriesChart"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- HEALTH TAB -->
|
||||
<div class="tab-pane fade" id="health" role="tabpanel">
|
||||
<!-- Water Intake -->
|
||||
<div class="card-stat mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="stat-label text-info">Water Intake</span>
|
||||
<span class="text-muted small"><span id="water-consumed">0</span> / <span id="water-goal">0</span> L</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="flex-grow-1">
|
||||
<div class="progress-thin"><div id="water-progress" class="progress-bar-inner bg-info" style="width: 0%"></div></div>
|
||||
</div>
|
||||
<button id="btnAddWater" class="btn btn-info btn-sm rounded-circle p-2 text-white" style="width:36px; height:36px;">+</button>
|
||||
</div>
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button class="btn btn-outline-info btn-xs py-1 px-2 small water-btn" data-amount="0.25">250ml</button>
|
||||
<button class="btn btn-outline-info btn-xs py-1 px-2 small water-btn" data-amount="0.5">500ml</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Weight Tracking -->
|
||||
<div class="card-stat mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="stat-label text-warning">Current Weight</span>
|
||||
<span class="text-muted small" id="weight-last-date">Last logged: --</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-baseline gap-2">
|
||||
<span class="stat-value text-warning mb-0" id="weight-current">0</span>
|
||||
<span class="text-muted">kg</span>
|
||||
</div>
|
||||
<button class="btn btn-outline-warning w-100 mt-3 btn-sm" data-bs-toggle="modal" data-bs-target="#weightModal">Update Weight</button>
|
||||
</div>
|
||||
|
||||
<!-- Supplement Status -->
|
||||
<div class="card-stat">
|
||||
<span class="stat-label">Creatine Status</span>
|
||||
<div class="mt-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<span class="small">Creatine</span>
|
||||
<span id="cre-status" class="badge bg-danger">Not taken</span>
|
||||
</div>
|
||||
<div class="progress-thin"><div id="cre-progress-health" class="progress-bar-inner" style="width: 0%"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GALLERY TAB -->
|
||||
<div class="tab-pane fade" id="gallery" role="tabpanel">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="h6 fw-bold mb-0">Progress Gallery</h2>
|
||||
<button class="btn btn-primary-custom btn-sm" data-bs-toggle="modal" data-bs-target="#photoModal">+ Add Photo</button>
|
||||
</div>
|
||||
<div id="gallery-container" class="row g-3">
|
||||
<!-- Photos filled by JS -->
|
||||
<div class="col-12 text-center py-5">
|
||||
<p class="text-muted small">No photos uploaded yet.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ANALYSIS TAB -->
|
||||
<div class="tab-pane fade" id="analysis" role="tabpanel">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="h6 fw-bold mb-0">Performance Trends</h2>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<input type="radio" class="btn-check" name="analysisPeriod" id="periodWeekly" checked>
|
||||
<label class="btn btn-outline-secondary" for="periodWeekly">Week</label>
|
||||
<input type="radio" class="btn-check" name="analysisPeriod" id="periodMonthly">
|
||||
<label class="btn btn-outline-secondary" for="periodMonthly">Month</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-stat mb-4">
|
||||
<span class="stat-label">Weight (kg)</span>
|
||||
<div class="mt-2" style="height: 200px;">
|
||||
<canvas id="weightChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-stat mb-4">
|
||||
<span class="stat-label">Calories vs Goal</span>
|
||||
<div class="mt-2" style="height: 200px;">
|
||||
<canvas id="caloriesChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="bottom-nav">
|
||||
<a href="#" class="nav-item active" data-tab="today">
|
||||
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
||||
<span>Today</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-tab="health">
|
||||
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path></svg>
|
||||
<span>Health</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-tab="gallery">
|
||||
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
|
||||
<span>Gallery</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-tab="analysis">
|
||||
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
|
||||
<span>Trends</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- MODALS -->
|
||||
@ -227,35 +214,17 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="h5 fw-bold mb-0">New Log</h2>
|
||||
<ul class="nav nav-pills small" id="logTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active py-1 px-3" id="manual-tab" data-bs-toggle="pill" data-bs-target="#manual-log" type="button" role="tab">Manual</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link py-1 px-3" id="ai-tab" data-bs-toggle="pill" data-bs-target="#ai-log" type="button" role="tab">AI ✨</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation"><button class="nav-link active py-1 px-3" id="manual-tab" data-bs-toggle="pill" data-bs-target="#manual-log" type="button" role="tab">Manual</button></li>
|
||||
<li class="nav-item" role="presentation"><button class="nav-link py-1 px-3" id="ai-tab" data-bs-toggle="pill" data-bs-target="#ai-log" type="button" role="tab">AI ✨</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="manual-log" role="tabpanel">
|
||||
<form id="addLogForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Item Name</label>
|
||||
<input type="text" id="logName" class="form-control" placeholder="e.g. Chicken Breast" required>
|
||||
</div>
|
||||
<div class="mb-3"><label class="form-label small fw-bold">Item Name</label><input type="text" id="logName" class="form-control" placeholder="Chicken Breast" required></div>
|
||||
<div class="row g-3">
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label small fw-bold">Calories (kcal)</label>
|
||||
<input type="number" id="logCalories" class="form-control" placeholder="0">
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label small fw-bold">Protein (g)</label>
|
||||
<input type="number" id="logProtein" class="form-control" placeholder="0">
|
||||
</div>
|
||||
<div class="col-12 mb-3">
|
||||
<label class="form-label small fw-bold">Creatine (g)</label>
|
||||
<input type="number" step="0.1" id="logCreatine" class="form-control" placeholder="0">
|
||||
</div>
|
||||
<div class="col-6 mb-3"><label class="form-label small fw-bold">Calories (kcal)</label><input type="number" id="logCalories" class="form-control" placeholder="0"></div>
|
||||
<div class="col-6 mb-3"><label class="form-label small fw-bold">Protein (g)</label><input type="number" id="logProtein" class="form-control" placeholder="0"></div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary-custom mt-3">Save Entry</button>
|
||||
</form>
|
||||
@ -281,10 +250,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<div class="modal-content p-4">
|
||||
<h2 class="h5 fw-bold mb-4">Log Weight</h2>
|
||||
<form id="weightForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Weight (kg)</label>
|
||||
<input type="number" step="0.1" id="weightInput" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3"><label class="form-label small fw-bold">Weight (kg)</label><input type="number" step="0.1" id="weightInput" class="form-control" required></div>
|
||||
<button type="submit" class="btn btn-primary-custom mt-3">Save Weight</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -296,14 +262,8 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<div class="modal-content p-4">
|
||||
<h2 class="h5 fw-bold mb-4">Progress Photo</h2>
|
||||
<form id="photoForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Select Photo</label>
|
||||
<input type="file" id="photoInput" class="form-control" accept="image/*" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Current Weight (kg - optional)</label>
|
||||
<input type="number" step="0.1" id="photoWeight" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3"><label class="form-label small fw-bold">Select Photo</label><input type="file" id="photoInput" class="form-control" accept="image/*" required></div>
|
||||
<div class="mb-3"><label class="form-label small fw-bold">Current Weight (kg - optional)</label><input type="number" step="0.1" id="photoWeight" class="form-control"></div>
|
||||
<button type="submit" class="btn btn-primary-custom mt-3">Upload Photo</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -315,22 +275,10 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<div class="modal-content p-4">
|
||||
<h2 class="h5 fw-bold mb-4">Daily Goals</h2>
|
||||
<form id="updateGoalsForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Calorie Target (kcal)</label>
|
||||
<input type="number" id="goalCalories" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Protein Target (g)</label>
|
||||
<input type="number" id="goalProtein" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Creatine Target (g)</label>
|
||||
<input type="number" step="0.1" id="goalCreatine" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Water Target (L)</label>
|
||||
<input type="number" step="0.1" id="goalWater" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3"><label class="form-label small fw-bold">Calorie Target (kcal)</label><input type="number" id="goalCalories" class="form-control" required></div>
|
||||
<div class="mb-3"><label class="form-label small fw-bold">Protein Target (g)</label><input type="number" id="goalProtein" class="form-control" required></div>
|
||||
<div class="mb-3"><label class="form-label small fw-bold">Creatine Target (g)</label><input type="number" step="0.1" id="goalCreatine" class="form-control" required></div>
|
||||
<div class="mb-3"><label class="form-label small fw-bold">Water Target (L)</label><input type="number" step="0.1" id="goalWater" class="form-control" required></div>
|
||||
<button type="submit" class="btn btn-primary-custom mt-3">Update Goals</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
17
manifest.json
Normal file
17
manifest.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Nutrition Pulse",
|
||||
"short_name": "Pulse",
|
||||
"description": "Modern nutrition and fitness tracking.",
|
||||
"start_url": "/index.php",
|
||||
"display": "standalone",
|
||||
"background_color": "#0f172a",
|
||||
"theme_color": "#3b82f6",
|
||||
"icons": [
|
||||
{
|
||||
"src": "https://cdn-icons-png.flaticon.com/512/3069/3069172.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
26
sw.js
Normal file
26
sw.js
Normal file
@ -0,0 +1,26 @@
|
||||
const CACHE_NAME = 'pulse-v1';
|
||||
const ASSETS = [
|
||||
'/',
|
||||
'/index.php',
|
||||
'/assets/css/custom.css',
|
||||
'/assets/js/main.js',
|
||||
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css',
|
||||
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js',
|
||||
'https://cdn.jsdelivr.net/npm/chart.js'
|
||||
];
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then(cache => {
|
||||
return cache.addAll(ASSETS);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(response => {
|
||||
return response || fetch(event.request);
|
||||
})
|
||||
);
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user