Improve month calendar

This commit is contained in:
Peter Stockings
2024-12-29 01:02:11 +11:00
parent ff66583b7b
commit 07ae62c633
3 changed files with 216 additions and 33 deletions

View File

@@ -156,6 +156,35 @@ def dashboard():
badges.append("Night Owl: Logged Readings Every Night for a Week") badges.append("Night Owl: Logged Readings Every Night for a Week")
return badges 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 # Get the first and last reading timestamps
first_reading_timestamp, last_reading_timestamp = get_reading_date_range(current_user.id) 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.relative_timestamp = humanize.naturaltime(now - reading.timestamp)
reading.local_timestamp = utc.localize(reading.timestamp).astimezone(local_tz) 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 # Calculate weekly summary and progress badges
systolic_avg, diastolic_avg, heart_rate_avg = calculate_weekly_summary(readings) systolic_avg, diastolic_avg, heart_rate_avg = calculate_weekly_summary(readings)
badges = calculate_progress_badges(readings) badges = calculate_progress_badges(readings)
@@ -223,6 +254,7 @@ def dashboard():
start_date=start_date, start_date=start_date,
end_date=end_date, end_date=end_date,
readings_by_date=readings_by_date, readings_by_date=readings_by_date,
month = month_view,
date=date, date=date,
timedelta=timedelta timedelta=timedelta
) )

View File

@@ -717,6 +717,14 @@ video {
margin-top: 1.5rem; margin-top: 1.5rem;
} }
.-mb-px {
margin-bottom: -1px;
}
.ml-0 {
margin-left: 0px;
}
.block { .block {
display: block; display: block;
} }
@@ -766,6 +774,14 @@ video {
height: 2rem; height: 2rem;
} }
.h-10 {
height: 2.5rem;
}
.h-36 {
height: 9rem;
}
.w-16 { .w-16 {
width: 4rem; 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)); 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-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr)); grid-template-columns: repeat(1, minmax(0, 1fr));
} }
@@ -842,6 +862,10 @@ video {
flex-wrap: wrap; flex-wrap: wrap;
} }
.items-start {
align-items: flex-start;
}
.items-center { .items-center {
align-items: center; align-items: center;
} }
@@ -904,6 +928,16 @@ video {
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
} }
.overflow-hidden {
overflow: hidden;
}
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.rounded { .rounded {
border-radius: 0.25rem; border-radius: 0.25rem;
} }
@@ -916,6 +950,10 @@ video {
border-radius: 0.5rem; border-radius: 0.5rem;
} }
.rounded-md {
border-radius: 0.375rem;
}
.border { .border {
border-width: 1px; border-width: 1px;
} }
@@ -924,6 +962,10 @@ video {
border-width: 2px; border-width: 2px;
} }
.border-4 {
border-width: 4px;
}
.border-b { .border-b {
border-bottom-width: 1px; border-bottom-width: 1px;
} }
@@ -947,6 +989,11 @@ video {
border-color: rgb(255 255 255 / var(--tw-border-opacity, 1)); 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 { .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));
@@ -1098,6 +1145,16 @@ video {
padding-bottom: 1rem; padding-bottom: 1rem;
} }
.px-0 {
padding-left: 0px;
padding-right: 0px;
}
.py-1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.pl-2 { .pl-2 {
padding-left: 0.5rem; padding-left: 0.5rem;
} }
@@ -1106,6 +1163,18 @@ video {
padding-top: 1.5rem; 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-left {
text-align: left; text-align: left;
} }
@@ -1166,6 +1235,14 @@ video {
font-weight: 600; font-weight: 600;
} }
.font-light {
font-weight: 300;
}
.leading-none {
line-height: 1;
}
.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));
@@ -1383,7 +1460,42 @@ video {
text-decoration-line: underline; 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) { @media (min-width: 768px) {
.md\:ml-2 {
margin-left: 0.5rem;
}
.md\:block {
display: block;
}
.md\:hidden {
display: none;
}
.md\:w-auto { .md\:w-auto {
width: auto; width: auto;
} }
@@ -1403,9 +1515,22 @@ video {
.md\:p-4 { .md\:p-4 {
padding: 1rem; padding: 1rem;
} }
.md\:px-0\.5 {
padding-left: 0.125rem;
padding-right: 0.125rem;
}
} }
@media (min-width: 1024px) { @media (min-width: 1024px) {
.lg\:ml-2 {
margin-left: 0.5rem;
}
.lg\:block {
display: block;
}
.lg\:flex { .lg\:flex {
display: flex; display: flex;
} }
@@ -1426,7 +1551,26 @@ video {
align-items: center; align-items: center;
} }
.lg\:px-0\.5 {
padding-left: 0.125rem;
padding-right: 0.125rem;
}
.lg\:pt-0 { .lg\:pt-0 {
padding-top: 0px; padding-top: 0px;
} }
}
@media (min-width: 1280px) {
.xl\:block {
display: block;
}
.xl\:hidden {
display: none;
}
.xl\:flex-row {
flex-direction: row;
}
} }

