From 296361c8a49f32658c4c184a6587e44cbb7d4a81 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:44:48 +0000 Subject: [PATCH 1/8] Initial plan From afd93ffd3b4fdccf851de54c8b12e9c5e0db93b7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:50:29 +0000 Subject: [PATCH 2/8] Update structs with new OpenAPI-aligned fields (Region, Application, OsItem) Co-authored-by: tayyebi <14053493+tayyebi@users.noreply.github.com> --- src/api/applications.rs | 23 +++++++++++++++++++++++ src/api/instances.rs | 1 + src/api/operating_systems.rs | 1 + src/api/regions.rs | 26 ++++++++++++++++++++++++++ src/handlers/instances.rs | 1 + src/models/os_item.rs | 7 +++++++ src/models/region.rs | 27 +++++++++++++++++++++++++++ src/services/instance_service.rs | 1 + 8 files changed, 87 insertions(+) diff --git a/src/api/applications.rs b/src/api/applications.rs index 123fce0..3023ec4 100644 --- a/src/api/applications.rs +++ b/src/api/applications.rs @@ -11,6 +11,14 @@ pub struct Application { pub category: Option, #[allow(dead_code)] pub os_compatibility: Vec, + // New OpenAPI-aligned fields + pub price: Option, + pub pricing_type: Option, + pub is_active: bool, + pub tag: Option, + pub is_experimental: bool, + pub os_family: Option, + pub os_list: Vec, } /// Load available one-click applications from the API @@ -35,6 +43,14 @@ pub async fn load_applications( Vec::new() }; + let os_list = if let Some(list) = obj.get("osList").and_then(|v| v.as_array()) { + list.iter() + .filter_map(|v| v.as_str().map(|s| s.to_string())) + .collect() + } else { + Vec::new() + }; + applications.push(Application { id: obj.get("id").and_then(|v| v.as_str()).unwrap_or("").to_string(), name: obj.get("name").and_then(|v| v.as_str()).unwrap_or("").to_string(), @@ -42,6 +58,13 @@ pub async fn load_applications( logo_url: obj.get("logoUrl").and_then(|v| v.as_str()).map(|s| s.to_string()), category: obj.get("category").and_then(|v| v.as_str()).map(|s| s.to_string()), os_compatibility: os_compat, + price: obj.get("price").and_then(|v| v.as_f64()), + pricing_type: obj.get("pricingType").and_then(|v| v.as_str()).map(|s| s.to_string()), + is_active: obj.get("isActive").and_then(|v| v.as_bool()).unwrap_or(true), + tag: obj.get("tag").and_then(|v| v.as_str()).map(|s| s.to_string()), + is_experimental: obj.get("isExperimental").and_then(|v| v.as_bool()).unwrap_or(false), + os_family: obj.get("osFamily").and_then(|v| v.as_str()).map(|s| s.to_string()), + os_list, }); } } diff --git a/src/api/instances.rs b/src/api/instances.rs index f74de4d..28ceda6 100644 --- a/src/api/instances.rs +++ b/src/api/instances.rs @@ -97,6 +97,7 @@ pub async fn load_instances_for_user( arch: os_obj.get("arch").and_then(|v| v.as_str()).map(|s| s.to_string()), min_ram: os_obj.get("minRam").and_then(|v| v.as_str()).map(|s| s.to_string()), is_default: os_obj.get("isDefault").and_then(|v| v.as_bool()).unwrap_or(false), + is_active: os_obj.get("isActive").and_then(|v| v.as_bool()).unwrap_or(true), }) } else { None diff --git a/src/api/operating_systems.rs b/src/api/operating_systems.rs index 3bc28bd..794d476 100644 --- a/src/api/operating_systems.rs +++ b/src/api/operating_systems.rs @@ -24,6 +24,7 @@ pub async fn load_os_list( arch: obj.get("arch").and_then(|v| v.as_str()).map(|s| s.to_string()), min_ram: obj.get("minRam").and_then(|v| v.as_str()).map(|s| s.to_string()), is_default: obj.get("isDefault").and_then(|v| v.as_bool()).unwrap_or(false), + is_active: obj.get("isActive").and_then(|v| v.as_bool()).unwrap_or(true), }); } } diff --git a/src/api/regions.rs b/src/api/regions.rs index 55ac40f..84bf41b 100644 --- a/src/api/regions.rs +++ b/src/api/regions.rs @@ -45,6 +45,20 @@ pub async fn load_regions( .to_string(); let latitude = obj.get("latitude").and_then(|v| v.as_f64()); let longitude = obj.get("longitude").and_then(|v| v.as_f64()); + + // New OpenAPI-aligned fields + let abbr = obj.get("abbr").and_then(|v| v.as_str()).unwrap_or("").to_string(); + let image = obj.get("image").and_then(|v| v.as_str()).map(|s| s.to_string()); + let is_active = obj.get("isActive").and_then(|v| v.as_bool()).unwrap_or(true); + let is_out_of_stock = obj.get("isOutOfStock").and_then(|v| v.as_bool()).unwrap_or(false); + let overall_activeness = obj.get("overallActiveness").and_then(|v| v.as_bool()); + let ddos_activeness = obj.get("ddosActiveness").and_then(|v| v.as_bool()); + let is_premium = obj.get("isPremium").and_then(|v| v.as_bool()).unwrap_or(false); + let is_hidden = obj.get("isHidden").and_then(|v| v.as_bool()).unwrap_or(false); + let has_offset_price = obj.get("hasOffsetPrice").and_then(|v| v.as_bool()).unwrap_or(false); + let max_discount_percent = obj.get("maxDiscountPercent").and_then(|v| v.as_f64()); + let position = obj.get("position").and_then(|v| v.as_i64()).map(|n| n as i32); + let config = obj.get("config").cloned(); let region = Region { id: id.clone(), @@ -54,6 +68,18 @@ pub async fn load_regions( city, latitude, longitude, + abbr, + image, + is_active, + is_out_of_stock, + overall_activeness, + ddos_activeness, + is_premium, + is_hidden, + has_offset_price, + max_discount_percent, + position, + config, }; regions.push(region.clone()); map.insert(id, region); diff --git a/src/handlers/instances.rs b/src/handlers/instances.rs index c01b764..6a12110 100644 --- a/src/handlers/instances.rs +++ b/src/handlers/instances.rs @@ -542,6 +542,7 @@ pub async fn instance_change_os_get( arch: os_obj.get("arch").and_then(|v| v.as_str()).map(|s| s.to_string()), min_ram: os_obj.get("minRam").and_then(|v| v.as_str()).map(|s| s.to_string()), is_default: os_obj.get("isDefault").and_then(|v| v.as_bool()).unwrap_or(false), + is_active: os_obj.get("isActive").and_then(|v| v.as_bool()).unwrap_or(true), }); } } diff --git a/src/models/os_item.rs b/src/models/os_item.rs index cea3043..0e66354 100644 --- a/src/models/os_item.rs +++ b/src/models/os_item.rs @@ -7,5 +7,12 @@ pub struct OsItem { pub family: String, pub arch: Option, pub min_ram: Option, + #[serde(default)] pub is_default: bool, + #[serde(default = "default_true")] + pub is_active: bool, +} + +fn default_true() -> bool { + true } diff --git a/src/models/region.rs b/src/models/region.rs index 2eb4424..0cf83c8 100644 --- a/src/models/region.rs +++ b/src/models/region.rs @@ -4,9 +4,36 @@ use serde::{Deserialize, Serialize}; pub struct Region { pub id: String, pub name: String, + // Old fields - kept for backward compatibility + #[serde(default)] pub slug: String, + #[serde(default)] pub country: String, + #[serde(default)] pub city: String, pub latitude: Option, pub longitude: Option, + // New OpenAPI-aligned fields + #[serde(default)] + pub abbr: String, + pub image: Option, + #[serde(default = "default_true")] + pub is_active: bool, + #[serde(default)] + pub is_out_of_stock: bool, + pub overall_activeness: Option, + pub ddos_activeness: Option, + #[serde(default)] + pub is_premium: bool, + #[serde(default)] + pub is_hidden: bool, + #[serde(default)] + pub has_offset_price: bool, + pub max_discount_percent: Option, + pub position: Option, + pub config: Option, +} + +fn default_true() -> bool { + true } diff --git a/src/services/instance_service.rs b/src/services/instance_service.rs index 962fc71..267c053 100644 --- a/src/services/instance_service.rs +++ b/src/services/instance_service.rs @@ -73,6 +73,7 @@ pub async fn get_instance_for_action(state: &AppState, instance_id: &str) -> Ins arch: os_obj.get("arch").and_then(|v| v.as_str()).map(|s| s.to_string()), min_ram: os_obj.get("minRam").and_then(|v| v.as_str()).map(|s| s.to_string()), is_default: os_obj.get("isDefault").and_then(|v| v.as_bool()).unwrap_or(false), + is_active: os_obj.get("isActive").and_then(|v| v.as_bool()).unwrap_or(true), }); } instance.region = data.get("region").and_then(|v| v.as_str()).unwrap_or("").to_string(); From 3e1b4b2135677db9e3192153bc9f76cc7bd15692 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:52:35 +0000 Subject: [PATCH 3/8] Implement mobile-first responsive design system (300px to 5K+) Co-authored-by: tayyebi <14053493+tayyebi@users.noreply.github.com> --- static/responsive.css | 244 ++++++++++++++++++++++++++ static/styles.css | 388 ++++++++++++++++++++++++++++++++++++++++++ static/tablet.css | 140 --------------- templates/base.html | 2 +- 4 files changed, 633 insertions(+), 141 deletions(-) create mode 100644 static/responsive.css delete mode 100644 static/tablet.css diff --git a/static/responsive.css b/static/responsive.css new file mode 100644 index 0000000..1ab2dfe --- /dev/null +++ b/static/responsive.css @@ -0,0 +1,244 @@ +/* Responsive enhancements for tablet and larger screens */ + +/* Tablet and larger screens (768px+) */ +@media (min-width: 768px) { + /* Restore Grid Layouts */ + .product-grid { + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + } + + .invoice-body { + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + } + + div[data-admin-grid] { + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + } + + section[role="listitem"] > div[role="list"] { + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + } + + section dl { + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + } + + section[aria-labelledby="regions-heading"] > div[role="list"] { + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + } + + section[aria-labelledby="regions-heading"] > div[role="list"] > article dl { + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + } + + section dl > div { + display: flex; + flex-direction: column; + gap: 0.25rem; + } + + .action-grid { + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: var(--space-4); + } + + /* Navigation - Sidebar Layout */ + body { + flex-direction: row; + align-items: flex-start; + } + + .nav-toggle-label { + display: none; + } + + nav { + width: 260px; + height: 100vh; + position: sticky; + top: 0; + flex-direction: column; + align-items: stretch; + flex-wrap: nowrap; + gap: var(--space-4); + border-bottom: none; + border-right: 1px solid var(--border-color); + overflow-y: auto; + flex-shrink: 0; + } + + nav > header { + flex-direction: column; + align-items: flex-start; + margin-right: 0; + width: 100%; + } + + nav > header > div:first-of-type { + width: 100%; + } + + div[role="navigation"] { + display: flex; + flex-direction: column; + align-items: stretch; + gap: var(--space-1); + width: 100%; + padding-top: 0; + border-top: none; + animation: none; + } + + div[role="navigation"] a { + padding: var(--space-2) var(--space-3); + width: 100%; + font-size: 0.9rem; + justify-content: flex-start; + } + + nav footer { + display: block; + margin-top: auto; + padding-top: var(--space-3); + border-top: 1px solid var(--border-color); + text-align: center; + font-size: 0.8rem; + color: var(--text-muted); + } + + /* Adjust header elements for sidebar layout */ + nav > header > div:last-of-type { + width: 100%; + flex: 0 0 auto; + background: var(--gray-50); + border: 1px solid var(--border-color); + padding: var(--space-3); + flex-direction: column; + align-items: flex-start; + gap: var(--space-3); + } + + nav > header > div:last-of-type form { + width: 100%; + } + + nav > header > div:last-of-type form button { + width: 100%; + padding: var(--space-2) var(--space-3); + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + background: var(--white); + cursor: pointer; + } + + nav > header > div:last-of-type form button:hover { + background: var(--gray-50); + } + + main { + flex: 1; + min-width: 0; /* Prevent overflow issues */ + padding: var(--space-5); + } +} + +/* Desktop 1024px+ */ +@media (min-width: 1024px) { + /* Increase grid columns for more screen space */ + .product-grid { + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + } + + div[data-admin-grid] { + grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); + } + + /* Wider navigation */ + nav { + width: 280px; + } + + main { + padding: var(--space-6); + } +} + +/* Large desktop 1536px+ */ +@media (min-width: 1536px) { + /* Even more columns for large screens */ + .product-grid { + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + } + + div[data-admin-grid] { + grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); + } + + /* Wider navigation for large screens */ + nav { + width: 300px; + } + + nav div[role="navigation"] a { + font-size: 1rem; + padding: var(--space-3) var(--space-4); + } + + main { + padding: var(--space-7); + } +} + +/* Full HD 1920px+ */ +@media (min-width: 1920px) { + .product-grid { + grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); + } +} + +/* 4K 2560px+ */ +@media (min-width: 2560px) { + /* Maximum columns for 4K displays */ + .product-grid { + grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); + } + + div[data-admin-grid] { + grid-template-columns: repeat(auto-fit, minmax(450px, 1fr)); + } + + /* Extra wide navigation for 4K */ + nav { + width: 360px; + } + + nav div[role="navigation"] a { + font-size: 1.1rem; + padding: var(--space-4); + } + + main { + padding: calc(var(--space-7) * 1.5); + } +} + +/* 5K+ and TVs (3840px+) */ +@media (min-width: 3840px) { + /* Ultra-wide layouts for TVs and 5K+ displays */ + .product-grid { + grid-template-columns: repeat(auto-fit, minmax(480px, 1fr)); + } + + div[data-admin-grid] { + grid-template-columns: repeat(auto-fit, minmax(520px, 1fr)); + } + + nav { + width: 420px; + } + + nav div[role="navigation"] a { + font-size: 1.25rem; + padding: var(--space-5); + } +} diff --git a/static/styles.css b/static/styles.css index 156f988..fac5282 100644 --- a/static/styles.css +++ b/static/styles.css @@ -46,6 +46,45 @@ --red-500: #ef4444; --red-600: #dc2626; --red-700: #b91c1c; + + /* Mobile-first responsive breakpoints */ + --screen-xs: 300px; /* Very small phones */ + --screen-sm: 480px; /* Small phones */ + --screen-md: 768px; /* Tablets */ + --screen-lg: 1024px; /* Laptops */ + --screen-xl: 1280px; /* Desktops */ + --screen-2xl: 1536px; /* Large desktops */ + --screen-3xl: 1920px; /* Full HD */ + --screen-4k: 2560px; /* 4K displays */ + --screen-5k: 3840px; /* 5K+ and TVs */ + + /* Container widths */ + --container-xs: 100%; + --container-sm: 100%; + --container-md: 720px; + --container-lg: 960px; + --container-xl: 1140px; + --container-2xl: 1320px; + --container-3xl: 1600px; + --container-4k: 2000px; + + /* Fluid typography - scales smoothly across all screen sizes */ + --font-size-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem); + --font-size-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem); + --font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); + --font-size-lg: clamp(1.125rem, 1rem + 0.625vw, 1.25rem); + --font-size-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem); + --font-size-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem); + --font-size-3xl: clamp(1.875rem, 1.5rem + 1.875vw, 2.5rem); + --font-size-4xl: clamp(2.25rem, 1.8rem + 2.25vw, 3rem); + + /* Fluid spacing */ + --spacing-xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.5rem); + --spacing-sm: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem); + --spacing-md: clamp(0.75rem, 0.6rem + 0.75vw, 1rem); + --spacing-lg: clamp(1rem, 0.8rem + 1vw, 1.5rem); + --spacing-xl: clamp(1.5rem, 1.2rem + 1.5vw, 2rem); + --spacing-2xl: clamp(2rem, 1.6rem + 2vw, 3rem); } @media (prefers-color-scheme: dark) { @@ -2506,3 +2545,352 @@ code { border-bottom-color: var(--red-400); color: var(--red-700); } + +/* ============================================ + MOBILE-FIRST RESPONSIVE DESIGN SYSTEM + ============================================ */ + +/* Container System with Breakpoint-based Max Widths */ +.container { + width: 100%; + margin-left: auto; + margin-right: auto; + padding-left: var(--spacing-md); + padding-right: var(--spacing-md); +} + +@media (min-width: 768px) { + .container { + max-width: var(--container-md); + padding-left: var(--spacing-lg); + padding-right: var(--spacing-lg); + } +} + +@media (min-width: 1024px) { + .container { max-width: var(--container-lg); } +} + +@media (min-width: 1280px) { + .container { max-width: var(--container-xl); } +} + +@media (min-width: 1536px) { + .container { + max-width: var(--container-2xl); + padding-left: var(--spacing-xl); + padding-right: var(--spacing-xl); + } +} + +@media (min-width: 1920px) { + .container { max-width: var(--container-3xl); } +} + +@media (min-width: 2560px) { + .container { + max-width: var(--container-4k); + padding-left: var(--spacing-2xl); + padding-right: var(--spacing-2xl); + } +} + +/* Responsive Grid System - Mobile-first */ +.grid { + display: grid; + gap: var(--spacing-md); + grid-template-columns: 1fr; +} + +/* Small phones (480px+) */ +@media (min-width: 480px) { + .grid-sm-2 { grid-template-columns: repeat(2, 1fr); } +} + +/* Tablets (768px+) */ +@media (min-width: 768px) { + .grid-md-2 { grid-template-columns: repeat(2, 1fr); } + .grid-md-3 { grid-template-columns: repeat(3, 1fr); } + .grid-md-4 { grid-template-columns: repeat(4, 1fr); } +} + +/* Laptops (1024px+) */ +@media (min-width: 1024px) { + .grid-lg-2 { grid-template-columns: repeat(2, 1fr); } + .grid-lg-3 { grid-template-columns: repeat(3, 1fr); } + .grid-lg-4 { grid-template-columns: repeat(4, 1fr); } + .grid-lg-6 { grid-template-columns: repeat(6, 1fr); } +} + +/* Large desktops (1536px+) */ +@media (min-width: 1536px) { + .grid-2xl-4 { grid-template-columns: repeat(4, 1fr); } + .grid-2xl-6 { grid-template-columns: repeat(6, 1fr); } + .grid-2xl-8 { grid-template-columns: repeat(8, 1fr); } +} + +/* 4K displays (2560px+) */ +@media (min-width: 2560px) { + .grid-4k-6 { grid-template-columns: repeat(6, 1fr); } + .grid-4k-8 { grid-template-columns: repeat(8, 1fr); } + .grid-4k-12 { grid-template-columns: repeat(12, 1fr); } +} + +/* Auto-fit grids for dynamic columns */ +.grid-auto-fit { + grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--grid-min, 250px)), 1fr)); +} + +/* Touch-Friendly UI Elements (WCAG AAA Compliant) */ +button, +a.btn, +input[type="submit"], +input[type="button"], +select { + min-height: 44px; + min-width: 44px; + padding: var(--spacing-md) var(--spacing-lg); +} + +/* Larger touch targets on mobile */ +@media (max-width: 767px) { + button, + a.btn, + input[type="submit"], + input[type="button"] { + min-height: 48px; + font-size: var(--font-size-lg); + } + + /* Stacked buttons on mobile */ + .button-group { + display: flex; + flex-direction: column; + gap: var(--spacing-sm); + } + + .button-group button, + .button-group a.btn { + width: 100%; + } +} + +/* Side-by-side buttons on larger screens */ +@media (min-width: 768px) { + .button-group { + display: flex; + flex-direction: row; + gap: var(--spacing-md); + } + + .button-group button, + .button-group a.btn { + width: auto; + } +} + +/* Responsive Tables */ +@media (max-width: 767px) { + /* Card layout on mobile */ + table.table-responsive, + table.table-responsive thead, + table.table-responsive tbody, + table.table-responsive th, + table.table-responsive td, + table.table-responsive tr { + display: block; + } + + table.table-responsive thead tr { + position: absolute; + top: -9999px; + left: -9999px; + } + + table.table-responsive tr { + margin-bottom: var(--spacing-lg); + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + padding: var(--spacing-md); + background: var(--canvas-bg); + } + + table.table-responsive td { + border: none; + position: relative; + padding-left: 50%; + padding-top: var(--spacing-sm); + padding-bottom: var(--spacing-sm); + } + + table.table-responsive td:before { + content: attr(data-label); + position: absolute; + left: var(--spacing-md); + font-weight: 600; + color: var(--text-muted); + } +} + +/* Normal table on tablet and up */ +@media (min-width: 768px) { + table.table-responsive { + width: 100%; + border-collapse: collapse; + } + + table.table-responsive th, + table.table-responsive td { + padding: var(--spacing-md); + text-align: left; + border-bottom: 1px solid var(--border-color); + } +} + +/* More generous table spacing on large screens */ +@media (min-width: 1536px) { + table.table-responsive th, + table.table-responsive td { + padding: var(--spacing-lg); + } +} + +/* Responsive Images */ +img { + max-width: 100%; + height: auto; + display: block; +} + +.region-icon { + width: 32px; + height: 32px; + border-radius: var(--border-radius); +} + +@media (min-width: 1024px) { + .region-icon { + width: 48px; + height: 48px; + } +} + +@media (min-width: 2560px) { + .region-icon { + width: 64px; + height: 64px; + } +} + +/* Badge Styles for New Fields */ +.badge { + display: inline-flex; + align-items: center; + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + font-weight: 600; + border-radius: var(--border-radius); + background-color: var(--gray-100); + color: var(--text-secondary); + border: 1px solid var(--border-color); +} + +.badge-premium { + background-color: #ffd700; + color: #000; + border-color: #ffcd00; +} + +.badge-warning { + background-color: var(--red-100); + color: var(--red-700); + border-color: var(--red-400); +} + +.badge-success { + background-color: #d1f4e0; + color: #0f5132; + border-color: #0f5132; +} + +.badge-security { + background-color: #cfe2ff; + color: #084298; + border-color: #084298; +} + +/* Mobile-first Navigation Adjustments */ +@media (max-width: 767px) { + /* Ensure nav is fixed on mobile */ + nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + } + + /* Add margin to main content to account for fixed nav */ + main { + margin-top: 70px; + } +} + +/* Tablet: Collapsible sidebar */ +@media (min-width: 768px) and (max-width: 1023px) { + body { + display: grid; + grid-template-columns: 200px 1fr; + } + + nav { + position: sticky; + top: 0; + height: 100vh; + overflow-y: auto; + } +} + +/* Desktop: Full sidebar (already handled by tablet.css) */ +@media (min-width: 1024px) { + body { + display: grid; + grid-template-columns: 260px 1fr; + } +} + +/* Large desktop: Wider sidebar */ +@media (min-width: 1536px) { + body { + grid-template-columns: 300px 1fr; + } +} + +/* 4K+: Extra wide sidebar */ +@media (min-width: 2560px) { + body { + grid-template-columns: 360px 1fr; + } +} + +/* Responsive utility classes */ +@media (max-width: 767px) { + .hide-mobile { display: none !important; } +} + +@media (min-width: 768px) { + .hide-tablet { display: none !important; } + .show-mobile { display: none !important; } +} + +@media (min-width: 1024px) { + .hide-desktop { display: none !important; } +} + +/* Fluid typography for headings */ +h1 { font-size: var(--font-size-4xl); } +h2 { font-size: var(--font-size-3xl); } +h3 { font-size: var(--font-size-2xl); } +h4 { font-size: var(--font-size-xl); } +h5 { font-size: var(--font-size-lg); } +h6 { font-size: var(--font-size-base); } diff --git a/static/tablet.css b/static/tablet.css deleted file mode 100644 index c0815e5..0000000 --- a/static/tablet.css +++ /dev/null @@ -1,140 +0,0 @@ -/* Tablet and larger screens (min-width: 768px) */ - -/* Restore Grid Layouts */ -.product-grid { - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); -} - -.invoice-body { - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); -} - -div[data-admin-grid] { - grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); -} - -section[role="listitem"] > div[role="list"] { - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); -} - -section dl { - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); -} - -section[aria-labelledby="regions-heading"] > div[role="list"] { - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); -} - -section[aria-labelledby="regions-heading"] > div[role="list"] > article dl { - grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); -} - -section dl > div { - display: flex; - flex-direction: column; - gap: 0.25rem; -} - -.action-grid { - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: var(--space-4); -} - -/* Navigation - Sidebar Layout */ -body { - flex-direction: row; - align-items: flex-start; -} - -.nav-toggle-label { - display: none; -} - -nav { - width: 260px; - height: 100vh; - position: sticky; - top: 0; - flex-direction: column; - align-items: stretch; - flex-wrap: nowrap; - gap: var(--space-4); - border-bottom: none; - border-right: 1px solid var(--border-color); - overflow-y: auto; - flex-shrink: 0; -} - -nav > header { - flex-direction: column; - align-items: flex-start; - margin-right: 0; - width: 100%; -} - -nav > header > div:first-of-type { - width: 100%; -} - -div[role="navigation"] { - display: flex; - flex-direction: column; - align-items: stretch; - gap: var(--space-1); - width: 100%; - padding-top: 0; - border-top: none; - animation: none; -} - -div[role="navigation"] a { - padding: var(--space-2) var(--space-3); - width: 100%; - font-size: 0.9rem; - justify-content: flex-start; -} - -nav footer { - display: block; - margin-top: auto; - padding-top: var(--space-3); - border-top: 1px solid var(--border-color); - text-align: center; - font-size: 0.8rem; - color: var(--text-muted); -} - -/* Adjust header elements for sidebar layout */ -nav > header > div:last-of-type { - width: 100%; - flex: 0 0 auto; - background: var(--gray-50); - border: 1px solid var(--border-color); - padding: var(--space-3); - flex-direction: column; - align-items: flex-start; - gap: var(--space-3); -} - -nav > header > div:last-of-type form { - width: 100%; -} - -nav > header > div:last-of-type form button { - width: 100%; - padding: var(--space-2) var(--space-3); - border: 1px solid var(--border-color); - border-radius: var(--border-radius); - background: var(--white); - cursor: pointer; -} - -nav > header > div:last-of-type form button:hover { - background: var(--gray-50); -} - -main { - flex: 1; - min-width: 0; /* Prevent overflow issues */ - padding: var(--space-5); -} diff --git a/templates/base.html b/templates/base.html index dbd5b53..6f0b1fa 100644 --- a/templates/base.html +++ b/templates/base.html @@ -6,7 +6,7 @@ {% block title %}Zy{% endblock %} - + {% if current_user.is_some() %} From b99ed2616d5fd4c9024c89d59280d0df5ecc944e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:55:21 +0000 Subject: [PATCH 4/8] Update templates with new struct fields and responsive classes Co-authored-by: tayyebi <14053493+tayyebi@users.noreply.github.com> --- templates/change_os_instance.html | 8 +++--- templates/instances.html | 12 ++++----- templates/step_1.html | 9 +++++-- templates/step_5.html | 43 ++++++++++++++++++++++++++----- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/templates/change_os_instance.html b/templates/change_os_instance.html index d3b6381..4ee9e8d 100644 --- a/templates/change_os_instance.html +++ b/templates/change_os_instance.html @@ -65,7 +65,7 @@

⚠️ Warning

@@ -83,12 +83,12 @@

⚠️ Warning

- Select the operating system you want to install on this instance. + Select the operating system you want to install on this instance. Unavailable OS options are disabled.