From 73567c185c2aaaa2cd17a4effc6d669190514b36 Mon Sep 17 00:00:00 2001 From: Connor Johnstone Date: Mon, 1 Sep 2025 20:08:05 -0400 Subject: [PATCH 1/3] Implement comprehensive style system with Google Calendar theme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a complete style system alongside the existing theme system, allowing users to switch between different UI styles while maintaining theme color variations. **Core Features:** - Style enum (Default, Google Calendar) separate from Theme enum - Hot-swappable stylesheets with dynamic loading - Style preference persistence (localStorage + database) - Style picker UI in sidebar below theme picker **Frontend Implementation:** - Add Style enum to sidebar.rs with value/display methods - Implement dynamic stylesheet loading in app.rs - Add style picker dropdown with proper styling - Handle style state management and persistence - Add web-sys features for HtmlLinkElement support **Backend Integration:** - Add calendar_style column to user_preferences table - Update all database operations (insert/update/select) - Extend API models for style preference - Add migration for existing users **Google Calendar Style:** - Clean Material Design-inspired interface - White sidebar with proper contrast - Enhanced calendar grid with subtle shadows - Improved event styling with hover effects - Google Sans typography throughout - Professional color scheme and spacing **Technical Details:** - Trunk asset management for stylesheet copying - High CSS specificity to override theme styles - Modular CSS architecture for easy extensibility - Comprehensive text contrast fixes - Enhanced calendar cells and navigation Users can now choose between the original gradient design (Default) and a clean Google Calendar-inspired interface (Google Calendar), with full preference persistence across sessions. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .dockerignore | 2 +- .../migrations/004_add_style_preference.sql | 2 + backend/src/auth.rs | 1 + backend/src/db.rs | 10 +- backend/src/handlers/preferences.rs | 5 + backend/src/models.rs | 2 + calendar.db | Bin 20480 -> 57344 bytes compose.yml | 7 +- frontend/Cargo.toml | 2 + frontend/index.html | 1 + frontend/src/app.rs | 76 + frontend/src/components/sidebar.rs | 59 + frontend/styles.css | 155 + frontend/styles.css.backup | 3501 +++++++++++++++++ frontend/styles/base.css | 51 + frontend/styles/default.css | 3501 +++++++++++++++++ frontend/styles/google.css | 645 +++ 17 files changed, 8012 insertions(+), 8 deletions(-) create mode 100644 backend/migrations/004_add_style_preference.sql create mode 100644 frontend/styles.css.backup create mode 100644 frontend/styles/base.css create mode 100644 frontend/styles/default.css create mode 100644 frontend/styles/google.css diff --git a/.dockerignore b/.dockerignore index 665431c..e85971e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -38,4 +38,4 @@ calendar.db **/tests/ # Migrations (not needed for builds) -migrations/ \ No newline at end of file +migrations/ diff --git a/backend/migrations/004_add_style_preference.sql b/backend/migrations/004_add_style_preference.sql new file mode 100644 index 0000000..ff26dd5 --- /dev/null +++ b/backend/migrations/004_add_style_preference.sql @@ -0,0 +1,2 @@ +-- Add calendar style preference to user preferences +ALTER TABLE user_preferences ADD COLUMN calendar_style TEXT DEFAULT 'default'; \ No newline at end of file diff --git a/backend/src/auth.rs b/backend/src/auth.rs index b62e182..cb47cba 100644 --- a/backend/src/auth.rs +++ b/backend/src/auth.rs @@ -91,6 +91,7 @@ impl AuthService { calendar_time_increment: preferences.calendar_time_increment, calendar_view_mode: preferences.calendar_view_mode, calendar_theme: preferences.calendar_theme, + calendar_style: preferences.calendar_style, calendar_colors: preferences.calendar_colors, }, }) diff --git a/backend/src/db.rs b/backend/src/db.rs index aa286aa..0f6fdc7 100644 --- a/backend/src/db.rs +++ b/backend/src/db.rs @@ -93,6 +93,7 @@ pub struct UserPreferences { pub calendar_time_increment: Option, pub calendar_view_mode: Option, pub calendar_theme: Option, + pub calendar_style: Option, pub calendar_colors: Option, // JSON string pub updated_at: DateTime, } @@ -106,6 +107,7 @@ impl UserPreferences { calendar_time_increment: Some(15), calendar_view_mode: Some("month".to_string()), calendar_theme: Some("light".to_string()), + calendar_style: Some("default".to_string()), calendar_colors: None, updated_at: Utc::now(), } @@ -264,14 +266,15 @@ impl<'a> PreferencesRepository<'a> { sqlx::query( "INSERT INTO user_preferences (user_id, calendar_selected_date, calendar_time_increment, - calendar_view_mode, calendar_theme, calendar_colors, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?)", + calendar_view_mode, calendar_theme, calendar_style, calendar_colors, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(&prefs.user_id) .bind(&prefs.calendar_selected_date) .bind(&prefs.calendar_time_increment) .bind(&prefs.calendar_view_mode) .bind(&prefs.calendar_theme) + .bind(&prefs.calendar_style) .bind(&prefs.calendar_colors) .bind(&prefs.updated_at) .execute(self.db.pool()) @@ -286,7 +289,7 @@ impl<'a> PreferencesRepository<'a> { sqlx::query( "UPDATE user_preferences SET calendar_selected_date = ?, calendar_time_increment = ?, - calendar_view_mode = ?, calendar_theme = ?, + calendar_view_mode = ?, calendar_theme = ?, calendar_style = ?, calendar_colors = ?, updated_at = ? WHERE user_id = ?", ) @@ -294,6 +297,7 @@ impl<'a> PreferencesRepository<'a> { .bind(&prefs.calendar_time_increment) .bind(&prefs.calendar_view_mode) .bind(&prefs.calendar_theme) + .bind(&prefs.calendar_style) .bind(&prefs.calendar_colors) .bind(Utc::now()) .bind(&prefs.user_id) diff --git a/backend/src/handlers/preferences.rs b/backend/src/handlers/preferences.rs index 74a656c..aa405e2 100644 --- a/backend/src/handlers/preferences.rs +++ b/backend/src/handlers/preferences.rs @@ -38,6 +38,7 @@ pub async fn get_preferences( calendar_time_increment: preferences.calendar_time_increment, calendar_view_mode: preferences.calendar_view_mode, calendar_theme: preferences.calendar_theme, + calendar_style: preferences.calendar_style, calendar_colors: preferences.calendar_colors, })) } @@ -78,6 +79,9 @@ pub async fn update_preferences( if request.calendar_theme.is_some() { preferences.calendar_theme = request.calendar_theme; } + if request.calendar_style.is_some() { + preferences.calendar_style = request.calendar_style; + } if request.calendar_colors.is_some() { preferences.calendar_colors = request.calendar_colors; } @@ -94,6 +98,7 @@ pub async fn update_preferences( calendar_time_increment: preferences.calendar_time_increment, calendar_view_mode: preferences.calendar_view_mode, calendar_theme: preferences.calendar_theme, + calendar_style: preferences.calendar_style, calendar_colors: preferences.calendar_colors, }), )) diff --git a/backend/src/models.rs b/backend/src/models.rs index b14f9f8..d53b020 100644 --- a/backend/src/models.rs +++ b/backend/src/models.rs @@ -28,6 +28,7 @@ pub struct UserPreferencesResponse { pub calendar_time_increment: Option, pub calendar_view_mode: Option, pub calendar_theme: Option, + pub calendar_style: Option, pub calendar_colors: Option, } @@ -37,6 +38,7 @@ pub struct UpdatePreferencesRequest { pub calendar_time_increment: Option, pub calendar_view_mode: Option, pub calendar_theme: Option, + pub calendar_style: Option, pub calendar_colors: Option, } diff --git a/calendar.db b/calendar.db index 46985a5d858d3264494da0f23f66e2dd0d14ac01..0fd94154ce2c67030b4474e910cbd3072b1a7440 100644 GIT binary patch literal 57344 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 diff --git a/compose.yml b/compose.yml index ac729d4..ad09c3e 100644 --- a/compose.yml +++ b/compose.yml @@ -1,17 +1,16 @@ services: calendar-backend: build: . - env_file: - - .env ports: - "3000:3000" volumes: - ./data/site_dist:/srv/www + - ./data/db:/db calendar-frontend: image: caddy - env_file: - - .env + environment: + - BACKEND_API_URL=http://localhost:3000/api ports: - "80:80" - "443:443" diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 407cf33..cd2759d 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -13,6 +13,8 @@ web-sys = { version = "0.3", features = [ "HtmlSelectElement", "HtmlInputElement", "HtmlTextAreaElement", + "HtmlLinkElement", + "HtmlHeadElement", "Event", "MouseEvent", "InputEvent", diff --git a/frontend/index.html b/frontend/index.html index de3186b..452dfb4 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -6,6 +6,7 @@ +