From 044f30101390483522e2f66eb621152e9aa478a1 Mon Sep 17 00:00:00 2001 From: rafaeldpsilva Date: Tue, 2 Sep 2025 16:19:20 +0100 Subject: [PATCH] Add SCSS styles for base, components, layouts, and utilities (not on use) --- src/assets/scss/abstracts/_mixins.scss | 181 +++++++ src/assets/scss/abstracts/_variables.scss | 83 ++++ src/assets/scss/base/_reset.scss | 34 ++ src/assets/scss/base/_typography.scss | 56 +++ src/assets/scss/components/_buttons.scss | 133 ++++++ src/assets/scss/components/_cards.scss | 452 ++++++++++++++++++ src/assets/scss/components/_forms.scss | 47 ++ src/assets/scss/components/_modals.scss | 256 ++++++++++ src/assets/scss/layouts/_dashboard.scss | 27 ++ src/assets/scss/layouts/_grid.scss | 52 ++ src/assets/scss/main.scss | 23 + src/assets/scss/pages/_home.scss | 167 +++++++ src/assets/scss/pages/_sensor-management.scss | 114 +++++ src/assets/scss/utilities/_helpers.scss | 109 +++++ 14 files changed, 1734 insertions(+) create mode 100644 src/assets/scss/abstracts/_mixins.scss create mode 100644 src/assets/scss/abstracts/_variables.scss create mode 100644 src/assets/scss/base/_reset.scss create mode 100644 src/assets/scss/base/_typography.scss create mode 100644 src/assets/scss/components/_buttons.scss create mode 100644 src/assets/scss/components/_cards.scss create mode 100644 src/assets/scss/components/_forms.scss create mode 100644 src/assets/scss/components/_modals.scss create mode 100644 src/assets/scss/layouts/_dashboard.scss create mode 100644 src/assets/scss/layouts/_grid.scss create mode 100644 src/assets/scss/main.scss create mode 100644 src/assets/scss/pages/_home.scss create mode 100644 src/assets/scss/pages/_sensor-management.scss create mode 100644 src/assets/scss/utilities/_helpers.scss diff --git a/src/assets/scss/abstracts/_mixins.scss b/src/assets/scss/abstracts/_mixins.scss new file mode 100644 index 0000000..34a04f4 --- /dev/null +++ b/src/assets/scss/abstracts/_mixins.scss @@ -0,0 +1,181 @@ +// Responsive breakpoints +@mixin respond-above($breakpoint) { + @media (min-width: $breakpoint) { + @content; + } +} + +@mixin respond-below($breakpoint) { + @media (max-width: #{$breakpoint - 1px}) { + @content; + } +} + +// Flexbox utilities +@mixin flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +@mixin flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +@mixin flex-column { + display: flex; + flex-direction: column; +} + +// Grid utilities +@mixin grid-responsive($columns-mobile: 1, $columns-tablet: 2, $columns-desktop: 3, $gap: $spacing-md) { + display: grid; + gap: $gap; + grid-template-columns: repeat($columns-mobile, 1fr); + + @include respond-above($breakpoint-md) { + grid-template-columns: repeat($columns-tablet, 1fr); + } + + @include respond-above($breakpoint-lg) { + grid-template-columns: repeat($columns-desktop, 1fr); + } +} + +// Button mixins +@mixin button-base { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: $radius-md; + font-weight: 500; + transition: all $transition-fast; + cursor: pointer; + border: none; + text-decoration: none; + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} + +@mixin button-primary { + @include button-base; + background-color: $primary; + color: white; + + &:hover:not(:disabled) { + background-color: $primary-dark; + } +} + +@mixin button-secondary { + @include button-base; + background-color: $gray-100; + color: $gray-700; + border: 1px solid $gray-200; + + &:hover:not(:disabled) { + background-color: $gray-200; + } +} + +// Card mixins +@mixin card-base { + background-color: white; + border-radius: $radius-2xl; + box-shadow: $shadow-sm; + border: 1px solid $gray-100; +} + +@mixin card-padding($size: md) { + @if $size == sm { + padding: $spacing-md; + } @else if $size == md { + padding: $spacing-lg; + } @else if $size == lg { + padding: $spacing-xl; + } +} + +// Status indicators +@mixin status-indicator($color) { + width: 0.5rem; + height: 0.5rem; + border-radius: 50%; + background-color: $color; +} + +// Sensor type styling +@mixin sensor-type-style($bg-color, $text-color) { + background-color: $bg-color; + color: $text-color; + padding: $spacing-sm; + border-radius: $radius-lg; +} + +// Battery indicator +@mixin battery-indicator { + height: 0.25rem; + background-color: $gray-200; + border-radius: 9999px; + overflow: hidden; + + .fill { + height: 100%; + border-radius: inherit; + transition: all $transition-normal; + + &.good { + background-color: $battery-good; + } + + &.warning { + background-color: $battery-warning; + } + + &.critical { + background-color: $battery-critical; + } + } +} + +// Custom slider +@mixin custom-slider { + width: 100%; + height: 0.5rem; + background-color: $gray-200; + border-radius: $radius-lg; + appearance: none; + cursor: pointer; + + &::-webkit-slider-thumb { + appearance: none; + height: 1.25rem; + width: 1.25rem; + border-radius: 50%; + background: $primary; + cursor: pointer; + box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.2); + } + + &::-moz-range-thumb { + height: 1.25rem; + width: 1.25rem; + border-radius: 50%; + background: $primary; + cursor: pointer; + border: none; + box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.2); + } +} + +// Truncate text +@mixin truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} \ No newline at end of file diff --git a/src/assets/scss/abstracts/_variables.scss b/src/assets/scss/abstracts/_variables.scss new file mode 100644 index 0000000..d8dc78a --- /dev/null +++ b/src/assets/scss/abstracts/_variables.scss @@ -0,0 +1,83 @@ +// Colors +$primary: #3b82f6; +$primary-light: #60a5fa; +$primary-dark: #2563eb; + +$secondary: #6b7280; +$secondary-light: #9ca3af; +$secondary-dark: #4b5563; + +$success: #10b981; +$warning: #f59e0b; +$danger: #ef4444; +$info: #06b6d4; + +$gray-50: #f9fafb; +$gray-100: #f3f4f6; +$gray-200: #e5e7eb; +$gray-300: #d1d5db; +$gray-400: #9ca3af; +$gray-500: #6b7280; +$gray-600: #4b5563; +$gray-700: #374151; +$gray-800: #1f2937; +$gray-900: #111827; + +// Status Colors +$status-online: #10b981; +$status-offline: #6b7280; +$status-error: #ef4444; + +// Sensor Type Colors +$sensor-energy-bg: #fef3c7; +$sensor-energy-text: #b45309; +$sensor-co2-bg: #dcfce7; +$sensor-co2-text: #166534; +$sensor-temperature-bg: #fee2e2; +$sensor-temperature-text: #991b1b; +$sensor-humidity-bg: #dbeafe; +$sensor-humidity-text: #1e40af; +$sensor-hvac-bg: #cffafe; +$sensor-hvac-text: #155e75; +$sensor-lighting-bg: #fef3c7; +$sensor-lighting-text: #92400e; +$sensor-security-bg: #f3e8ff; +$sensor-security-text: #7c2d12; + +// Battery Colors +$battery-good: #10b981; +$battery-warning: #f59e0b; +$battery-critical: #ef4444; + +// Spacing +$spacing-xs: 0.25rem; +$spacing-sm: 0.5rem; +$spacing-md: 1rem; +$spacing-lg: 1.5rem; +$spacing-xl: 2rem; +$spacing-2xl: 3rem; + +// Border Radius +$radius-sm: 0.375rem; +$radius-md: 0.5rem; +$radius-lg: 0.75rem; +$radius-xl: 1rem; +$radius-2xl: 1.5rem; + +// Shadows +$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); +$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); +$shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); +$shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + +// Transitions +$transition-fast: 150ms ease-in-out; +$transition-normal: 300ms ease-in-out; +$transition-slow: 500ms ease-in-out; + +// Breakpoints +$breakpoint-sm: 640px; +$breakpoint-md: 768px; +$breakpoint-lg: 1024px; +$breakpoint-xl: 1280px; +$breakpoint-2xl: 1536px; \ No newline at end of file diff --git a/src/assets/scss/base/_reset.scss b/src/assets/scss/base/_reset.scss new file mode 100644 index 0000000..08bd122 --- /dev/null +++ b/src/assets/scss/base/_reset.scss @@ -0,0 +1,34 @@ +// CSS Reset and Base Styles +* { + box-sizing: border-box; +} + +html { + line-height: 1.15; + -webkit-text-size-adjust: 100%; +} + +body { + margin: 0; + font-family: + ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', + 'Noto Color Emoji'; + background-color: $gray-100; + color: $gray-900; +} + +ul, +ol { + margin: 0; + padding: 0; + list-style: none; +} + +button { + font-family: inherit; +} + +a { + color: inherit; + text-decoration: none; +} diff --git a/src/assets/scss/base/_typography.scss b/src/assets/scss/base/_typography.scss new file mode 100644 index 0000000..423a125 --- /dev/null +++ b/src/assets/scss/base/_typography.scss @@ -0,0 +1,56 @@ +// Typography Styles +h1, h2, h3, h4, h5, h6 { + margin: 0; + font-weight: 600; +} + +h1 { + font-size: 2rem; + line-height: 1.2; +} + +h2 { + font-size: 1.5rem; + line-height: 1.3; +} + +h3 { + font-size: 1.25rem; + line-height: 1.4; +} + +h4 { + font-size: 1.125rem; + line-height: 1.4; +} + +h5 { + font-size: 1rem; + line-height: 1.5; +} + +h6 { + font-size: 0.875rem; + line-height: 1.5; +} + +p { + margin: 0; + line-height: 1.6; +} + +// Text utilities +.text-xs { font-size: 0.75rem; } +.text-sm { font-size: 0.875rem; } +.text-base { font-size: 1rem; } +.text-lg { font-size: 1.125rem; } +.text-xl { font-size: 1.25rem; } +.text-2xl { font-size: 1.5rem; } + +.font-medium { font-weight: 500; } +.font-semibold { font-weight: 600; } +.font-bold { font-weight: 700; } + +.text-center { text-align: center; } +.text-left { text-align: left; } +.text-right { text-align: right; } \ No newline at end of file diff --git a/src/assets/scss/components/_buttons.scss b/src/assets/scss/components/_buttons.scss new file mode 100644 index 0000000..a61fdb8 --- /dev/null +++ b/src/assets/scss/components/_buttons.scss @@ -0,0 +1,133 @@ +// Button Base Styles +.btn { + @include button-base; + padding: $spacing-sm $spacing-md; + font-size: 0.875rem; + + &--primary { + @include button-primary; + } + + &--secondary { + @include button-secondary; + } + + &--sm { + padding: $spacing-xs $spacing-sm; + font-size: 0.75rem; + } + + &--md { + padding: $spacing-sm $spacing-md; + font-size: 0.875rem; + } + + &--lg { + padding: $spacing-md $spacing-lg; + font-size: 1rem; + } + + &--icon-only { + padding: $spacing-sm; + width: auto; + height: auto; + } + + &--full-width { + width: 100%; + } +} + +// Toggle Button Group +.toggle-group { + @include flex-center; + gap: $spacing-sm; + background-color: $gray-100; + border-radius: $radius-lg; + padding: $spacing-xs; + + .toggle-btn { + padding: $spacing-sm * 1.5 $spacing-md; + border-radius: $radius-md; + font-size: 0.875rem; + font-weight: 500; + transition: all $transition-fast; + cursor: pointer; + border: none; + background: none; + + @include flex-center; + gap: $spacing-xs; + + &.active { + background-color: white; + color: $gray-900; + box-shadow: $shadow-sm; + } + + &.inactive { + color: $gray-600; + + &:hover { + color: $gray-900; + } + } + + .icon { + width: 1rem; + height: 1rem; + } + } +} + +// Action Buttons +.action-button { + @include flex-center; + gap: $spacing-sm; + padding: $spacing-sm $spacing-md; + border-radius: $radius-lg; + font-size: 0.875rem; + font-weight: 500; + transition: all $transition-fast; + cursor: pointer; + border: 1px solid $gray-200; + background-color: $gray-50; + color: $gray-700; + + &:hover:not(:disabled) { + background-color: $gray-100; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .icon { + font-size: 1rem; + } + + .text { + @include truncate; + } +} + +// Close Button +.close-button { + padding: $spacing-sm; + border-radius: 50%; + transition: background-color $transition-fast; + background: none; + border: none; + cursor: pointer; + + &:hover { + background-color: $gray-100; + } + + .icon { + width: 1.25rem; + height: 1.25rem; + color: $gray-400; + } +} \ No newline at end of file diff --git a/src/assets/scss/components/_cards.scss b/src/assets/scss/components/_cards.scss new file mode 100644 index 0000000..72cafc6 --- /dev/null +++ b/src/assets/scss/components/_cards.scss @@ -0,0 +1,452 @@ +// Base Card Styles +.card { + @include card-base; + + &--padding-sm { + @include card-padding(sm); + } + + &--padding-md { + @include card-padding(md); + } + + &--padding-lg { + @include card-padding(lg); + } +} + +// Simple Sensor Card +.simple-sensor-card { + @include card-base; + @include card-padding(md); + + &__header { + @include flex-between; + margin-bottom: $spacing-md; + } + + &__info { + @include flex-center; + gap: $spacing-sm; + + .icon-container { + padding: $spacing-sm * 1.5; + border-radius: $radius-lg; + } + + .details { + h3 { + font-weight: 500; + color: $gray-900; + font-size: 0.875rem; + margin: 0; + } + + p { + font-size: 0.75rem; + color: $gray-500; + margin: 0; + } + } + } + + &__status { + @include flex-center; + gap: $spacing-xs; + + .indicator { + @include status-indicator($status-offline); + + &--online { + background-color: $status-online; + } + + &--offline { + background-color: $status-offline; + } + + &--error { + background-color: $status-error; + } + } + + .label { + font-size: 0.75rem; + color: $gray-500; + text-transform: capitalize; + } + } + + &__values { + margin-bottom: $spacing-md; + + .grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: $spacing-sm; + + .metric { + background-color: $gray-50; + border-radius: $radius-md; + padding: $spacing-sm; + + .label { + color: $gray-600; + font-size: 0.75rem; + margin-bottom: $spacing-xs; + } + + .value { + font-weight: 500; + color: $gray-900; + + .unit { + color: $gray-500; + } + } + } + } + } + + &__actions { + .title { + font-size: 0.75rem; + font-weight: 500; + color: $gray-600; + margin-bottom: $spacing-sm; + } + + .buttons { + display: flex; + gap: $spacing-xs; + flex-wrap: wrap; + + .action-btn { + @include flex-center; + gap: $spacing-xs; + padding: $spacing-xs $spacing-sm; + background-color: $gray-100; + color: $gray-700; + border-radius: $radius-md; + font-size: 0.75rem; + font-weight: 500; + transition: all $transition-fast; + border: none; + cursor: pointer; + + &:hover:not(:disabled) { + background-color: $gray-200; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .icon { + font-size: 0.75rem; + } + + .text { + @include truncate; + } + } + + .more-btn { + @extend .action-btn; + background-color: #dbeafe; + color: #1e40af; + + &:hover:not(:disabled) { + background-color: #bfdbfe; + } + } + } + } + + &__monitor-only { + font-size: 0.75rem; + color: $gray-500; + text-align: center; + padding: $spacing-sm; + background-color: $gray-50; + border-radius: $radius-md; + } +} + +// Detailed Sensor Card +.detailed-sensor-card { + @include card-base; + overflow: hidden; + + &__header { + @include card-padding(md); + border-bottom: 1px solid $gray-100; + + .content { + @include flex-between; + align-items: flex-start; + } + + .info { + @include flex-center; + gap: $spacing-md; + + .icon-container { + padding: $spacing-sm; + border-radius: $radius-lg; + + .icon { + font-size: 1.125rem; + } + } + + .details { + h3 { + font-weight: 500; + color: $gray-900; + margin: 0; + } + + p { + font-size: 0.875rem; + color: $gray-500; + margin: 0; + } + } + } + + .status { + @include flex-center; + gap: $spacing-sm; + + .indicator { + @include status-indicator($status-offline); + + &--online { + background-color: $status-online; + } + + &--offline { + background-color: $status-offline; + } + + &--error { + background-color: $status-error; + } + } + + .label { + font-size: 0.75rem; + color: $gray-500; + text-transform: capitalize; + } + } + } + + &__content { + @include card-padding(md); + + .space-y-4 > * + * { + margin-top: $spacing-md; + } + + .section-title { + display: block; + font-size: 0.875rem; + font-weight: 500; + color: $gray-700; + margin-bottom: $spacing-sm; + } + } + + &__room-select { + width: 100%; + padding: $spacing-md; + border: 1px solid $gray-200; + border-radius: $radius-lg; + background-color: white; + font-size: 0.875rem; + } + + &__tags { + display: flex; + flex-wrap: wrap; + gap: $spacing-xs; + margin-bottom: $spacing-sm; + + .tag { + padding: $spacing-xs $spacing-sm; + background-color: $gray-100; + color: $gray-700; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 500; + } + } + + &__capabilities { + display: flex; + flex-wrap: wrap; + gap: $spacing-xs; + + .capability { + padding: $spacing-xs $spacing-sm; + background-color: #dbeafe; + color: #1e40af; + border-radius: $radius-md; + font-size: 0.75rem; + font-weight: 500; + } + } + + &__values-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: $spacing-sm; + font-size: 0.75rem; + + .metric { + background-color: $gray-50; + border-radius: $radius-md; + padding: $spacing-sm; + + .label { + color: $gray-600; + margin-bottom: $spacing-xs; + } + + .value { + font-weight: 500; + color: $gray-900; + + .unit { + color: $gray-500; + } + } + } + } + + &__device-info { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: $spacing-sm; + font-size: 0.75rem; + color: $gray-600; + + .info-item { + .label { + font-weight: 500; + } + + .value { + margin-top: $spacing-xs; + } + } + + .battery-info { + @include flex-center; + gap: $spacing-xs; + + .battery-bar { + @include battery-indicator; + width: 0.75rem; + } + } + + .signal-info { + @include flex-center; + gap: $spacing-xs; + + .signal-bars { + display: flex; + gap: 1px; + + .bar { + width: $spacing-xs; + height: $spacing-sm; + background-color: $gray-200; + border-radius: 1px; + + &.active { + background-color: $status-online; + } + } + } + } + } + + &__actions-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: $spacing-sm; + + .action-btn { + @include flex-center; + gap: $spacing-sm; + padding: $spacing-md; + background-color: $gray-50; + border: 1px solid $gray-200; + border-radius: $radius-lg; + font-size: 0.875rem; + font-weight: 500; + color: $gray-700; + transition: all $transition-fast; + cursor: pointer; + + &:hover:not(:disabled) { + background-color: $gray-100; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .text { + @include truncate; + } + } + } + + &__no-actions { + .placeholder { + font-size: 0.75rem; + color: $gray-500; + text-align: center; + padding: $spacing-md; + background-color: $gray-50; + border: 2px dashed $gray-200; + border-radius: $radius-md; + } + } +} + +// Sensor Type Specific Styles +.sensor-energy { + @include sensor-type-style($sensor-energy-bg, $sensor-energy-text); +} + +.sensor-co2 { + @include sensor-type-style($sensor-co2-bg, $sensor-co2-text); +} + +.sensor-temperature { + @include sensor-type-style($sensor-temperature-bg, $sensor-temperature-text); +} + +.sensor-humidity { + @include sensor-type-style($sensor-humidity-bg, $sensor-humidity-text); +} + +.sensor-hvac { + @include sensor-type-style($sensor-hvac-bg, $sensor-hvac-text); +} + +.sensor-lighting { + @include sensor-type-style($sensor-lighting-bg, $sensor-lighting-text); +} + +.sensor-security { + @include sensor-type-style($sensor-security-bg, $sensor-security-text); +} \ No newline at end of file diff --git a/src/assets/scss/components/_forms.scss b/src/assets/scss/components/_forms.scss new file mode 100644 index 0000000..bc9fcd0 --- /dev/null +++ b/src/assets/scss/components/_forms.scss @@ -0,0 +1,47 @@ +// Form Elements +.form-select { + padding: $spacing-sm $spacing-md; + border: 1px solid $gray-200; + border-radius: $radius-lg; + background-color: white; + font-size: 0.875rem; + transition: border-color $transition-fast; + + &:focus { + outline: none; + border-color: $primary; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); + } +} + +.form-input { + padding: $spacing-sm $spacing-md; + border: 1px solid $gray-200; + border-radius: $radius-lg; + background-color: white; + font-size: 0.875rem; + transition: border-color $transition-fast; + + &:focus { + outline: none; + border-color: $primary; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); + } + + &--full-width { + width: 100%; + } +} + +.form-label { + display: block; + font-size: 0.875rem; + font-weight: 500; + color: $gray-700; + margin-bottom: $spacing-sm; +} + +// Custom Range Input +.range-slider { + @include custom-slider; +} \ No newline at end of file diff --git a/src/assets/scss/components/_modals.scss b/src/assets/scss/components/_modals.scss new file mode 100644 index 0000000..14e523b --- /dev/null +++ b/src/assets/scss/components/_modals.scss @@ -0,0 +1,256 @@ +// Action Modal Styles +.action-modal { + position: fixed; + inset: 0; + z-index: 50; + @include flex-center; + + &__backdrop { + position: absolute; + inset: 0; + background-color: rgba(0, 0, 0, 0.5); + } + + &__container { + position: relative; + background-color: white; + border-radius: $radius-2xl; + box-shadow: $shadow-xl; + max-width: 28rem; + width: 100%; + margin: 0 $spacing-md; + max-height: 90vh; + overflow-y: auto; + } + + &__header { + padding: $spacing-xl; + border-bottom: 1px solid $gray-100; + @include flex-between; + + h3 { + font-size: 1.125rem; + font-weight: 500; + color: $gray-900; + margin: 0; + } + + p { + font-size: 0.875rem; + color: $gray-600; + margin: 0; + } + } + + &__close-button { + padding: $spacing-sm; + border-radius: 50%; + transition: background-color $transition-fast; + background: none; + border: none; + cursor: pointer; + + &:hover { + background-color: $gray-100; + } + + svg { + width: 1.25rem; + height: 1.25rem; + color: $gray-400; + } + } + + &__content { + padding: $spacing-xl; + + .space-y-4 > * + * { + margin-top: $spacing-md; + } + } + + &__footer { + padding: $spacing-xl $spacing-xl $spacing-md; + background-color: $gray-50; + border-radius: 0 0 $radius-2xl $radius-2xl; + display: flex; + gap: $spacing-md; + + button { + flex: 1; + padding: $spacing-sm $spacing-md; + border-radius: $radius-lg; + font-weight: 500; + transition: all $transition-fast; + + &.cancel { + @include button-secondary; + } + + &.execute { + @include button-primary; + + &:disabled { + background-color: #93c5fd; + cursor: not-allowed; + } + } + } + } +} + +// Range Input Styling +.range-input { + &__container { + .space-y-3 > * + * { + margin-top: $spacing-md; + } + } + + &__slider { + @include custom-slider; + } + + &__labels { + @include flex-between; + font-size: 0.875rem; + color: $gray-600; + + .current-value { + font-weight: 500; + } + } + + &__label { + display: block; + font-size: 0.875rem; + font-weight: 500; + color: $gray-700; + margin-bottom: $spacing-sm; + } +} + +// Option Selection +.option-selection { + &__label { + display: block; + font-size: 0.875rem; + font-weight: 500; + color: $gray-700; + margin-bottom: $spacing-sm; + } + + &__grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: $spacing-sm; + } + + &__button { + padding: $spacing-md; + border: 1px solid $gray-200; + border-radius: $radius-lg; + font-size: 0.875rem; + font-weight: 500; + transition: all $transition-fast; + cursor: pointer; + background: white; + color: $gray-700; + + &:hover { + background-color: $gray-50; + } + + &.selected { + background-color: $primary; + color: white; + border-color: $primary; + } + } +} + +// Toggle Control +.toggle-control { + @include flex-between; + + &__label { + font-size: 0.875rem; + font-weight: 500; + color: $gray-700; + } + + &__switch { + position: relative; + display: inline-flex; + height: 1.5rem; + width: 2.75rem; + align-items: center; + border-radius: 9999px; + transition: background-color $transition-fast; + cursor: pointer; + border: none; + + &.on { + background-color: $primary; + } + + &.off { + background-color: $gray-200; + } + } + + &__thumb { + display: inline-block; + height: 1rem; + width: 1rem; + border-radius: 50%; + background-color: white; + transition: transform $transition-fast; + + &.on { + transform: translateX(1.5rem); + } + + &.off { + transform: translateX(0.25rem); + } + } + + &__status { + font-size: 0.875rem; + color: $gray-500; + margin-top: $spacing-xs; + } +} + +// Trigger Action +.trigger-action { + &__container { + background-color: $gray-50; + border-radius: $radius-lg; + padding: $spacing-md; + } + + &__content { + @include flex-center; + gap: $spacing-md; + + .icon { + font-size: 2rem; + } + + .info { + .title { + font-weight: 500; + color: $gray-900; + margin: 0; + } + + .description { + font-size: 0.875rem; + color: $gray-600; + margin: 0; + } + } + } +} \ No newline at end of file diff --git a/src/assets/scss/layouts/_dashboard.scss b/src/assets/scss/layouts/_dashboard.scss new file mode 100644 index 0000000..eb86912 --- /dev/null +++ b/src/assets/scss/layouts/_dashboard.scss @@ -0,0 +1,27 @@ +// Dashboard Layout +.dashboard { + &__container { + max-width: 1200px; + margin: 0 auto; + padding: $spacing-md; + + @include respond-above($breakpoint-sm) { + padding: $spacing-lg; + } + } + + &__main { + flex-grow: 1; + padding-bottom: 5rem; // Space for bottom nav on mobile + + @include respond-above($breakpoint-md) { + padding-bottom: 0; + } + } + + &__content { + .space-y-6 > * + * { + margin-top: $spacing-xl; + } + } +} \ No newline at end of file diff --git a/src/assets/scss/layouts/_grid.scss b/src/assets/scss/layouts/_grid.scss new file mode 100644 index 0000000..de77cec --- /dev/null +++ b/src/assets/scss/layouts/_grid.scss @@ -0,0 +1,52 @@ +// Grid Layouts +.grid { + display: grid; + + &--1 { grid-template-columns: 1fr; } + &--2 { grid-template-columns: repeat(2, 1fr); } + &--3 { grid-template-columns: repeat(3, 1fr); } + &--4 { grid-template-columns: repeat(4, 1fr); } + + &--gap-2 { gap: $spacing-sm; } + &--gap-4 { gap: $spacing-md; } + &--gap-6 { gap: $spacing-lg; } + + // Responsive grids + &--responsive-simple { + @include grid-responsive(1, 3, 4, $spacing-md); + } + + &--responsive-detailed { + @include grid-responsive(1, 2, 3, $spacing-lg); + } + + &--responsive-cards { + @include grid-responsive(1, 2, 3, $spacing-md); + } +} + +// Flexbox utilities +.flex { + display: flex; + + &--center { + @include flex-center; + } + + &--between { + @include flex-between; + } + + &--column { + @include flex-column; + } + + &--wrap { + flex-wrap: wrap; + } + + &--gap-1 { gap: $spacing-xs; } + &--gap-2 { gap: $spacing-sm; } + &--gap-3 { gap: $spacing-md; } + &--gap-4 { gap: $spacing-lg; } +} \ No newline at end of file diff --git a/src/assets/scss/main.scss b/src/assets/scss/main.scss new file mode 100644 index 0000000..52b9b93 --- /dev/null +++ b/src/assets/scss/main.scss @@ -0,0 +1,23 @@ +@use 'tailwindcss'; +@import 'abstracts/variables'; +@import 'abstracts/mixins'; + +@import 'base/reset'; +@import 'base/typography'; + +@import 'components/buttons'; +@import 'components/cards'; +@import 'components/forms'; +@import 'components/modals'; + +@import 'layouts/grid'; +@import 'layouts/dashboard'; + +@import 'pages/sensor-management'; +@import 'pages/home'; + +@import 'utilities/helpers'; + +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/assets/scss/pages/_home.scss b/src/assets/scss/pages/_home.scss new file mode 100644 index 0000000..cf9c716 --- /dev/null +++ b/src/assets/scss/pages/_home.scss @@ -0,0 +1,167 @@ +// Home Dashboard Page +.home-dashboard { + &__filters { + display: flex; + flex-direction: column; + gap: $spacing-md; + margin-bottom: $spacing-xl; + + @include respond-above($breakpoint-sm) { + flex-direction: row; + } + + .filter-select { + @extend .form-select; + } + } + + &__metrics-row { + display: grid; + grid-template-columns: 1fr; + gap: $spacing-md; + min-height: 24rem; + + @include respond-above($breakpoint-lg) { + grid-template-columns: 1fr 1fr; + } + + .metrics-grid { + @include grid-responsive(1, 2, 3, $spacing-md); + + @include respond-above($breakpoint-lg) { + grid-template-columns: 1fr; + + @include respond-above($breakpoint-lg) { + grid-template-columns: repeat(3, 1fr); + } + } + } + + .chart-container { + // Chart styling handled by chart library + } + } + + &__charts-row { + @include grid-responsive(1, 2, 2, $spacing-md); + } + + &__topics-row { + @include grid-responsive(1, 2, 2, $spacing-md); + + .topic-card { + @include card-base; + @include card-padding(md); + + .title { + font-size: 0.875rem; + font-weight: 700; + color: $gray-500; + margin-bottom: $spacing-sm; + } + } + } + + &__leaderboards-row { + @include grid-responsive(1, 2, 2, $spacing-md); + + .leaderboard-card { + @include card-base; + @include card-padding(md); + + .title { + font-size: 0.875rem; + font-weight: 700; + color: $gray-500; + margin-bottom: $spacing-sm; + } + } + } +} + +// Metric Cards +.metric-card { + @include card-base; + @include card-padding(md); + @include flex-column; + justify-content: space-between; + aspect-ratio: 1; + + .title { + font-size: 0.875rem; + font-weight: 700; + color: $gray-500; + } + + .content-section { + flex-grow: 1; + @include flex-center; + justify-content: flex-start; + + .value { + color: $gray-900; + font-weight: 700; + font-size: 1.5rem; + + .unit { + font-size: 0.875rem; + color: $gray-500; + } + } + } +} + +// Graph Metric Cards +.graph-metric-card { + @include card-base; + @include card-padding(md); + + .header { + @include flex-between; + margin-bottom: $spacing-md; + + .title { + font-size: 0.875rem; + font-weight: 700; + color: $gray-500; + } + + .trend-indicator { + font-size: 0.75rem; + padding: $spacing-xs $spacing-sm; + border-radius: $radius-md; + + &--up { + background-color: #dcfce7; + color: #166534; + } + + &--down { + background-color: #fee2e2; + color: #991b1b; + } + + &--neutral { + background-color: $gray-100; + color: $gray-600; + } + } + } + + .value { + font-size: 1.25rem; + font-weight: 700; + color: $gray-900; + margin-bottom: $spacing-sm; + + .unit { + font-size: 0.875rem; + color: $gray-500; + } + } + + .chart { + height: 2rem; + // Chart styling handled by chart library + } +} \ No newline at end of file diff --git a/src/assets/scss/pages/_sensor-management.scss b/src/assets/scss/pages/_sensor-management.scss new file mode 100644 index 0000000..55ce86e --- /dev/null +++ b/src/assets/scss/pages/_sensor-management.scss @@ -0,0 +1,114 @@ +// Sensor Management Page +.sensor-management { + &__header { + @include flex-between; + align-items: flex-start; + flex-direction: column; + + @include respond-above($breakpoint-sm) { + align-items: center; + flex-direction: row; + } + + .title-section { + h1 { + font-size: 1.5rem; + font-weight: 700; + color: $gray-900; + margin-bottom: $spacing-xs; + } + + p { + color: $gray-600; + } + } + + .status-section { + margin-top: $spacing-md; + + @include respond-above($breakpoint-sm) { + margin-top: 0; + } + + .status-info { + @include flex-center; + gap: $spacing-sm; + font-size: 0.875rem; + color: $gray-600; + + .connection-indicator { + @include status-indicator($status-offline); + + &--connected { + background-color: $status-online; + } + + &--disconnected { + background-color: $status-error; + } + } + + .separator { + margin: 0 $spacing-sm; + } + } + } + } + + &__filters { + display: flex; + flex-direction: column; + gap: $spacing-md; + + @include respond-above($breakpoint-lg) { + flex-direction: row; + } + + .filter-group { + display: flex; + flex-direction: column; + gap: $spacing-md; + flex: 1; + + @include respond-above($breakpoint-sm) { + flex-direction: row; + } + } + + .view-toggle { + @extend .toggle-group; + } + } + + &__grid { + &--simple { + @include grid-responsive(1, 3, 4, $spacing-md); + } + + &--detailed { + @include grid-responsive(1, 2, 3, $spacing-lg); + } + } + + &__empty-state { + text-align: center; + padding: $spacing-2xl * 2; + + .icon { + color: $gray-400; + font-size: 3.75rem; + margin-bottom: $spacing-md; + } + + .title { + font-size: 1.125rem; + font-weight: 500; + color: $gray-900; + margin-bottom: $spacing-sm; + } + + .description { + color: $gray-600; + } + } +} \ No newline at end of file diff --git a/src/assets/scss/utilities/_helpers.scss b/src/assets/scss/utilities/_helpers.scss new file mode 100644 index 0000000..88d43dc --- /dev/null +++ b/src/assets/scss/utilities/_helpers.scss @@ -0,0 +1,109 @@ +// Utility Classes +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +// Spacing utilities +.space-y-2 > * + * { margin-top: $spacing-sm; } +.space-y-3 > * + * { margin-top: $spacing-md; } +.space-y-4 > * + * { margin-top: $spacing-lg; } +.space-y-6 > * + * { margin-top: $spacing-xl; } + +.space-x-2 > * + * { margin-left: $spacing-sm; } +.space-x-3 > * + * { margin-left: $spacing-md; } +.space-x-4 > * + * { margin-left: $spacing-lg; } + +// Margin utilities +.m-0 { margin: 0; } +.mb-1 { margin-bottom: $spacing-xs; } +.mb-2 { margin-bottom: $spacing-sm; } +.mb-3 { margin-bottom: $spacing-md; } +.mb-4 { margin-bottom: $spacing-lg; } + +.mt-1 { margin-top: $spacing-xs; } +.mt-2 { margin-top: $spacing-sm; } +.mt-3 { margin-top: $spacing-md; } +.mt-4 { margin-top: $spacing-lg; } + +// Padding utilities +.p-0 { padding: 0; } +.p-1 { padding: $spacing-xs; } +.p-2 { padding: $spacing-sm; } +.p-3 { padding: $spacing-md; } +.p-4 { padding: $spacing-lg; } + +// Width utilities +.w-full { width: 100%; } +.w-auto { width: auto; } + +// Height utilities +.h-full { height: 100%; } +.h-auto { height: auto; } + +// Color utilities +.text-primary { color: $primary; } +.text-secondary { color: $secondary; } +.text-success { color: $success; } +.text-warning { color: $warning; } +.text-danger { color: $danger; } + +.text-gray-400 { color: $gray-400; } +.text-gray-500 { color: $gray-500; } +.text-gray-600 { color: $gray-600; } +.text-gray-700 { color: $gray-700; } +.text-gray-900 { color: $gray-900; } + +// Background utilities +.bg-primary { background-color: $primary; } +.bg-white { background-color: white; } +.bg-gray-50 { background-color: $gray-50; } +.bg-gray-100 { background-color: $gray-100; } + +// Border utilities +.border { border: 1px solid $gray-200; } +.border-0 { border: 0; } +.border-gray-100 { border-color: $gray-100; } +.border-gray-200 { border-color: $gray-200; } + +// Border radius utilities +.rounded { border-radius: $radius-md; } +.rounded-lg { border-radius: $radius-lg; } +.rounded-xl { border-radius: $radius-xl; } +.rounded-2xl { border-radius: $radius-2xl; } +.rounded-full { border-radius: 9999px; } + +// Shadow utilities +.shadow-sm { box-shadow: $shadow-sm; } +.shadow-md { box-shadow: $shadow-md; } +.shadow-lg { box-shadow: $shadow-lg; } +.shadow-xl { box-shadow: $shadow-xl; } + +// Position utilities +.relative { position: relative; } +.absolute { position: absolute; } +.fixed { position: fixed; } + +// Z-index utilities +.z-10 { z-index: 10; } +.z-20 { z-index: 20; } +.z-50 { z-index: 50; } + +// Overflow utilities +.overflow-hidden { overflow: hidden; } +.overflow-y-auto { overflow-y: auto; } + +// Cursor utilities +.cursor-pointer { cursor: pointer; } +.cursor-not-allowed { cursor: not-allowed; } + +// Opacity utilities +.opacity-50 { opacity: 0.5; } +.opacity-75 { opacity: 0.75; } \ No newline at end of file