Improve styling of app

This commit is contained in:
Peter Stockings
2026-03-09 21:50:53 +11:00
parent ab23bf6a9e
commit de66dc5fd8
9 changed files with 708 additions and 139 deletions

View File

@@ -1,6 +1,6 @@
from flask import Blueprint, render_template, redirect, url_for, flash from flask import Blueprint, render_template, redirect, url_for, flash
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
from app.models import db, User from app.models import db, User, Profile
from app.forms import LoginForm, SignupForm from app.forms import LoginForm, SignupForm
from flask_login import login_user, login_required, logout_user from flask_login import login_user, login_required, logout_user
@@ -11,7 +11,8 @@ def signup():
form = SignupForm() form = SignupForm()
if form.validate_on_submit(): if form.validate_on_submit():
hashed_password = generate_password_hash(form.password.data) hashed_password = generate_password_hash(form.password.data)
new_user = User(username=form.username.data, password_hash=hashed_password) new_profile = Profile()
new_user = User(username=form.username.data, password_hash=hashed_password, profile=new_profile)
db.session.add(new_user) db.session.add(new_user)
db.session.commit() db.session.commit()
flash("Account created successfully. Please log in.", "success") flash("Account created successfully. Please log in.", "success")

View File

@@ -154,7 +154,7 @@ html,
-o-tab-size: 4; -o-tab-size: 4;
tab-size: 4; tab-size: 4;
/* 3 */ /* 3 */
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-family: Inter, sans-serif;
/* 4 */ /* 4 */
font-feature-settings: normal; font-feature-settings: normal;
/* 5 */ /* 5 */
@@ -588,6 +588,10 @@ video {
} }
} }
.pointer-events-none {
pointer-events: none;
}
.visible { .visible {
visibility: visible; visibility: visible;
} }
@@ -608,6 +612,15 @@ video {
position: relative; position: relative;
} }
.inset-0 {
inset: 0px;
}
.inset-y-0 {
top: 0px;
bottom: 0px;
}
.bottom-0 { .bottom-0 {
bottom: 0px; bottom: 0px;
} }
@@ -644,10 +657,22 @@ video {
top: 1.25rem; top: 1.25rem;
} }
.top-24 {
top: 6rem;
}
.left-0 {
left: 0px;
}
.z-10 { .z-10 {
z-index: 10; z-index: 10;
} }
.z-50 {
z-index: 50;
}
.col-span-full { .col-span-full {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
@@ -717,6 +742,38 @@ video {
margin-top: 1.5rem; margin-top: 1.5rem;
} }
.ml-4 {
margin-left: 1rem;
}
.mb-12 {
margin-bottom: 3rem;
}
.mb-16 {
margin-bottom: 4rem;
}
.mb-3 {
margin-bottom: 0.75rem;
}
.mb-20 {
margin-bottom: 5rem;
}
.mb-5 {
margin-bottom: 1.25rem;
}
.mt-12 {
margin-top: 3rem;
}
.mt-8 {
margin-top: 2rem;
}
.block { .block {
display: block; display: block;
} }
@@ -774,6 +831,14 @@ video {
height: 2rem; height: 2rem;
} }
.h-20 {
height: 5rem;
}
.h-2 {
height: 0.5rem;
}
.w-16 { .w-16 {
width: 4rem; width: 4rem;
} }
@@ -802,6 +867,18 @@ video {
width: 100%; width: 100%;
} }
.w-10 {
width: 2.5rem;
}
.w-20 {
width: 5rem;
}
.min-w-\[300px\] {
min-width: 300px;
}
.max-w-4xl { .max-w-4xl {
max-width: 56rem; max-width: 56rem;
} }
@@ -818,6 +895,10 @@ video {
max-width: 28rem; max-width: 28rem;
} }
.max-w-2xl {
max-width: 42rem;
}
.flex-1 { .flex-1 {
flex: 1 1 0%; flex: 1 1 0%;
} }
@@ -886,6 +967,10 @@ video {
gap: 2rem; gap: 2rem;
} }
.gap-10 {
gap: 2.5rem;
}
.space-x-2 > :not([hidden]) ~ :not([hidden]) { .space-x-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0; --tw-space-x-reverse: 0;
margin-right: calc(0.5rem * var(--tw-space-x-reverse)); margin-right: calc(0.5rem * var(--tw-space-x-reverse));
@@ -936,6 +1021,23 @@ video {
border-radius: 0.375rem; border-radius: 0.375rem;
} }
.rounded-xl {
border-radius: 0.75rem;
}
.rounded-2xl {
border-radius: 1rem;
}
.rounded-3xl {
border-radius: 1.5rem;
}
.rounded-b-3xl {
border-bottom-right-radius: 1.5rem;
border-bottom-left-radius: 1.5rem;
}
.border { .border {
border-width: 1px; border-width: 1px;
} }
@@ -981,6 +1083,21 @@ video {
border-color: rgb(255 255 255 / var(--tw-border-opacity, 1)); border-color: rgb(255 255 255 / var(--tw-border-opacity, 1));
} }
.border-gray-100 {
--tw-border-opacity: 1;
border-color: rgb(243 244 246 / var(--tw-border-opacity, 1));
}
.border-primary-500 {
--tw-border-opacity: 1;
border-color: rgb(20 184 166 / var(--tw-border-opacity, 1));
}
.border-primary-600 {
--tw-border-opacity: 1;
border-color: rgb(13 148 136 / var(--tw-border-opacity, 1));
}
.bg-blue-600 { .bg-blue-600 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1)); background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1));
@@ -1041,20 +1158,88 @@ video {
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1)); background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
} }
.bg-primary-800 {
--tw-bg-opacity: 1;
background-color: rgb(17 94 89 / var(--tw-bg-opacity, 1));
}
.bg-primary-900 {
--tw-bg-opacity: 1;
background-color: rgb(19 78 74 / var(--tw-bg-opacity, 1));
}
.bg-primary-50 {
--tw-bg-opacity: 1;
background-color: rgb(240 253 250 / var(--tw-bg-opacity, 1));
}
.bg-primary-700 {
--tw-bg-opacity: 1;
background-color: rgb(15 118 110 / var(--tw-bg-opacity, 1));
}
.bg-white\/5 {
background-color: rgb(255 255 255 / 0.05);
}
.bg-primary-600 {
--tw-bg-opacity: 1;
background-color: rgb(13 148 136 / var(--tw-bg-opacity, 1));
}
.bg-primary-100 {
--tw-bg-opacity: 1;
background-color: rgb(204 251 241 / var(--tw-bg-opacity, 1));
}
.bg-gradient-to-r { .bg-gradient-to-r {
background-image: linear-gradient(to right, var(--tw-gradient-stops)); background-image: linear-gradient(to right, var(--tw-gradient-stops));
} }
.bg-gradient-to-br {
background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
}
.from-blue-500 { .from-blue-500 {
--tw-gradient-from: #3b82f6 var(--tw-gradient-from-position); --tw-gradient-from: #3b82f6 var(--tw-gradient-from-position);
--tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position); --tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
} }
.from-primary-600 {
--tw-gradient-from: #0d9488 var(--tw-gradient-from-position);
--tw-gradient-to: rgb(13 148 136 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-primary-400 {
--tw-gradient-from: #2dd4bf var(--tw-gradient-from-position);
--tw-gradient-to: rgb(45 212 191 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-primary-500 {
--tw-gradient-from: #14b8a6 var(--tw-gradient-from-position);
--tw-gradient-to: rgb(20 184 166 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-blue-700 { .to-blue-700 {
--tw-gradient-to: #1d4ed8 var(--tw-gradient-to-position); --tw-gradient-to: #1d4ed8 var(--tw-gradient-to-position);
} }
.to-primary-800 {
--tw-gradient-to: #115e59 var(--tw-gradient-to-position);
}
.to-primary-600 {
--tw-gradient-to: #0d9488 var(--tw-gradient-to-position);
}
.to-primary-700 {
--tw-gradient-to: #0f766e var(--tw-gradient-to-position);
}
.fill-current { .fill-current {
fill: currentColor; fill: currentColor;
} }
@@ -1092,6 +1277,10 @@ video {
padding: 2rem; padding: 2rem;
} }
.p-10 {
padding: 2.5rem;
}
.px-2 { .px-2 {
padding-left: 0.5rem; padding-left: 0.5rem;
padding-right: 0.5rem; padding-right: 0.5rem;
@@ -1147,6 +1336,11 @@ video {
padding-bottom: 1rem; padding-bottom: 1rem;
} }
.py-24 {
padding-top: 6rem;
padding-bottom: 6rem;
}
.pl-1 { .pl-1 {
padding-left: 0.25rem; padding-left: 0.25rem;
} }
@@ -1167,10 +1361,30 @@ video {
padding-top: 1.5rem; padding-top: 1.5rem;
} }
.pb-20 {
padding-bottom: 5rem;
}
.pt-24 {
padding-top: 6rem;
}
.pl-11 {
padding-left: 2.75rem;
}
.pl-4 {
padding-left: 1rem;
}
.text-center { .text-center {
text-align: center; text-align: center;
} }
.font-sans {
font-family: Inter, sans-serif;
}
.text-2xl { .text-2xl {
font-size: 1.5rem; font-size: 1.5rem;
line-height: 2rem; line-height: 2rem;
@@ -1211,6 +1425,11 @@ video {
line-height: 1rem; line-height: 1rem;
} }
.text-5xl {
font-size: 3rem;
line-height: 1;
}
.font-bold { .font-bold {
font-weight: 700; font-weight: 700;
} }
@@ -1223,6 +1442,26 @@ video {
font-weight: 600; font-weight: 600;
} }
.font-extrabold {
font-weight: 800;
}
.uppercase {
text-transform: uppercase;
}
.leading-relaxed {
line-height: 1.625;
}
.tracking-tight {
letter-spacing: -0.025em;
}
.tracking-wider {
letter-spacing: 0.05em;
}
.text-blue-600 { .text-blue-600 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity, 1)); color: rgb(37 99 235 / var(--tw-text-opacity, 1));
@@ -1278,10 +1517,40 @@ video {
color: rgb(255 255 255 / var(--tw-text-opacity, 1)); color: rgb(255 255 255 / var(--tw-text-opacity, 1));
} }
.text-gray-900 {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity, 1));
}
.text-primary-50 {
--tw-text-opacity: 1;
color: rgb(240 253 250 / var(--tw-text-opacity, 1));
}
.text-primary-600 {
--tw-text-opacity: 1;
color: rgb(13 148 136 / var(--tw-text-opacity, 1));
}
.text-primary-700 {
--tw-text-opacity: 1;
color: rgb(15 118 110 / var(--tw-text-opacity, 1));
}
.text-primary-200 {
--tw-text-opacity: 1;
color: rgb(153 246 228 / var(--tw-text-opacity, 1));
}
.no-underline { .no-underline {
text-decoration-line: none; text-decoration-line: none;
} }
.antialiased {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.shadow { .shadow {
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
@@ -1306,6 +1575,24 @@ video {
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
} }
.shadow-xl {
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.shadow-inner {
--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.shadow-2xl {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.transition { .transition {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
@@ -1320,6 +1607,37 @@ video {
transition-duration: 150ms; transition-duration: 150ms;
} }
.transition-colors {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-300 {
transition-duration: 300ms;
}
.hover\:-translate-y-1:hover {
--tw-translate-y: -0.25rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hover\:-translate-y-2:hover {
--tw-translate-y: -0.5rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hover\:-translate-y-0\.5:hover {
--tw-translate-y: -0.125rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hover\:bg-blue-700:hover { .hover\:bg-blue-700:hover {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1)); background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1));
@@ -1360,6 +1678,21 @@ video {
background-color: rgb(185 28 28 / var(--tw-bg-opacity, 1)); background-color: rgb(185 28 28 / var(--tw-bg-opacity, 1));
} }
.hover\:bg-gray-50:hover {
--tw-bg-opacity: 1;
background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));
}
.hover\:bg-primary-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(13 148 136 / var(--tw-bg-opacity, 1));
}
.hover\:bg-primary-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(15 118 110 / var(--tw-bg-opacity, 1));
}
.hover\:text-gray-200:hover { .hover\:text-gray-200:hover {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(229 231 235 / var(--tw-text-opacity, 1)); color: rgb(229 231 235 / var(--tw-text-opacity, 1));
@@ -1390,6 +1723,15 @@ video {
color: rgb(255 255 255 / var(--tw-text-opacity, 1)); color: rgb(255 255 255 / var(--tw-text-opacity, 1));
} }
.hover\:text-primary-800:hover {
--tw-text-opacity: 1;
color: rgb(17 94 89 / var(--tw-text-opacity, 1));
}
.hover\:underline:hover {
text-decoration-line: underline;
}
.hover\:no-underline:hover { .hover\:no-underline:hover {
text-decoration-line: none; text-decoration-line: none;
} }
@@ -1400,11 +1742,33 @@ video {
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
} }
.hover\:shadow-2xl:hover {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.hover\:shadow-xl:hover {
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.focus\:border-blue-500:focus { .focus\:border-blue-500:focus {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity, 1)); border-color: rgb(59 130 246 / var(--tw-border-opacity, 1));
} }
.focus\:border-primary-500:focus {
--tw-border-opacity: 1;
border-color: rgb(20 184 166 / var(--tw-border-opacity, 1));
}
.focus\:bg-white:focus {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
}
.focus\:text-white:focus { .focus\:text-white:focus {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity, 1)); color: rgb(255 255 255 / var(--tw-text-opacity, 1));
@@ -1431,6 +1795,11 @@ video {
--tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity, 1)); --tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity, 1));
} }
.focus\:ring-primary-500:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(20 184 166 / var(--tw-ring-opacity, 1));
}
.group:hover .group-hover\:scale-105 { .group:hover .group-hover\:scale-105 {
--tw-scale-x: 1.05; --tw-scale-x: 1.05;
--tw-scale-y: 1.05; --tw-scale-y: 1.05;
@@ -1453,6 +1822,22 @@ video {
.sm\:h-40 { .sm\:h-40 {
height: 10rem; height: 10rem;
} }
.sm\:flex-row {
flex-direction: row;
}
.sm\:space-x-6 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(1.5rem * var(--tw-space-x-reverse));
margin-left: calc(1.5rem * calc(1 - var(--tw-space-x-reverse)));
}
.sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(0px * var(--tw-space-y-reverse));
}
} }
@media (min-width: 768px) { @media (min-width: 768px) {
@@ -1483,6 +1868,16 @@ video {
.md\:p-4 { .md\:p-4 {
padding: 1rem; padding: 1rem;
} }
.md\:text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
.md\:text-6xl {
font-size: 3.75rem;
line-height: 1;
}
} }
@media (min-width: 1024px) { @media (min-width: 1024px) {

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#14b8a6">
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@@ -5,17 +5,17 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}BP Tracker{% endblock %}</title> <title>{% block title %}BP Tracker{% endblock %}</title>
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='images/favicon-32x32.png') }}"> <link rel="icon" type="image/svg+xml" sizes="any" href="{{ url_for('static', filename='images/favicon.svg') }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ url_for('static', filename='images/favicon-32x32.png') }}"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="/static/css/tailwind.css" rel="stylesheet"> <link href="/static/css/tailwind.css" rel="stylesheet">
<script src="/static/js/alpine.min.js" defer></script> <script src="/static/js/alpine.min.js" defer></script>
<script src="/static/js/turbolinks.min.js"></script> <script src="/static/js/turbolinks.min.js"></script>
</head> </head>
<body class="bg-gray-100 text-gray-800"> <body class="bg-gray-50 text-gray-800 font-sans antialiased">
<nav class="flex items-center justify-between flex-wrap p-6 fixed w-full z-10 top-0" x-data="{ isOpen: false }" <nav class="flex items-center justify-between flex-wrap p-6 fixed w-full z-10 top-0 transition-colors duration-300 shadow-md"
@keydown.escape="isOpen = false" @click.away="isOpen = false" x-data="{ isOpen: false }" @keydown.escape="isOpen = false" @click.away="isOpen = false"
:class="{ 'shadow-lg bg-indigo-900' : isOpen , 'bg-gray-800' : !isOpen}"> :class="{ 'bg-primary-900' : isOpen , 'bg-primary-800' : !isOpen}">
<!--Logo etc--> <!--Logo etc-->
<div class="flex items-center flex-shrink-0 text-white mr-6"> <div class="flex items-center flex-shrink-0 text-white mr-6">
<a class="text-white no-underline hover:text-white hover:no-underline" href="/"> <a class="text-white no-underline hover:text-white hover:no-underline" href="/">
@@ -45,7 +45,7 @@
{% if request.path == url_for('main.dashboard') %} {% if request.path == url_for('main.dashboard') %}
text-white text-white
{% else %} {% else %}
text-gray-600 hover:text-gray-200 hover:text-underline text-primary-200 hover:text-white font-medium transition-colors
{% endif %}" href="{{ url_for('main.dashboard') }}">Dashboard {% endif %}" href="{{ url_for('main.dashboard') }}">Dashboard
</a> </a>
</li> </li>
@@ -54,12 +54,12 @@
{% if request.path == url_for('data.manage_data') %} {% if request.path == url_for('data.manage_data') %}
text-white text-white
{% else %} {% else %}
text-gray-600 hover:text-gray-200 hover:text-underline text-primary-200 hover:text-white font-medium transition-colors
{% endif %}" href="{{ url_for('data.manage_data') }}">Data {% endif %}" href="{{ url_for('data.manage_data') }}">Data
</a> </a>
</li> </li>
<li class="mr-3"> <li class="mr-3">
<a class="flex items-center gap-2 text-gray-600 no-underline hover:text-gray-200 hover:text-underline py-2 px-4" <a class="flex items-center gap-2 text-primary-200 no-underline hover:text-white font-medium transition-colors py-2 px-4"
href="{{ url_for('user.profile') }}"> href="{{ url_for('user.profile') }}">
{% if current_user.profile and current_user.profile.profile_pic %} {% if current_user.profile and current_user.profile.profile_pic %}
<img src="{{ url_for('user.profile_image', user_id=current_user.id) }}" alt="Profile Picture" <img src="{{ url_for('user.profile_image', user_id=current_user.id) }}" alt="Profile Picture"
@@ -67,7 +67,7 @@
{% else %} {% else %}
<!-- Default SVG Icon --> <!-- Default SVG Icon -->
<div <div
class="w-8 h-8 bg-gray-300 rounded-full flex items-center justify-center border-2 border-white"> class="w-8 h-8 bg-primary-100 rounded-full flex items-center justify-center border-2 border-white">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="w-5 h-5 text-gray-600" <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="w-5 h-5 text-gray-600"
viewBox="0 0 24 24"> viewBox="0 0 24 24">
<path <path
@@ -79,7 +79,7 @@
{% if request.path == url_for('user.profile') %} {% if request.path == url_for('user.profile') %}
text-white text-white
{% else %} {% else %}
text-gray-600 hover:text-gray-200 hover:text-underline text-primary-200 hover:text-white font-medium transition-colors
{% endif %}">Profile</span> {% endif %}">Profile</span>
</a> </a>
</li> </li>
@@ -88,7 +88,7 @@
{% if request.path == url_for('auth.logout') %} {% if request.path == url_for('auth.logout') %}
text-white text-white
{% else %} {% else %}
text-gray-600 hover:text-gray-200 hover:text-underline text-primary-200 hover:text-white font-medium transition-colors
{% endif %}" href="{{ url_for('auth.logout') }}">Logout {% endif %}" href="{{ url_for('auth.logout') }}">Logout
</a> </a>
</li> </li>
@@ -98,7 +98,7 @@
{% if request.path == url_for('auth.login') %} {% if request.path == url_for('auth.login') %}
text-white text-white
{% else %} {% else %}
text-gray-600 hover:text-gray-200 hover:text-underline text-primary-200 hover:text-white font-medium transition-colors
{% endif %}" href="{{ url_for('auth.login') }}">Login {% endif %}" href="{{ url_for('auth.login') }}">Login
</a> </a>
</li> </li>
@@ -107,7 +107,7 @@
{% if request.path == url_for('auth.signup') %} {% if request.path == url_for('auth.signup') %}
text-white text-white
{% else %} {% else %}
text-gray-600 hover:text-gray-200 hover:text-underline text-primary-200 hover:text-white font-medium transition-colors
{% endif %}" href="{{ url_for('auth.signup') }}">Signup {% endif %}" href="{{ url_for('auth.signup') }}">Signup
</a> </a>
</li> </li>
@@ -118,12 +118,13 @@
{% with messages = get_flashed_messages(with_categories=True) %} {% with messages = get_flashed_messages(with_categories=True) %}
{% if messages %} {% if messages %}
<div class="container mx-auto mt-4 space-y-4"> <div class="fixed top-24 right-4 z-50 space-y-4">
{% for category, message in messages %} {% for category, message in messages %}
<div class="flex items-center justify-between p-4 rounded-lg shadow text-white bg-{{ 'red' if category == 'danger' else 'green' }}-500" <div class="flex items-center justify-between p-4 rounded-xl shadow-xl text-white bg-{{ 'red' if category == 'danger' else 'primary' }}-500 min-w-[300px]"
x-data="{ visible: true }" x-show="visible" x-transition.duration.300ms> x-data="{ visible: true }" x-show="visible" x-transition.duration.300ms>
<span>{{ message }}</span> <span class="font-medium">{{ message }}</span>
<button @click="visible = false" class="text-xl font-bold">&times;</button> <button @click="visible = false"
class="text-2xl font-bold ml-4 hover:text-gray-200 transition-colors">&times;</button>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>

