diff --git a/app/routes.py b/app/routes.py index f874566..8ba9a4d 100644 --- a/app/routes.py +++ b/app/routes.py @@ -3,6 +3,7 @@ from io import StringIO import io from flask import Blueprint, Response, make_response, render_template, redirect, request, send_file, url_for, flash from werkzeug.security import generate_password_hash, check_password_hash +from werkzeug.http import http_date from app.models import Profile, Reading, db, User from app.forms import DeleteForm, LoginForm, ProfileForm, ReadingForm, SignupForm from flask_login import login_user, login_required, current_user, logout_user @@ -46,8 +47,11 @@ def logout(): flash('You have been logged out.', 'success') return redirect(url_for('auth.login')) # Redirect to login page or home page +@main.route('/', methods=['GET']) +def landing(): + return render_template('landing.html') -@main.route('/', methods=['GET', 'POST']) +@main.route('/dashboard', methods=['GET', 'POST']) @login_required def dashboard(): # Default values @@ -245,8 +249,10 @@ def profile_image(user_id): image_data = base64.b64decode(profile.profile_pic) response = make_response(image_data) response.headers.set('Content-Type', 'image/jpeg') - # Cache for 1 day - response.headers.set('Cache-Control', 'public, max-age=86400') + response.headers.set('Cache-Control', 'public, max-age=86400') # Cache for 1 day + response.headers.set('ETag', str(hash(profile.profile_pic))) # Unique ETag for the image + response.headers.set('Last-Modified', http_date(datetime.utcnow().timestamp())) + return response else: # Serve the default SVG if no profile picture is found diff --git a/app/static/css/tailwind.css b/app/static/css/tailwind.css index 4fd0a11..1842a62 100644 --- a/app/static/css/tailwind.css +++ b/app/static/css/tailwind.css @@ -625,10 +625,18 @@ video { margin-bottom: 1.5rem; } +.mb-8 { + margin-bottom: 2rem; +} + .ml-2 { margin-left: 0.5rem; } +.ml-4 { + margin-left: 1rem; +} + .mr-1 { margin-right: 0.25rem; } @@ -697,6 +705,14 @@ video { display: none; } +.h-12 { + height: 3rem; +} + +.h-16 { + height: 4rem; +} + .h-24 { height: 6rem; } @@ -717,6 +733,14 @@ video { height: 2rem; } +.w-12 { + width: 3rem; +} + +.w-16 { + width: 4rem; +} + .w-24 { width: 6rem; } @@ -813,24 +837,16 @@ video { gap: 1rem; } +.gap-8 { + gap: 2rem; +} + .space-x-2 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(0.5rem * var(--tw-space-x-reverse)); margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); } -.space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.space-y-6 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); -} - .space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(1rem * var(--tw-space-x-reverse)); @@ -843,6 +859,18 @@ video { margin-left: calc(1.5rem * calc(1 - var(--tw-space-x-reverse))); } +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} + +.space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); +} + .overflow-hidden { overflow: hidden; } @@ -894,6 +922,11 @@ video { background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1)); } +.bg-blue-700 { + --tw-bg-opacity: 1; + background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1)); +} + .bg-gray-100 { --tw-bg-opacity: 1; background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1)); @@ -949,20 +982,10 @@ video { --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); } -.from-gray-500 { - --tw-gradient-from: #6b7280 var(--tw-gradient-from-position); - --tw-gradient-to: rgb(107 114 128 / 0) var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} - .to-blue-700 { --tw-gradient-to: #1d4ed8 var(--tw-gradient-to-position); } -.to-gray-700 { - --tw-gradient-to: #374151 var(--tw-gradient-to-position); -} - .fill-current { fill: currentColor; } @@ -1007,16 +1030,31 @@ video { padding-right: 1.5rem; } +.px-8 { + padding-left: 2rem; + padding-right: 2rem; +} + .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; } +.py-16 { + padding-top: 4rem; + padding-bottom: 4rem; +} + .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; } +.py-20 { + padding-top: 5rem; + padding-bottom: 5rem; +} + .py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; @@ -1053,6 +1091,11 @@ video { line-height: 2.25rem; } +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + .text-base { font-size: 1rem; line-height: 1.5rem; @@ -1170,6 +1213,16 @@ video { background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1)); } +.hover\:bg-blue-800:hover { + --tw-bg-opacity: 1; + background-color: rgb(30 64 175 / var(--tw-bg-opacity, 1)); +} + +.hover\:bg-gray-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1)); +} + .hover\:bg-gray-400:hover { --tw-bg-opacity: 1; background-color: rgb(156 163 175 / var(--tw-bg-opacity, 1)); @@ -1190,31 +1243,6 @@ video { background-color: rgb(185 28 28 / var(--tw-bg-opacity, 1)); } -.hover\:bg-gray-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1)); -} - -.hover\:from-blue-600:hover { - --tw-gradient-from: #2563eb var(--tw-gradient-from-position); - --tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} - -.hover\:from-gray-600:hover { - --tw-gradient-from: #4b5563 var(--tw-gradient-from-position); - --tw-gradient-to: rgb(75 85 99 / 0) var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} - -.hover\:to-blue-800:hover { - --tw-gradient-to: #1e40af var(--tw-gradient-to-position); -} - -.hover\:to-gray-800:hover { - --tw-gradient-to: #1f2937 var(--tw-gradient-to-position); -} - .hover\:text-blue-800:hover { --tw-text-opacity: 1; color: rgb(30 64 175 / var(--tw-text-opacity, 1)); @@ -1225,6 +1253,11 @@ video { color: rgb(229 231 235 / var(--tw-text-opacity, 1)); } +.hover\:text-gray-900:hover { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity, 1)); +} + .hover\:text-red-800:hover { --tw-text-opacity: 1; color: rgb(153 27 27 / var(--tw-text-opacity, 1)); @@ -1235,11 +1268,6 @@ video { color: rgb(255 255 255 / var(--tw-text-opacity, 1)); } -.hover\:text-gray-900:hover { - --tw-text-opacity: 1; - color: rgb(17 24 39 / var(--tw-text-opacity, 1)); -} - .hover\:underline:hover { text-decoration-line: underline; } @@ -1318,6 +1346,10 @@ video { .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } + + .md\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } } @media (min-width: 1024px) { diff --git a/app/templates/landing.html b/app/templates/landing.html new file mode 100644 index 0000000..f24eadc --- /dev/null +++ b/app/templates/landing.html @@ -0,0 +1,61 @@ +{% extends "_layout.html" %} +{% block content %} +
+ Track your blood pressure and heart rate effortlessly. Take control of your health today! +
+Keep a detailed log of your blood pressure and heart rate over time.
+Visualize your progress and identify trends with intuitive charts.
+Your data is protected with state-of-the-art security measures.
+