Fix theme-independent login styling and improve calendar responsiveness
- Remove theme reset on logout to preserve user theme preferences - Implement hardcoded login page colors that override all theme styles - Add comprehensive overrides for Google theme affecting login forms - Optimize month view to show minimum required weeks (4-6) instead of fixed 6 - Implement dynamic calendar grid height calculations for better responsive fit - Add calendar header to print preview with updated height calculations - Update responsive breakpoints with proper header height variables 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -212,6 +212,7 @@
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Remove today highlighting in preview */
|
/* Remove today highlighting in preview */
|
||||||
.print-preview-paper .calendar-day.today,
|
.print-preview-paper .calendar-day.today,
|
||||||
.print-preview-paper .week-day-header.today,
|
.print-preview-paper .week-day-header.today,
|
||||||
|
|||||||
@@ -573,21 +573,6 @@ pub fn App() -> Html {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset theme to default when user is logged out
|
|
||||||
{
|
|
||||||
let auth_token = auth_token.clone();
|
|
||||||
use_effect_with((*auth_token).clone(), move |token| {
|
|
||||||
if token.is_none() {
|
|
||||||
// User is logged out, reset theme to default
|
|
||||||
if let Some(document) = web_sys::window().and_then(|w| w.document()) {
|
|
||||||
if let Some(root) = document.document_element() {
|
|
||||||
let _ = root.set_attribute("data-theme", "default");
|
|
||||||
let _ = root.set_attribute("data-style", "default");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch user info when token is available
|
// Fetch user info when token is available
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -114,8 +114,13 @@ pub fn month_view(props: &MonthViewProps) -> Html {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let weeks_needed = calculate_minimum_weeks_needed(first_weekday, days_in_month);
|
||||||
|
|
||||||
|
// Use calculated weeks with height-based container sizing for proper fit
|
||||||
|
let dynamic_style = format!("grid-template-rows: var(--weekday-header-height, 50px) repeat({}, 1fr);", weeks_needed);
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div class="calendar-grid">
|
<div class="calendar-grid" style={dynamic_style}>
|
||||||
// Weekday headers
|
// Weekday headers
|
||||||
<div class="weekday-header">{"Sun"}</div>
|
<div class="weekday-header">{"Sun"}</div>
|
||||||
<div class="weekday-header">{"Mon"}</div>
|
<div class="weekday-header">{"Mon"}</div>
|
||||||
@@ -238,13 +243,27 @@ pub fn month_view(props: &MonthViewProps) -> Html {
|
|||||||
}).collect::<Html>()
|
}).collect::<Html>()
|
||||||
}
|
}
|
||||||
|
|
||||||
{ render_next_month_days(days_from_prev_month.len(), days_in_month) }
|
{ render_next_month_days(days_from_prev_month.len(), days_in_month, calculate_minimum_weeks_needed(first_weekday, days_in_month)) }
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_next_month_days(prev_days_count: usize, current_days_count: u32) -> Html {
|
fn calculate_minimum_weeks_needed(first_weekday: Weekday, days_in_month: u32) -> u32 {
|
||||||
let total_slots = 42; // 6 rows x 7 days
|
let days_before = match first_weekday {
|
||||||
|
Weekday::Sun => 0,
|
||||||
|
Weekday::Mon => 1,
|
||||||
|
Weekday::Tue => 2,
|
||||||
|
Weekday::Wed => 3,
|
||||||
|
Weekday::Thu => 4,
|
||||||
|
Weekday::Fri => 5,
|
||||||
|
Weekday::Sat => 6,
|
||||||
|
};
|
||||||
|
let total_days_needed = days_before + days_in_month;
|
||||||
|
(total_days_needed + 6) / 7 // Round up to get number of weeks
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_next_month_days(prev_days_count: usize, current_days_count: u32, weeks_needed: u32) -> Html {
|
||||||
|
let total_slots = (weeks_needed * 7) as usize; // Dynamic based on weeks needed
|
||||||
let used_slots = prev_days_count + current_days_count as usize;
|
let used_slots = prev_days_count + current_days_count as usize;
|
||||||
let remaining_slots = if used_slots < total_slots {
|
let remaining_slots = if used_slots < total_slots {
|
||||||
total_slots - used_slots
|
total_slots - used_slots
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use crate::components::{ViewMode, WeekView, MonthView};
|
use crate::components::{ViewMode, WeekView, MonthView, CalendarHeader};
|
||||||
use crate::models::ical::VEvent;
|
use crate::models::ical::VEvent;
|
||||||
use crate::services::calendar_service::{UserInfo, ExternalCalendar};
|
use crate::services::calendar_service::{UserInfo, ExternalCalendar};
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use wasm_bindgen::{closure::Closure, JsCast};
|
use wasm_bindgen::{closure::Closure, JsCast};
|
||||||
|
use web_sys::MouseEvent;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
@@ -88,10 +89,11 @@ pub fn PrintPreviewModal(props: &PrintPreviewModalProps) -> Html {
|
|||||||
let calculate_print_dimensions = |start_hour: u32, end_hour: u32, time_increment: u32| -> (f64, f64, f64) {
|
let calculate_print_dimensions = |start_hour: u32, end_hour: u32, time_increment: u32| -> (f64, f64, f64) {
|
||||||
let visible_hours = (end_hour - start_hour) as f64;
|
let visible_hours = (end_hour - start_hour) as f64;
|
||||||
let slots_per_hour = if time_increment == 15 { 4.0 } else { 2.0 };
|
let slots_per_hour = if time_increment == 15 { 4.0 } else { 2.0 };
|
||||||
let header_height = 50.0; // Fixed week header height in print preview
|
let calendar_header_height = 80.0; // Calendar header height in print preview
|
||||||
|
let week_header_height = 50.0; // Fixed week header height in print preview
|
||||||
let header_border = 2.0; // Week header bottom border (2px solid)
|
let header_border = 2.0; // Week header bottom border (2px solid)
|
||||||
let container_spacing = 8.0; // Additional container spacing/margins
|
let container_spacing = 8.0; // Additional container spacing/margins
|
||||||
let total_overhead = header_height + header_border + container_spacing;
|
let total_overhead = calendar_header_height + week_header_height + header_border + container_spacing;
|
||||||
let available_height = 720.0 - total_overhead; // Available for time content
|
let available_height = 720.0 - total_overhead; // Available for time content
|
||||||
let base_unit = available_height / (visible_hours * slots_per_hour);
|
let base_unit = available_height / (visible_hours * slots_per_hour);
|
||||||
let pixels_per_hour = base_unit * slots_per_hour;
|
let pixels_per_hour = base_unit * slots_per_hour;
|
||||||
@@ -151,10 +153,11 @@ pub fn PrintPreviewModal(props: &PrintPreviewModalProps) -> Html {
|
|||||||
// Recalculate base-unit and pixels-per-hour based on actual height
|
// Recalculate base-unit and pixels-per-hour based on actual height
|
||||||
let visible_hours = (end_hour - start_hour) as f64;
|
let visible_hours = (end_hour - start_hour) as f64;
|
||||||
let slots_per_hour = if time_increment == 15 { 4.0 } else { 2.0 };
|
let slots_per_hour = if time_increment == 15 { 4.0 } else { 2.0 };
|
||||||
let header_height = 50.0;
|
let calendar_header_height = 80.0; // Calendar header height
|
||||||
|
let week_header_height = 50.0; // Week header height
|
||||||
let header_border = 2.0;
|
let header_border = 2.0;
|
||||||
let container_spacing = 8.0;
|
let container_spacing = 8.0;
|
||||||
let total_overhead = header_height + header_border + container_spacing;
|
let total_overhead = calendar_header_height + week_header_height + header_border + container_spacing;
|
||||||
let available_height = actual_height - total_overhead;
|
let available_height = actual_height - total_overhead;
|
||||||
let actual_base_unit = available_height / (visible_hours * slots_per_hour);
|
let actual_base_unit = available_height / (visible_hours * slots_per_hour);
|
||||||
let actual_pixels_per_hour = actual_base_unit * slots_per_hour;
|
let actual_pixels_per_hour = actual_base_unit * slots_per_hour;
|
||||||
@@ -320,38 +323,50 @@ pub fn PrintPreviewModal(props: &PrintPreviewModalProps) -> Html {
|
|||||||
*start_hour, *end_hour, base_unit, pixels_per_hour, *zoom_level
|
*start_hour, *end_hour, base_unit, pixels_per_hour, *zoom_level
|
||||||
)}>
|
)}>
|
||||||
<div class="print-preview-content">
|
<div class="print-preview-content">
|
||||||
{
|
<div class={classes!("calendar", match props.view_mode { ViewMode::Week => Some("week-view"), _ => None })}>
|
||||||
match props.view_mode {
|
<CalendarHeader
|
||||||
ViewMode::Week => html! {
|
current_date={props.current_date}
|
||||||
<WeekView
|
view_mode={props.view_mode.clone()}
|
||||||
key={format!("week-preview-{}-{}", *start_hour, *end_hour)}
|
on_prev={Callback::from(|_: MouseEvent| {})}
|
||||||
current_date={props.current_date}
|
on_next={Callback::from(|_: MouseEvent| {})}
|
||||||
today={props.today}
|
on_today={Callback::from(|_: MouseEvent| {})}
|
||||||
events={props.events.clone()}
|
time_increment={Some(props.time_increment)}
|
||||||
on_event_click={Callback::noop()}
|
on_time_increment_toggle={None::<Callback<MouseEvent>>}
|
||||||
user_info={props.user_info.clone()}
|
on_print={None::<Callback<MouseEvent>>}
|
||||||
external_calendars={props.external_calendars.clone()}
|
/>
|
||||||
time_increment={props.time_increment}
|
{
|
||||||
print_mode={true}
|
match props.view_mode {
|
||||||
print_pixels_per_hour={Some(pixels_per_hour)}
|
ViewMode::Week => html! {
|
||||||
print_start_hour={Some(*start_hour)}
|
<WeekView
|
||||||
/>
|
key={format!("week-preview-{}-{}", *start_hour, *end_hour)}
|
||||||
},
|
current_date={props.current_date}
|
||||||
ViewMode::Month => html! {
|
today={props.today}
|
||||||
<MonthView
|
events={props.events.clone()}
|
||||||
key={format!("month-preview-{}-{}", *start_hour, *end_hour)}
|
on_event_click={Callback::noop()}
|
||||||
current_month={props.current_date}
|
user_info={props.user_info.clone()}
|
||||||
selected_date={Some(props.selected_date)}
|
external_calendars={props.external_calendars.clone()}
|
||||||
today={props.today}
|
time_increment={props.time_increment}
|
||||||
events={props.events.clone()}
|
print_mode={true}
|
||||||
on_day_select={None::<Callback<NaiveDate>>}
|
print_pixels_per_hour={Some(pixels_per_hour)}
|
||||||
on_event_click={Callback::noop()}
|
print_start_hour={Some(*start_hour)}
|
||||||
user_info={props.user_info.clone()}
|
/>
|
||||||
external_calendars={props.external_calendars.clone()}
|
},
|
||||||
/>
|
ViewMode::Month => html! {
|
||||||
},
|
<MonthView
|
||||||
|
key={format!("month-preview-{}-{}", *start_hour, *end_hour)}
|
||||||
|
current_month={props.current_date}
|
||||||
|
selected_date={Some(props.selected_date)}
|
||||||
|
today={props.today}
|
||||||
|
events={props.events.clone()}
|
||||||
|
on_day_select={None::<Callback<NaiveDate>>}
|
||||||
|
on_event_click={Callback::noop()}
|
||||||
|
user_info={props.user_info.clone()}
|
||||||
|
external_calendars={props.external_calendars.clone()}
|
||||||
|
/>
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -586,6 +586,11 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-height: 750px) and (min-height: 600px) {
|
@media (max-height: 750px) and (min-height: 600px) {
|
||||||
|
.calendar-grid {
|
||||||
|
--calendar-header-height: 80px;
|
||||||
|
--weekday-header-height: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
padding: 0.8rem 1rem 0.5rem;
|
padding: 0.8rem 1rem 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -695,6 +700,11 @@ body {
|
|||||||
font-size: 1.2rem !important;
|
font-size: 1.2rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Adjust weekday header height for responsive row calculation */
|
||||||
|
.calendar-grid {
|
||||||
|
--weekday-header-height: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.logout-button {
|
.logout-button {
|
||||||
padding: 0.2rem 0.4rem;
|
padding: 0.2rem 0.4rem;
|
||||||
@@ -702,7 +712,12 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-height: 650px) and (min-height: 600px) {
|
@media (max-height: 650px) {
|
||||||
|
.calendar-grid {
|
||||||
|
--calendar-header-height: 75px;
|
||||||
|
--weekday-header-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
padding: 0.5rem 0.8rem 0.3rem;
|
padding: 0.5rem 0.8rem 0.3rem;
|
||||||
}
|
}
|
||||||
@@ -827,6 +842,11 @@ body {
|
|||||||
font-size: 1.1rem !important;
|
font-size: 1.1rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Most compact weekday header height for responsive row calculation */
|
||||||
|
.calendar-grid {
|
||||||
|
--weekday-header-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.logout-button {
|
.logout-button {
|
||||||
padding: 0.15rem 0.3rem;
|
padding: 0.15rem 0.3rem;
|
||||||
@@ -843,9 +863,9 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-form, .register-form {
|
.login-form, .register-form {
|
||||||
background: white;
|
background: #ffffff !important;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
border-radius: var(--border-radius-medium);
|
border-radius: 12px;
|
||||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
@@ -854,7 +874,8 @@ body {
|
|||||||
.login-form h2, .register-form h2 {
|
.login-form h2, .register-form h2 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
color: #333;
|
color: #333333 !important;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
@@ -868,6 +889,15 @@ body {
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form .form-group label,
|
||||||
|
.register-form .form-group label,
|
||||||
|
.login-form label,
|
||||||
|
.register-form label {
|
||||||
|
color: #333333 !important;
|
||||||
|
font-size: 1rem !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-group input {
|
.form-group input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: var(--control-padding);
|
padding: var(--control-padding);
|
||||||
@@ -877,12 +907,41 @@ body {
|
|||||||
transition: border-color 0.2s;
|
transition: border-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form .form-group input,
|
||||||
|
.register-form .form-group input,
|
||||||
|
.login-form input[type="text"],
|
||||||
|
.login-form input[type="email"],
|
||||||
|
.login-form input[type="password"],
|
||||||
|
.login-form input[type="url"],
|
||||||
|
.register-form input[type="text"],
|
||||||
|
.register-form input[type="email"],
|
||||||
|
.register-form input[type="password"],
|
||||||
|
.register-form input[type="url"] {
|
||||||
|
background: #ffffff !important;
|
||||||
|
color: #333333 !important;
|
||||||
|
border: 1px solid #d1d5db !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-group input:focus {
|
.form-group input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--input-border-focus);
|
border-color: var(--input-border-focus);
|
||||||
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
|
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form .form-group input:focus,
|
||||||
|
.register-form .form-group input:focus,
|
||||||
|
.login-form input[type="text"]:focus,
|
||||||
|
.login-form input[type="email"]:focus,
|
||||||
|
.login-form input[type="password"]:focus,
|
||||||
|
.login-form input[type="url"]:focus,
|
||||||
|
.register-form input[type="text"]:focus,
|
||||||
|
.register-form input[type="email"]:focus,
|
||||||
|
.register-form input[type="password"]:focus,
|
||||||
|
.register-form input[type="url"]:focus {
|
||||||
|
border-color: #667eea !important;
|
||||||
|
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-group input:disabled {
|
.form-group input:disabled {
|
||||||
background-color: var(--background-tertiary);
|
background-color: var(--background-tertiary);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
@@ -924,7 +983,7 @@ body {
|
|||||||
.remember-checkbox label {
|
.remember-checkbox label {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 0.55rem;
|
font-size: 0.55rem;
|
||||||
color: #888;
|
color: #888888;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@@ -956,15 +1015,51 @@ body {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form .password-toggle-btn,
|
||||||
|
.register-form .password-toggle-btn {
|
||||||
|
position: absolute !important;
|
||||||
|
right: 0.75rem !important;
|
||||||
|
background: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
color: #888888 !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
padding: 0.25rem !important;
|
||||||
|
font-size: 1rem !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
transition: color 0.2s ease !important;
|
||||||
|
z-index: 1 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.password-toggle-btn:hover {
|
.password-toggle-btn:hover {
|
||||||
color: #555;
|
color: #555;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form .password-toggle-btn:hover,
|
||||||
|
.register-form .password-toggle-btn:hover {
|
||||||
|
color: #555555 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.password-toggle-btn:focus {
|
.password-toggle-btn:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
color: #667eea;
|
color: #667eea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form .password-toggle-btn:focus,
|
||||||
|
.register-form .password-toggle-btn:focus {
|
||||||
|
outline: none !important;
|
||||||
|
color: #667eea !important;
|
||||||
|
background: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.login-button, .register-button {
|
.login-button, .register-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: var(--control-padding);
|
padding: var(--control-padding);
|
||||||
@@ -998,6 +1093,13 @@ body {
|
|||||||
border: 1px solid #f5c6cb;
|
border: 1px solid #f5c6cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form .error-message,
|
||||||
|
.register-form .error-message {
|
||||||
|
background-color: #ef4444;
|
||||||
|
color: #ffffff;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
}
|
||||||
|
|
||||||
.auth-links {
|
.auth-links {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
@@ -1179,10 +1281,13 @@ body {
|
|||||||
.calendar-grid {
|
.calendar-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(7, 1fr);
|
grid-template-columns: repeat(7, 1fr);
|
||||||
grid-template-rows: auto repeat(6, 1fr);
|
/* grid-template-rows set dynamically in component with calculated weeks */
|
||||||
flex: 1;
|
position: relative;
|
||||||
|
height: calc(100% - var(--calendar-header-height, 88px));
|
||||||
background: var(--calendar-bg, white);
|
background: var(--calendar-bg, white);
|
||||||
gap: 0;
|
gap: 0;
|
||||||
|
--weekday-header-height: 50px; /* Base weekday header height */
|
||||||
|
--calendar-header-height: 88px; /* Base calendar header height */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Week View Container */
|
/* Week View Container */
|
||||||
|
|||||||
Reference in New Issue
Block a user