View File

@@ -1,32 +1,88 @@
{% extends "_layout.html" %} {% extends "_layout.html" %}
{% block content %} {% block content %}
<div class="max-w-md mx-auto bg-white p-6 rounded-lg shadow-md"> <div class="max-w-md mx-auto mt-12 mb-20">
<h1 class="text-2xl font-bold text-center mb-4">Login</h1> <div class="bg-white p-10 rounded-3xl shadow-2xl border border-gray-100 relative overflow-hidden">
<!-- Decorative background element -->
<div class="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-primary-400 to-primary-600"></div>
<h1 class="text-3xl font-extrabold text-center mb-2 text-gray-900">Welcome Back</h1>
<p class="text-center text-gray-500 mb-8">Please enter your details to sign in.</p>
<form method="POST" action="{{ url_for('auth.login') }}" novalidate> <form method="POST" action="{{ url_for('auth.login') }}" novalidate>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<!-- Username Field --> <!-- Username Field -->
<div class="mb-4"> <div class="mb-5">
{{ form.username.label(class="block text-sm font-medium text-gray-700") }} {{ form.username.label(class="block text-sm font-semibold text-gray-700 mb-2") }}
{{ form.username(class="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500") }} <div class="relative">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
clip-rule="evenodd" />
</svg>
</div>
{{ form.username(class="w-full pl-11 p-3 border border-gray-300 rounded-xl focus:outline-none
focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition-all shadow-sm bg-gray-50
focus:bg-white") }}
</div>
{% for error in form.username.errors %} {% for error in form.username.errors %}
<p class="text-sm text-red-600 mt-1">{{ error }}</p> <p class="text-sm text-red-500 mt-2 font-medium flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"></path>
</svg>
{{ error }}
</p>
{% endfor %} {% endfor %}
</div> </div>
<!-- Password Field --> <!-- Password Field -->
<div class="mb-4"> <div class="mb-5">
{{ form.password.label(class="block text-sm font-medium text-gray-700") }} <div class="flex justify-between items-center mb-2">
{{ form.password(class="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500") }} {{ form.password.label(class="block text-sm font-semibold text-gray-700") }}
<a href="#"
class="text-sm text-primary-600 hover:text-primary-800 hover:underline font-medium">Forgot
Password?</a>
</div>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M3 3a1 1 0 011 1v12a1 1 0 11-2 0V4a1 1 0 011-1zm7.707 3.293a1 1 0 010 1.414L9.414 9H17a1 1 0 110 2H9.414l1.293 1.293a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0z"
clip-rule="evenodd" />
</svg>
</div>
{{ form.password(class="w-full pl-11 p-3 border border-gray-300 rounded-xl focus:outline-none
focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition-all shadow-sm bg-gray-50
focus:bg-white") }}
</div>
{% for error in form.password.errors %} {% for error in form.password.errors %}
<p class="text-sm text-red-600 mt-1">{{ error }}</p> <p class="text-sm text-red-500 mt-2 font-medium flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"></path>
</svg>
{{ error }}
</p>
{% endfor %} {% endfor %}
</div> </div>
<!-- Submit Button --> <!-- Submit Button -->
<div> <div class="mt-8">
{{ form.submit(class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700") }} {{ form.submit(class="w-full bg-primary-600 hover:bg-primary-700 text-white font-bold py-3 rounded-xl
shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-0.5 cursor-pointer")
}}
</div> </div>
<p class="text-center mt-6 text-gray-600">
Don't have an account? <a href="{{ url_for('auth.signup') }}"
class="text-primary-600 hover:text-primary-800 font-bold hover:underline">Sign up</a>
</p>
</form> </form>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@@ -1,42 +1,111 @@
{% extends "_layout.html" %} {% extends "_layout.html" %}
{% block content %} {% block content %}
<div class="max-w-md mx-auto bg-white p-6 rounded-lg shadow-md"> <div class="max-w-md mx-auto mt-12 mb-20">
<h1 class="text-2xl font-bold text-center mb-4">Sign Up</h1> <div class="bg-white p-10 rounded-3xl shadow-2xl border border-gray-100 relative overflow-hidden">
<!-- Decorative background element -->
<div class="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-primary-400 to-primary-600"></div>
<h1 class="text-3xl font-extrabold text-center mb-2 text-gray-900">Create Account</h1>
<p class="text-center text-gray-500 mb-8">Join us to start tracking your health.</p>
<form method="POST" action="{{ url_for('auth.signup') }}" novalidate> <form method="POST" action="{{ url_for('auth.signup') }}" novalidate>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<!-- Username Field --> <!-- Username Field -->
<div class="mb-4"> <div class="mb-5">
{{ form.username.label(class="block text-sm font-medium text-gray-700") }} {{ form.username.label(class="block text-sm font-semibold text-gray-700 mb-2") }}
{{ form.username(class="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500") }} <div class="relative">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
clip-rule="evenodd" />
</svg>
</div>
{{ form.username(class="w-full pl-11 p-3 border border-gray-300 rounded-xl focus:outline-none
focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition-all shadow-sm bg-gray-50
focus:bg-white") }}
</div>
{% for error in form.username.errors %} {% for error in form.username.errors %}
<p class="text-sm text-red-600 mt-1">{{ error }}</p> <p class="text-sm text-red-500 mt-2 font-medium flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"></path>
</svg>
{{ error }}
</p>
{% endfor %} {% endfor %}
</div> </div>
<!-- Password Field --> <!-- Password Field -->
<div class="mb-4"> <div class="mb-5">
{{ form.password.label(class="block text-sm font-medium text-gray-700") }} {{ form.password.label(class="block text-sm font-semibold text-gray-700 mb-2") }}
{{ form.password(class="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500") }} <div class="relative">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M3 3a1 1 0 011 1v12a1 1 0 11-2 0V4a1 1 0 011-1zm7.707 3.293a1 1 0 010 1.414L9.414 9H17a1 1 0 110 2H9.414l1.293 1.293a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0z"
clip-rule="evenodd" />
</svg>
</div>
{{ form.password(class="w-full pl-11 p-3 border border-gray-300 rounded-xl focus:outline-none
focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition-all shadow-sm bg-gray-50
focus:bg-white") }}
</div>
{% for error in form.password.errors %} {% for error in form.password.errors %}
<p class="text-sm text-red-600 mt-1">{{ error }}</p> <p class="text-sm text-red-500 mt-2 font-medium flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"></path>
</svg>
{{ error }}
</p>
{% endfor %} {% endfor %}
</div> </div>
<!-- Confirm Password Field --> <!-- Confirm Password Field -->
<div class="mb-4"> <div class="mb-6">
{{ form.confirm_password.label(class="block text-sm font-medium text-gray-700") }} {{ form.confirm_password.label(class="block text-sm font-semibold text-gray-700 mb-2") }}
{{ form.confirm_password(class="w-full p-2 border rounded focus:outline-none focus:ring-2 <div class="relative">
focus:ring-blue-500") }} <div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M3 3a1 1 0 011 1v12a1 1 0 11-2 0V4a1 1 0 011-1zm7.707 3.293a1 1 0 010 1.414L9.414 9H17a1 1 0 110 2H9.414l1.293 1.293a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0z"
clip-rule="evenodd" />
</svg>
</div>
{{ form.confirm_password(class="w-full pl-11 p-3 border border-gray-300 rounded-xl
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition-all
shadow-sm bg-gray-50 focus:bg-white") }}
</div>
{% for error in form.confirm_password.errors %} {% for error in form.confirm_password.errors %}
<p class="text-sm text-red-600 mt-1">{{ error }}</p> <p class="text-sm text-red-500 mt-2 font-medium flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"></path>
</svg>
{{ error }}
</p>
{% endfor %} {% endfor %}
</div> </div>
<!-- Submit Button --> <!-- Submit Button -->
<div> <div class="mt-8">
{{ form.submit(class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700") }} {{ form.submit(class="w-full bg-primary-600 hover:bg-primary-700 text-white font-bold py-3 rounded-xl
shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-0.5 cursor-pointer")
}}
</div> </div>
<p class="text-center mt-6 text-gray-600">
Already have an account? <a href="{{ url_for('auth.login') }}"
class="text-primary-600 hover:text-primary-800 font-bold hover:underline">Log in</a>
</p>
</form> </form>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@@ -5,13 +5,13 @@
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<h1 class="text-2xl font-bold text-gray-800">Dashboard</h1> <h1 class="text-2xl font-bold text-gray-800">Dashboard</h1>
<a rel="prefetch" href="{{ url_for('reading.add_reading') }}" <a rel="prefetch" href="{{ url_for('reading.add_reading') }}"
class="bg-blue-600 text-white px-4 py-2 rounded shadow hover:bg-blue-700"> class="bg-primary-600 text-white px-4 py-2 rounded shadow hover:bg-primary-700">
+ Add New Reading + Add New Reading
</a> </a>
</div> </div>
<!-- Weekly Summary --> <!-- Weekly Summary -->
<div class="bg-gradient-to-r from-blue-500 to-blue-700 text-white p-6 rounded-lg shadow-md"> <div class="bg-gradient-to-r from-primary-500 to-primary-700 text-white p-6 rounded-xl shadow-md">
<h3 class="text-lg font-bold">Weekly Summary</h3> <h3 class="text-lg font-bold">Weekly Summary</h3>
<div class="flex justify-between mt-4"> <div class="flex justify-between mt-4">
<div> <div>
@@ -43,7 +43,7 @@
<div x-data="{ open: {{ 'true' if request.method == 'POST' else 'false' }} }" class="relative"> <div x-data="{ open: {{ 'true' if request.method == 'POST' else 'false' }} }" class="relative">
<!-- Compact Icon --> <!-- Compact Icon -->
<button @click="open = !open" <button @click="open = !open"
class="bg-blue-600 text-white p-3 rounded-full shadow-lg focus:outline-none hover:bg-blue-700"> class="bg-primary-600 text-white p-3 rounded-full shadow-lg focus:outline-none hover:bg-primary-700">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" class="w-6 h-6"> stroke="currentColor" class="w-6 h-6">
<path x-show="!open" stroke-linecap="round" stroke-linejoin="round" d="M6 9l6 6 6-6" /> <path x-show="!open" stroke-linecap="round" stroke-linejoin="round" d="M6 9l6 6 6-6" />
@@ -53,7 +53,7 @@
<!-- Collapsible Filter Form --> <!-- Collapsible Filter Form -->
<div x-show="open" x-transition.duration.300ms <div x-show="open" x-transition.duration.300ms
class="w-full md:w-1/3 bg-white p-6 rounded-lg shadow-lg border border-gray-200"> class="w-full md:w-1/3 bg-white p-6 rounded-xl shadow-lg border border-gray-200">
<div class="flex justify-between items-center mb-4"> <div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-bold text-gray-800">Filter Readings</h3> <h3 class="text-lg font-bold text-gray-800">Filter Readings</h3>
</div> </div>
@@ -62,20 +62,20 @@
<div> <div>
<label for="start_date" class="block text-sm font-medium text-gray-700">Start Date</label> <label for="start_date" class="block text-sm font-medium text-gray-700">Start Date</label>
<input type="date" name="start_date" id="start_date" <input type="date" name="start_date" id="start_date"
class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"> class="w-full p-3 border border-gray-300 rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-primary-500">
</div> </div>
<!-- End Date --> <!-- End Date -->
<div> <div>
<label for="end_date" class="block text-sm font-medium text-gray-700">End Date</label> <label for="end_date" class="block text-sm font-medium text-gray-700">End Date</label>
<input type="date" name="end_date" id="end_date" <input type="date" name="end_date" id="end_date"
class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"> class="w-full p-3 border border-gray-300 rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-primary-500">
</div> </div>
<!-- Apply Button --> <!-- Apply Button -->
<div> <div>
<button type="submit" <button type="submit"
class="w-full bg-blue-600 text-white py-2 rounded-lg font-semibold shadow-md hover:bg-blue-700 focus:outline-none"> class="w-full bg-primary-600 text-white py-2 rounded-xl font-semibold shadow-md hover:bg-primary-700 focus:outline-none">
Apply Filters Apply Filters
</button> </button>
</div> </div>
@@ -87,13 +87,16 @@
<div class="max-w-5xl mx-auto" x-data="{ activeView: 'list' }"> <div class="max-w-5xl mx-auto" x-data="{ activeView: 'list' }">
<!-- Tabs --> <!-- Tabs -->
<div class="flex border-b mb-4"> <div class="flex border-b mb-4">
<button @click="activeView = 'list'" :class="{'border-blue-600 text-blue-600': activeView === 'list'}" <button @click="activeView = 'list'" :class="{'border-primary-600 text-primary-600': activeView === 'list'}"
class="px-4 py-2 text-sm font-medium border-b-2">List View</button> class="px-4 py-2 text-sm font-medium border-b-2">List View</button>
<button @click="activeView = 'weekly'" :class="{'border-blue-600 text-blue-600': activeView === 'weekly'}" <button @click="activeView = 'weekly'"
:class="{'border-primary-600 text-primary-600': activeView === 'weekly'}"
class="px-4 py-2 text-sm font-medium border-b-2">Weekly View</button> class="px-4 py-2 text-sm font-medium border-b-2">Weekly View</button>
<button @click="activeView = 'monthly'" :class="{'border-blue-600 text-blue-600': activeView === 'monthly'}" <button @click="activeView = 'monthly'"
:class="{'border-primary-600 text-primary-600': activeView === 'monthly'}"
class="px-4 py-2 text-sm font-medium border-b-2">Monthly View</button> class="px-4 py-2 text-sm font-medium border-b-2">Monthly View</button>
<button @click="activeView = 'graph'" :class="{'border-blue-600 text-blue-600': activeView === 'graph'}" <button @click="activeView = 'graph'"
:class="{'border-primary-600 text-primary-600': activeView === 'graph'}"
class="px-4 py-2 text-sm font-medium border-b-2">Graph View</button> class="px-4 py-2 text-sm font-medium border-b-2">Graph View</button>
</div> </div>
@@ -101,7 +104,7 @@
<div x-show="activeView === 'list'" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div x-show="activeView === 'list'" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{% for reading in readings %} {% for reading in readings %}
<a href="{{ url_for('reading.edit_reading', reading_id=reading.id) }}" <a href="{{ url_for('reading.edit_reading', reading_id=reading.id) }}"
class="bg-white shadow-md rounded-lg p-4 flex flex-col justify-between relative hover:shadow-lg transition-shadow"> class="bg-white shadow-md rounded-xl p-4 flex flex-col justify-between relative hover:shadow-lg transition-shadow">
<!-- Timestamp --> <!-- Timestamp -->
<div class="absolute top-2 right-2 flex items-center text-gray-400 text-xs"> <div class="absolute top-2 right-2 flex items-center text-gray-400 text-xs">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24" <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24"
@@ -117,7 +120,7 @@
<!-- Blood Pressure --> <!-- Blood Pressure -->
<div class="text-sm text-gray-600 mb-2"> <div class="text-sm text-gray-600 mb-2">
<span class="block text-lg font-semibold text-gray-800">Blood Pressure</span> <span class="block text-lg font-semibold text-gray-800">Blood Pressure</span>
<span class="text-2xl font-bold text-blue-600">{{ reading.systolic }}</span> <span class="text-2xl font-bold text-primary-600">{{ reading.systolic }}</span>
<span class="text-lg text-gray-500">/</span> <span class="text-lg text-gray-500">/</span>
<span class="text-xl font-bold text-red-600">{{ reading.diastolic }}</span> <span class="text-xl font-bold text-red-600">{{ reading.diastolic }}</span>
<span class="text-sm text-gray-500">mmHg</span> <span class="text-sm text-gray-500">mmHg</span>
@@ -159,7 +162,7 @@
{% for page_num in pagination.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %} {% for page_num in pagination.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %}
{% if page_num %} {% if page_num %}
<a href="{{ url_for('main.dashboard', page=page_num) }}" <a href="{{ url_for('main.dashboard', page=page_num) }}"
class="px-3 py-1 rounded text-sm {% if page_num == pagination.page %}bg-blue-600 text-white{% else %}bg-gray-200 hover:bg-gray-300{% endif %}"> class="px-3 py-1 rounded text-sm {% if page_num == pagination.page %}bg-primary-600 text-white{% else %}bg-gray-200 hover:bg-gray-300{% endif %}">
{{ page_num }} {{ page_num }}
</a> </a>
{% else %} {% else %}
@@ -182,7 +185,7 @@
{% if day.readings %} {% if day.readings %}
{% for reading in day.readings %} {% for reading in day.readings %}
<a href="{{ url_for('reading.edit_reading', reading_id=reading.id) }}" <a href="{{ url_for('reading.edit_reading', reading_id=reading.id) }}"
class="block mt-2 p-0 md:p-2 bg-green-100 rounded-lg shadow hover:bg-green-200 transition"> class="block mt-2 p-0 md:p-2 bg-green-100 rounded-xl shadow hover:bg-green-200 transition">
<p class="text-xs font-medium text-green-800"> <p class="text-xs font-medium text-green-800">
{{ reading.systolic }}/{{ reading.diastolic }} mmHg {{ reading.systolic }}/{{ reading.diastolic }} mmHg
</p> </p>
@@ -250,7 +253,7 @@
</div> </div>
{% for reading in day.readings %} {% for reading in day.readings %}
<a href="{{ url_for('reading.edit_reading', reading_id=reading.id) }}" <a href="{{ url_for('reading.edit_reading', reading_id=reading.id) }}"
class="block mt-2 p-0 md:p-2 bg-green-100 rounded-lg shadow hover:bg-green-200 transition"> class="block mt-2 p-0 md:p-2 bg-green-100 rounded-xl shadow hover:bg-green-200 transition">
<p class="text-xs font-medium text-green-800"> <p class="text-xs font-medium text-green-800">
{{ reading.systolic }}/{{ reading.diastolic }} mmHg {{ reading.systolic }}/{{ reading.diastolic }} mmHg
</p> </p>

View File

@@ -1,22 +1,24 @@
{% extends "_layout.html" %} {% extends "_layout.html" %}
{% block content %} {% block content %}
<div class="bg-gray-50"> <div class="bg-gray-50 pb-20">
<!-- Hero Section --> <!-- Hero Section -->
<section class="bg-blue-600 text-white"> <section
<div class="container mx-auto px-4 py-20 text-center"> class="bg-gradient-to-br from-primary-600 to-primary-800 text-white rounded-b-3xl shadow-xl overflow-hidden relative">
<h1 class="text-4xl font-bold mb-6"> <div class="absolute inset-0 bg-white/5 pattern-dots pointer-events-none"></div>
<div class="container mx-auto px-4 py-24 text-center relative z-10">
<h1 class="text-5xl md:text-6xl font-extrabold mb-6 tracking-tight">
Welcome to BP Tracker Welcome to BP Tracker
</h1> </h1>
<p class="text-lg mb-8"> <p class="text-xl md:text-2xl mb-12 max-w-2xl mx-auto font-medium text-primary-50 leading-relaxed">
Track your blood pressure and heart rate effortlessly. Take control of your health today! Track your blood pressure and heart rate effortlessly. Take control of your health today!
</p> </p>
<div class="flex justify-center space-x-4"> <div class="flex flex-col sm:flex-row justify-center space-y-4 sm:space-y-0 sm:space-x-6">
<a href="{{ url_for('auth.signup') }}" <a href="{{ url_for('auth.signup') }}"
class="px-8 py-3 bg-white text-blue-600 font-semibold rounded-lg shadow hover:bg-gray-200"> class="px-8 py-4 bg-white text-primary-700 font-bold text-lg rounded-xl shadow-xl hover:shadow-2xl hover:bg-gray-50 transition-all duration-300 transform hover:-translate-y-1">
Get Started Get Started
</a> </a>
<a href="{{ url_for('auth.login') }}" <a href="{{ url_for('auth.login') }}"
class="px-8 py-3 bg-blue-700 text-white font-semibold rounded-lg shadow hover:bg-blue-800"> class="px-8 py-4 bg-primary-700 text-white border border-primary-500 font-bold text-lg rounded-xl shadow-lg hover:shadow-xl hover:bg-primary-600 transition-all duration-300 transform hover:-translate-y-1">
Login Login
</a> </a>
</div> </div>
@@ -24,35 +26,56 @@
</section> </section>
<!-- Features Section --> <!-- Features Section -->
<section class="container mx-auto px-4 py-16"> <section class="container mx-auto px-4 pt-24">
<h2 class="text-3xl font-bold text-center mb-8">Why Choose BP Tracker?</h2> <div class="text-center mb-16">
<div class="grid md:grid-cols-3 gap-8"> <span class="text-primary-600 font-bold tracking-wider uppercase text-sm mb-2 block">Why Track With
<div class="text-center bg-white p-6 rounded-lg shadow"> Us</span>
<svg xmlns="http://www.w3.org/2000/svg" class="w-16 h-16 text-blue-600 mx-auto mb-4" fill="none" <h2 class="text-4xl font-extrabold text-gray-900 tracking-tight">Why Choose BP Tracker?</h2>
viewBox="0 0 24 24" stroke="currentColor"> </div>
<div class="grid md:grid-cols-3 gap-10">
<div
class="text-center bg-white p-10 rounded-3xl shadow-lg border border-gray-100 hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-2">
<div
class="w-20 h-20 mx-auto bg-primary-50 rounded-2xl flex items-center justify-center mb-6 shadow-inner text-primary-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8c2.21 0 4 1.79 4 4s-1.79 4-4 4-4-1.79-4-4 1.79-4 4-4z"></path> d="M12 8c2.21 0 4 1.79 4 4s-1.79 4-4 4-4-1.79-4-4 1.79-4 4-4z"></path>
</svg> </svg>
<h3 class="text-lg font-semibold mb-2">Accurate Tracking</h3>
<p>Keep a detailed log of your blood pressure and heart rate over time.</p>
</div> </div>
<div class="text-center bg-white p-6 rounded-lg shadow"> <h3 class="text-xl font-bold text-gray-900 mb-3">Accurate Tracking</h3>
<svg xmlns="http://www.w3.org/2000/svg" class="w-16 h-16 text-blue-600 mx-auto mb-4" fill="none" <p class="text-gray-600 leading-relaxed">Keep a detailed log of your blood pressure and heart rate over
viewBox="0 0 24 24" stroke="currentColor"> time to share with your doctor.</p>
</div>
<div
class="text-center bg-white p-10 rounded-3xl shadow-lg border border-gray-100 hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-2">
<div
class="w-20 h-20 mx-auto bg-primary-50 rounded-2xl flex items-center justify-center mb-6 shadow-inner text-primary-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9.75 16.5L15 12m0 0l-5.25-4.5m5.25 4.5H3"></path> d="M9.75 16.5L15 12m0 0l-5.25-4.5m5.25 4.5H3"></path>
</svg> </svg>
<h3 class="text-lg font-semibold mb-2">Insightful Graphs</h3>
<p>Visualize your progress and identify trends with intuitive charts.</p>
</div> </div>
<div class="text-center bg-white p-6 rounded-lg shadow"> <h3 class="text-xl font-bold text-gray-900 mb-3">Insightful Graphs</h3>
<svg xmlns="http://www.w3.org/2000/svg" class="w-16 h-16 text-blue-600 mx-auto mb-4" fill="none" <p class="text-gray-600 leading-relaxed">Visualize your progress and identify health trends with our
viewBox="0 0 24 24" stroke="currentColor"> intuitive, easy-to-read charts.</p>
</div>
<div
class="text-center bg-white p-10 rounded-3xl shadow-lg border border-gray-100 hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-2">
<div
class="w-20 h-20 mx-auto bg-primary-50 rounded-2xl flex items-center justify-center mb-6 shadow-inner text-primary-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 20l-5.5-5.5M9 20V9m0 11h11"></path> d="M9 20l-5.5-5.5M9 20V9m0 11h11"></path>
</svg> </svg>
<h3 class="text-lg font-semibold mb-2">Secure and Private</h3> </div>
<p>Your data is protected with state-of-the-art security measures.</p> <h3 class="text-xl font-bold text-gray-900 mb-3">Secure and Private</h3>
<p class="text-gray-600 leading-relaxed">Your medical data is protected with state-of-the-art security
measures. Your privacy is paramount.</p>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -2,7 +2,25 @@
module.exports = { module.exports = {
content: ["./app/templates/**/*.html"], content: ["./app/templates/**/*.html"],
theme: { theme: {
extend: {}, extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
colors: {
primary: {
50: '#f0fdfa',
100: '#ccfbf1',
200: '#99f6e4',
300: '#5eead4',
400: '#2dd4bf',
500: '#14b8a6', // Teal MedTech feeling
600: '#0d9488',
700: '#0f766e',
800: '#115e59',
900: '#134e4a',
},
},
},
}, },
plugins: [], plugins: [],
}; };