diff --git a/src/assets/scss/_mixins.scss b/src/assets/scss/_mixins.scss index bbbe9ce..e5189ee 100644 --- a/src/assets/scss/_mixins.scss +++ b/src/assets/scss/_mixins.scss @@ -28,6 +28,16 @@ padding: $spacing-xl; } +// Ensure cards occupy consistent vertical space and can stretch horizontally +@mixin card-dimensions($min-width: 320px, $min-height: 180px) { + min-width: $min-width; + min-height: $min-height; + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; +} + /// Standard vertical stack for card-style list rows (dashboard recent apps, server detail apps, etc.) @mixin card-list-vertical($gap: $spacing-md) { display: flex; @@ -141,4 +151,63 @@ font-weight: $font-weight-semibold; background-color: rgba($color, 0.1); // color: darken($color, 20%); +} + +// Compact chip used for stat chips and small interactive chips +@mixin chip( + $bg: white, + $border-color: $border-color, + $radius: $radius-md, + $padding: $spacing-md $spacing-lg, + $gap: $spacing-md, + $hover-translate: -2px, + $hover-shadow: $shadow-sm, + $font-size: $font-size-base +) { + display: flex; + align-items: center; + gap: $gap; + padding: $padding; + background: $bg; + border: 2px solid $border-color; + border-radius: $radius; + cursor: pointer; + transition: all $transition-base; + font-size: $font-size; + + &:hover:not(:disabled) { + border-color: $primary; + transform: translateY($hover-translate); + box-shadow: $hover-shadow; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} + +// Reusable action button styles; accepts border width and radius +@mixin action-btn($border-width: 1px, $radius: $radius-sm, $padding: $spacing-xs $spacing-md, $font-size: $font-size-sm) { + background: none; + border: $border-width solid $border-color; + padding: $padding; + border-radius: $radius; + cursor: pointer; + font-size: $font-size; + color: $text-primary; + font-weight: $font-weight-medium; + transition: all $transition-base; + + &:hover { + background-color: rgba($primary, 0.05); + color: $text-primary; + transform: translateY(-1px); + border-color: $primary; + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } } \ No newline at end of file diff --git a/src/routes/Apps/App.scss b/src/routes/Apps/App.scss index c0b528b..aa45b8f 100644 --- a/src/routes/Apps/App.scss +++ b/src/routes/Apps/App.scss @@ -47,25 +47,7 @@ // Action buttons .action-btn { - padding: $spacing-sm $spacing-lg; - border: 2px solid $border-color; - border-radius: $radius-md; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - cursor: pointer; - transition: all $transition-base; - background: white; - color: $text-primary; - - &:disabled { - opacity: 0.6; - cursor: not-allowed; - } - - &:hover:not(:disabled) { - transform: translateY(-1px); - box-shadow: $shadow-md; - } + @include action-btn(2px, $radius-md, $spacing-sm $spacing-lg, $font-size-sm); &.primary { background: $primary; diff --git a/src/routes/Apps/Apps.scss b/src/routes/Apps/Apps.scss index b3b966d..78bb452 100644 --- a/src/routes/Apps/Apps.scss +++ b/src/routes/Apps/Apps.scss @@ -21,31 +21,12 @@ } .stat-chip { - display: flex; - align-items: center; - gap: $spacing-sm; - padding: $spacing-sm $spacing-md; - background: white; - border: 2px solid $border-color; - border-radius: $radius-md; - cursor: pointer; - transition: all $transition-base; - font-size: $font-size-base; - - &:hover:not(:disabled) { - border-color: $primary; - transform: translateY(-2px); - box-shadow: $shadow-sm; - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } + @include chip(); &.active { - border-color: $primary; - background: rgba($primary, 0.05); + border-color: $primary-dark; + background: $bg-primary; + box-shadow: 0 0 0 3px rgba($primary-dark, 0.08); } .stat-label { @@ -63,23 +44,7 @@ } } -.reset-filters-btn { - padding: $spacing-sm $spacing-md; - background: $bg-secondary; - border: 2px solid $border-color; - border-radius: $radius-md; - cursor: pointer; - transition: all $transition-base; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $text-secondary; - - &:hover { - background: white; - color: $text-primary; - border-color: $primary; - } -} +/* reset-filters-btn removed — outline on stat-chip indicates active filters */ // Apps table specific styles .apps-table-container { @@ -181,15 +146,7 @@ gap: $spacing-sm; .action-btn { - background: none; - border: 1px solid $border-color; - padding: $spacing-xs $spacing-md; - border-radius: $radius-sm; - cursor: pointer; - font-size: $font-size-sm; - color: $text-primary; - font-weight: $font-weight-medium; - transition: all $transition-base; + @include action-btn(1px, $radius-sm, $spacing-xs $spacing-md, $font-size-sm); &:hover { background-color: $primary; diff --git a/src/routes/Apps/Apps.tsx b/src/routes/Apps/Apps.tsx index 9ca5703..b49d4c5 100644 --- a/src/routes/Apps/Apps.tsx +++ b/src/routes/Apps/Apps.tsx @@ -94,19 +94,19 @@ export const Apps: React.FC = () => { setShowUpgradesOnly(false); }; - const toggleUpgrades = () => setShowUpgradesOnly(prev => !prev); - const toggleChaos = () => setShowChaosOnly(prev => !prev); - - // Show only apps that need upgrades - const filterByUpgrades = () => { - setShowUpgradesOnly(true); - setFilterStatus('all'); + const toggleUpgrades = () => { + setShowUpgradesOnly(!showUpgradesOnly); + if (!showUpgradesOnly) { + setFilterStatus('all'); + setShowChaosOnly(false); + } }; - // Show only chaos apps - const filterByChaos = () => { - setFilterStatus('chaos'); - setShowUpgradesOnly(false); + const toggleChaos = () => { + const newChaosState = !showChaosOnly; + setShowChaosOnly(newChaosState); + setFilterStatus(newChaosState ? 'chaos' : 'all'); + if (newChaosState) setShowUpgradesOnly(false); }; if (loading) { @@ -150,7 +150,6 @@ export const Apps: React.FC = () => { Apps {stats.total} - navigate('/servers')} diff --git a/src/routes/Dashboard/_Dashboard.scss b/src/routes/Dashboard/_Dashboard.scss index f804d61..dbbc5dc 100644 --- a/src/routes/Dashboard/_Dashboard.scss +++ b/src/routes/Dashboard/_Dashboard.scss @@ -38,7 +38,6 @@ flex: 1; h4 { - margin: 0 0 $spacing-xs; font-size: $font-size-lg; color: $text-primary; font-weight: $font-weight-semibold; diff --git a/src/routes/Recipes/Recipes.scss b/src/routes/Recipes/Recipes.scss index ec084d7..df6348c 100644 --- a/src/routes/Recipes/Recipes.scss +++ b/src/routes/Recipes/Recipes.scss @@ -14,7 +14,7 @@ // Servers grid .recipes-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: $spacing-xl; margin-bottom: $spacing-xl; @@ -26,6 +26,7 @@ // Server card .recipe-card { @include card; + @include card-dimensions(320px, 180px); display: grid; grid-template-rows: 1fr 2fr auto 0.9fr; transition: transform $transition-base, box-shadow $transition-base; @@ -110,21 +111,7 @@ gap: $spacing-sm; .action-btn { flex: 1; - padding: $spacing-sm $spacing-md; - border: 2px solid $border-color; - background: none; - color: $text-primary; - border-radius: $radius-md; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - cursor: pointer; - transition: all $transition-base; - - &:hover { - background-color: rgba($primary, 0.1); - border-color: $primary; - transform: translateY(-1px); - } + @include action-btn(2px, $radius-md, $spacing-sm $spacing-md, $font-size-sm); &.primary { background-color: $primary; diff --git a/src/routes/Servers/Server.scss b/src/routes/Servers/Server.scss index 0976afd..203169f 100644 --- a/src/routes/Servers/Server.scss +++ b/src/routes/Servers/Server.scss @@ -47,25 +47,7 @@ // Action buttons (shared with App view, could be moved to global) .action-btn { - padding: $spacing-sm $spacing-lg; - border: 2px solid $border-color; - border-radius: $radius-md; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - cursor: pointer; - transition: all $transition-base; - background: white; - color: $text-primary; - - &:disabled { - opacity: 0.6; - cursor: not-allowed; - } - - &:hover:not(:disabled) { - transform: translateY(-1px); - box-shadow: $shadow-md; - } + @include action-btn(2px, $radius-md, $spacing-sm $spacing-lg, $font-size-sm); &.primary { background: $primary; diff --git a/src/routes/Servers/Servers.scss b/src/routes/Servers/Servers.scss index 5ba2c7f..19ce2f7 100644 --- a/src/routes/Servers/Servers.scss +++ b/src/routes/Servers/Servers.scss @@ -14,10 +14,12 @@ // Servers grid .servers-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: $spacing-xl; margin-bottom: $spacing-xl; + align-items: stretch; + @media (max-width: 768px) { grid-template-columns: 1fr; } @@ -27,6 +29,7 @@ .server-card { @include card; @include card-hover-lift(-4px, $shadow-xl); + @include card-dimensions(320px, 180px); display: flex; flex-direction: column; position: relative; @@ -116,21 +119,7 @@ .action-btn { flex: 1; - padding: $spacing-sm $spacing-md; - border: 2px solid $border-color; - background: none; - color: $text-primary; - border-radius: $radius-md; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - cursor: pointer; - transition: all $transition-base; - - &:hover { - background-color: rgba($primary, 0.1); - border-color: $primary; - transform: translateY(-1px); - } + @include action-btn(2px, $radius-md, $spacing-sm $spacing-md, $font-size-sm); &.primary { background-color: $primary; diff --git a/src/routes/Servers/Servers.tsx b/src/routes/Servers/Servers.tsx index ac26ed5..f6c9da4 100644 --- a/src/routes/Servers/Servers.tsx +++ b/src/routes/Servers/Servers.tsx @@ -102,11 +102,7 @@ export const Servers: React.FC = () => { return filtered; }, [servers, searchTerm, sortBy, showUpgradesOnly]); -const resetFilters = () => { - setSearchTerm(''); - setSortBy('name'); - setShowUpgradesOnly(false); -}; +// resetFilters removed; clearing filters is implicit via toggles if (loading) { return ( @@ -141,15 +137,6 @@ const resetFilters = () => { {/* Compact Stats Row */} - - Servers - {stats.totalServers} - - navigate('/apps')} @@ -178,14 +165,7 @@ const resetFilters = () => { {stats.totalChaos} - {(searchTerm || sortBy !== 'name' || showUpgradesOnly) && ( - - Clear filters - - )} + {/* Clear filters button removed — use stat-chip outlines for active filters */} {/* Filters */}