Improve month calendar
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -175,45 +175,54 @@
|
||||
</div>
|
||||
|
||||
<!-- 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) %}
|
||||
<!-- Month Name -->
|
||||
<div class="text-center">
|
||||
<h2 class="text-xl font-bold text-gray-800">{{ current_date.strftime('%B %Y') }}</h2>
|
||||
</div>
|
||||
|
||||
<!-- Day Headers -->
|
||||
<div class="grid grid-cols-7 text-center">
|
||||
<div class="font-semibold text-gray-700">Sun</div>
|
||||
<div class="font-semibold text-gray-700">Mon</div>
|
||||
<div class="font-semibold text-gray-700">Tue</div>
|
||||
<div class="font-semibold text-gray-700">Wed</div>
|
||||
<div class="font-semibold text-gray-700">Thu</div>
|
||||
<div class="font-semibold text-gray-700">Fri</div>
|
||||
<div class="font-semibold text-gray-700">Sat</div>
|
||||
<div class="grid grid-cols-7 pl-2 pr-2">
|
||||
|
||||
<div class="p-2 h-10 text-center font-bold">
|
||||
<span class="xl:block lg:block md:block sm:block hidden">Sunday</span>
|
||||
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Sun</span>
|
||||
</div>
|
||||
<div class="p-2 h-10 text-center font-bold">
|
||||
<span class="xl:block lg:block md:block sm:block hidden">Monday</span>
|
||||
<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>
|
||||
|
||||
<!-- Calendar Grid -->
|
||||
<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() %}
|
||||
<div class="grid grid-cols-7 overflow-hidden flex-1 pl-2 pr-2 w-full">
|
||||
|
||||
<!-- Empty slots for days before the 1st -->
|
||||
{% for _ in range((first_weekday + 1) % 7) %}
|
||||
<div></div>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Days of the Month -->
|
||||
{% for day in range(1, days_in_month + 1) %}
|
||||
{% 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) %}
|
||||
{% for day in month %}
|
||||
<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 %}">
|
||||
<div class="top h-5 w-full">
|
||||
<span class="text-gray-500 font-semibold">{{ day.day }}</span>
|
||||
</div>
|
||||
{% for reading in day.readings %}
|
||||
<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">
|
||||
<p class="text-xs font-medium text-green-800">
|
||||
@@ -226,11 +235,9 @@
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="h-8"></div> <!-- Placeholder for spacing -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user