diff --git a/app/routes.py b/app/routes.py index b3b7c6d..03540c5 100644 --- a/app/routes.py +++ b/app/routes.py @@ -156,6 +156,35 @@ def dashboard(): badges.append("Night Owl: Logged Readings Every Night for a Week") return badges + + def generate_monthly_calendar(readings, selected_date, local_timezone): + # Convert selected date to user's timezone and extract the start/end dates + today = datetime.now(local_timezone).date() + date = selected_date.astimezone(local_timezone).date() + first_day_of_month = date.replace(day=1) + days_to_subtract = (first_day_of_month.weekday() + 1) % 7 + start_date = first_day_of_month - timedelta(days=days_to_subtract) + end_date = start_date + timedelta(days=6 * 7 - 1) + + # Group readings by day + readings_by_day = {} + for reading in readings: + local_date = reading.timestamp.astimezone(local_timezone).date() + readings_by_day.setdefault(local_date, []).append(reading) + + # Build calendar days + calendar = [] + current_date = start_date + while current_date <= end_date: + calendar.append({ + 'day': current_date.day, + 'is_today': current_date == today, + 'is_in_current_month': current_date.month == date.month, + 'readings': readings_by_day.get(current_date, []), + }) + current_date += timedelta(days=1) + + return calendar # Get the first and last reading timestamps first_reading_timestamp, last_reading_timestamp = get_reading_date_range(current_user.id) @@ -188,6 +217,8 @@ def dashboard(): reading.relative_timestamp = humanize.naturaltime(now - reading.timestamp) reading.local_timestamp = utc.localize(reading.timestamp).astimezone(local_tz) + month_view = generate_monthly_calendar(readings, now, local_tz) + # Calculate weekly summary and progress badges systolic_avg, diastolic_avg, heart_rate_avg = calculate_weekly_summary(readings) badges = calculate_progress_badges(readings) @@ -223,6 +254,7 @@ def dashboard(): start_date=start_date, end_date=end_date, readings_by_date=readings_by_date, + month = month_view, date=date, timedelta=timedelta ) diff --git a/app/static/css/tailwind.css b/app/static/css/tailwind.css index f010bb8..6452d5a 100644 --- a/app/static/css/tailwind.css +++ b/app/static/css/tailwind.css @@ -717,6 +717,14 @@ video { margin-top: 1.5rem; } +.-mb-px { + margin-bottom: -1px; +} + +.ml-0 { + margin-left: 0px; +} + .block { display: block; } @@ -766,6 +774,14 @@ video { height: 2rem; } +.h-10 { + height: 2.5rem; +} + +.h-36 { + height: 9rem; +} + .w-16 { width: 4rem; } @@ -826,6 +842,10 @@ video { 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)); } +.cursor-pointer { + cursor: pointer; +} + .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } @@ -842,6 +862,10 @@ video { flex-wrap: wrap; } +.items-start { + align-items: flex-start; +} + .items-center { align-items: center; } @@ -904,6 +928,16 @@ video { margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); } +.overflow-hidden { + overflow: hidden; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .rounded { border-radius: 0.25rem; } @@ -916,6 +950,10 @@ video { border-radius: 0.5rem; } +.rounded-md { + border-radius: 0.375rem; +} + .border { border-width: 1px; } @@ -924,6 +962,10 @@ video { border-width: 2px; } +.border-4 { + border-width: 4px; +} + .border-b { border-bottom-width: 1px; } @@ -947,6 +989,11 @@ video { border-color: rgb(255 255 255 / var(--tw-border-opacity, 1)); } +.border-green-50 { + --tw-border-opacity: 1; + border-color: rgb(240 253 244 / var(--tw-border-opacity, 1)); +} + .bg-blue-600 { --tw-bg-opacity: 1; background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1)); @@ -1098,6 +1145,16 @@ video { padding-bottom: 1rem; } +.px-0 { + padding-left: 0px; + padding-right: 0px; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + .pl-2 { padding-left: 0.5rem; } @@ -1106,6 +1163,18 @@ video { padding-top: 1.5rem; } +.pl-1 { + padding-left: 0.25rem; +} + +.pr-2 { + padding-right: 0.5rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + .text-left { text-align: left; } @@ -1166,6 +1235,14 @@ video { font-weight: 600; } +.font-light { + font-weight: 300; +} + +.leading-none { + line-height: 1; +} + .text-blue-600 { --tw-text-opacity: 1; color: rgb(37 99 235 / var(--tw-text-opacity, 1)); @@ -1383,7 +1460,42 @@ video { text-decoration-line: underline; } +@media (min-width: 640px) { + .sm\:ml-0\.5 { + margin-left: 0.125rem; + } + + .sm\:block { + display: block; + } + + .sm\:hidden { + display: none; + } + + .sm\:h-40 { + height: 10rem; + } + + .sm\:px-0\.5 { + padding-left: 0.125rem; + padding-right: 0.125rem; + } +} + @media (min-width: 768px) { + .md\:ml-2 { + margin-left: 0.5rem; + } + + .md\:block { + display: block; + } + + .md\:hidden { + display: none; + } + .md\:w-auto { width: auto; } @@ -1403,9 +1515,22 @@ video { .md\:p-4 { padding: 1rem; } + + .md\:px-0\.5 { + padding-left: 0.125rem; + padding-right: 0.125rem; + } } @media (min-width: 1024px) { + .lg\:ml-2 { + margin-left: 0.5rem; + } + + .lg\:block { + display: block; + } + .lg\:flex { display: flex; } @@ -1426,7 +1551,26 @@ video { align-items: center; } + .lg\:px-0\.5 { + padding-left: 0.125rem; + padding-right: 0.125rem; + } + .lg\:pt-0 { padding-top: 0px; } +} + +@media (min-width: 1280px) { + .xl\:block { + display: block; + } + + .xl\:hidden { + display: none; + } + + .xl\:flex-row { + flex-direction: row; + } } \ No newline at end of file diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 9573657..d0d67ec 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -175,45 +175,54 @@ -
+
{% set current_date = date.today().replace(day=1) %}

{{ current_date.strftime('%B %Y') }}

- -
-
Sun
-
Mon
-
Tue
-
Wed
-
Thu
-
Fri
-
Sat
+
+ +
+ + Sun +
+
+ + Mon +
+
+ + Tue +
+
+ + Wed +
+
+ + Thu +
+
+ + Fri +
+
+ + Sat +
- -
- {% set days_in_month = (current_date.replace(month=current_date.month % 12 + 1, day=1) - - timedelta(days=1)).day %} - {% set first_weekday = current_date.weekday() %} +
- - {% for _ in range((first_weekday + 1) % 7) %} -
- {% endfor %} - - - {% for day in range(1, days_in_month + 1) %} - {% set current_day = current_date.replace(day=day) %} -
- -
{{ current_day.day }}
- - - {% if current_day in readings_by_date %} - {% for reading in readings_by_date[current_day]|sort(attribute="timestamp", reverse = True) %} + {% for day in month %} +
+
+ {{ day.day }} +
+ {% for reading in day.readings %}

@@ -226,11 +235,9 @@

{% endfor %} - {% else %} -
- {% endif %}
{% endfor %} +