View File

@@ -175,45 +175,54 @@
</div> </div>
<!-- Monthly View --> <!-- Monthly View -->
<div x-show="activeView === 'monthly'" class="space-y-4"> <div class="flex flex-col px-2 py-2 -mb-px" x-show="activeView === 'monthly'">
{% set current_date = date.today().replace(day=1) %} {% set current_date = date.today().replace(day=1) %}
<!-- Month Name --> <!-- Month Name -->
<div class="text-center"> <div class="text-center">
<h2 class="text-xl font-bold text-gray-800">{{ current_date.strftime('%B %Y') }}</h2> <h2 class="text-xl font-bold text-gray-800">{{ current_date.strftime('%B %Y') }}</h2>
</div> </div>
<!-- Day Headers --> <div class="grid grid-cols-7 pl-2 pr-2">
<div class="grid grid-cols-7 text-center">
<div class="font-semibold text-gray-700">Sun</div> <div class="p-2 h-10 text-center font-bold">
<div class="font-semibold text-gray-700">Mon</div> <span class="xl:block lg:block md:block sm:block hidden">Sunday</span>
<div class="font-semibold text-gray-700">Tue</div> <span class="xl:hidden lg:hidden md:hidden sm:hidden block">Sun</span>
<div class="font-semibold text-gray-700">Wed</div> </div>
<div class="font-semibold text-gray-700">Thu</div> <div class="p-2 h-10 text-center font-bold">
<div class="font-semibold text-gray-700">Fri</div> <span class="xl:block lg:block md:block sm:block hidden">Monday</span>
<div class="font-semibold text-gray-700">Sat</div> <span class="xl:hidden lg:hidden md:hidden sm:hidden block">Mon</span>
</div>
<div class="p-2 h-10 text-center font-bold">
<span class="xl:block lg:block md:block sm:block hidden">Tuesday</span>
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Tue</span>
</div>
<div class="p-2 h-10 text-center font-bold">
<span class="xl:block lg:block md:block sm:block hidden">Wednesday</span>
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Wed</span>
</div>
<div class="p-2 h-10 text-center font-bold">
<span class="xl:block lg:block md:block sm:block hidden">Thursday</span>
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Thu</span>
</div>
<div class="p-2 h-10 text-center font-bold">
<span class="xl:block lg:block md:block sm:block hidden">Friday</span>
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Fri</span>
</div>
<div class="p-2 h-10 text-center font-bold">
<span class="xl:block lg:block md:block sm:block hidden">Saturday</span>
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Sat</span>
</div>
</div> </div>
<!-- Calendar Grid --> <div class="grid grid-cols-7 overflow-hidden flex-1 pl-2 pr-2 w-full">
<div class="grid grid-cols-7 text-center">
{% 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() %}
<!-- Empty slots for days before the 1st --> {% for day in month %}
{% for _ in range((first_weekday + 1) % 7) %} <div
<div></div> class="{% if day.is_today %}rounded-md border-4 border-green-50{% endif %} border flex flex-col h-36 sm:h-40 md:h-30 lg:h-30 mx-auto mx-auto overflow-hidden w-full pt-2 pl-1 cursor-pointer {% if day.is_in_current_month %}bg-gray-100{% endif %}">
{% endfor %} <div class="top h-5 w-full">
<span class="text-gray-500 font-semibold">{{ day.day }}</span>
<!-- Days of the Month --> </div>
{% for day in range(1, days_in_month + 1) %} {% for reading in day.readings %}
{% set current_day = current_date.replace(day=day) %}
<div class="border p-1 md:p-4 bg-gray-50 relative">
<!-- Day Label -->
<div class="text-sm font-bold text-gray-500 text-left">{{ current_day.day }}</div>
<!-- Readings -->
{% if current_day in readings_by_date %}
{% for reading in readings_by_date[current_day]|sort(attribute="timestamp", reverse = True) %}
<a href="{{ url_for('main.edit_reading', reading_id=reading.id) }}" <a href="{{ url_for('main.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-lg shadow hover:bg-green-200 transition">
<p class="text-xs font-medium text-green-800"> <p class="text-xs font-medium text-green-800">
@@ -226,11 +235,9 @@
</div> </div>
</a> </a>
{% endfor %} {% endfor %}
{% else %}
<div class="h-8"></div> <!-- Placeholder for spacing -->
{% endif %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>