diff options
author | Vitalii Lysak <v.lysak@dunice.net> | 2022-07-13 15:46:06 +0300 |
---|---|---|
committer | Vitalii Lysak <v.lysak@dunice.net> | 2022-07-13 15:46:06 +0300 |
commit | 1272456ab2cb77f29b27f3839563b09a709cbc06 (patch) | |
tree | bb8655fd1dff128355a14f61b50556708e87f4a9 /src/components/_sila | |
parent | b2bea3021aea8be3d4bc34f965bf58297c358bca (diff) | |
download | webui-vue-sila-fe.tar.xz |
move sila-dev to _silasila-fe
Diffstat (limited to 'src/components/_sila')
33 files changed, 2711 insertions, 228 deletions
diff --git a/src/components/_sila/AppHeader/AppHeader.vue b/src/components/_sila/AppHeader/AppHeader.vue index 84e4588f..96c93fbb 100644 --- a/src/components/_sila/AppHeader/AppHeader.vue +++ b/src/components/_sila/AppHeader/AppHeader.vue @@ -31,39 +31,89 @@ </b-button> <b-navbar-nav> <b-navbar-brand - class="mr-0" - to="/" + class="mr-0 app-logo" + to="/hardware-status/inventory" data-test-id="appHeader-container-overview" > <img class="header-logo" - src="@/assets/images/logo-header.svg" - :alt="altLogo" + src="@/assets/images/_sila/logo-header-sila.svg" /> </b-navbar-brand> <div v-if="isNavTagPresent" :key="routerKey" class="pl-2 nav-tags"> - <span>|</span> - <span class="pl-3 asset-tag">{{ assetTag }}</span> - <span class="pl-3">{{ modelType }}</span> - <span class="pl-3">{{ serialNumber }}</span> + <span style="color: white">|</span> + <span style="color: white" class="pl-2 asset-tag">{{ + assetTag + }}</span> + <span style="color: white" class="pl-2">{{ modelType }}</span> + <span style="color: white" class="pl-2">{{ serialNumber }}</span> </div> </b-navbar-nav> - <!-- Right aligned nav items --> - <b-navbar-nav class="ml-auto helper-menu"> + <!-- Left nav items--> + <!-- <b-navbar-nav class="helper-menu"> <b-nav-item - to="/logs/event-logs" + to="/hardware-status/inventory" data-test-id="appHeader-container-health" + class="nav-top-button" + :class="{ + 'active-route-top-nav': ![ + 'profile-settings', + 'information-and-faq', + 'support', + ].includes($route.path.split('/')[1]), + }" > - <status-icon :status="healthStatusIcon" /> - {{ $t('appHeader.health') }} + {{ $t('appHeader.servers') }} </b-nav-item> + <b-nav-item - to="/operations/server-power-operations" + to="/information-and-faq" + data-test-id="appHeader-container-power" + class="nav-top-button" + :class="{ + 'active-route-top-nav': ''.includes( + $route.path.split('information-and-faq')[1] + ), + }" + > + {{ $t('appHeader.informationAndFAQ') }} + </b-nav-item> + + <b-nav-item + to="/support" data-test-id="appHeader-container-power" + class="nav-top-button" + :class="{ + 'active-route-top-nav': ''.includes( + $route.path.split('support')[1] + ), + }" > - <status-icon :status="serverStatusIcon" /> - {{ $t('appHeader.power') }} + {{ $t('appHeader.support') }} </b-nav-item> + </b-navbar-nav> --> + <!-- Right aligned nav items --> + <b-navbar-nav class="ml-auto helper-menu"> + <li class="nav-item"> + <b-button + variant="link" + data-test-id="appHeader-container-health" + @click="toLogs" + > + <status-icon :status="healthStatusIcon" /> + <span>{{ $t('appHeader.health') }}</span> + </b-button> + </li> + <li class="nav-item"> + <b-button + variant="link" + data-test-id="appHeader-container-power" + @click="toOperations" + > + <status-icon :status="serverStatusIcon" /> + <span>{{ $t('appHeader.power') }}</span> + </b-button> + </li> <!-- Using LI elements instead of b-nav-item to support semantic button elements --> <li class="nav-item"> <b-button @@ -103,28 +153,24 @@ </b-navbar-nav> </b-navbar> </header> - <loading-bar /> </div> </template> <script> -import BVToastMixin from '@/components/Mixins/BVToastMixin'; -import IconAvatar from '@carbon/icons-vue/es/user--avatar/20'; +import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; import IconClose from '@carbon/icons-vue/es/close/20'; import IconMenu from '@carbon/icons-vue/es/menu/20'; +import IconAvatar from '@carbon/icons-vue/es/user--avatar/20'; import IconRenew from '@carbon/icons-vue/es/renew/20'; -import StatusIcon from '@/components/Global/StatusIcon'; -import LoadingBar from '@/components/Global/LoadingBar'; - +import StatusIcon from '@/components/_sila/Global/StatusIcon'; export default { name: 'AppHeader', components: { - IconAvatar, IconClose, IconMenu, + IconAvatar, IconRenew, StatusIcon, - LoadingBar, }, mixins: [BVToastMixin], props: { @@ -136,7 +182,6 @@ export default { data() { return { isNavigationOpen: false, - altLogo: process.env.VUE_APP_COMPANY_NAME || 'Built on OpenBMC', }; }, computed: { @@ -232,6 +277,12 @@ export default { event.preventDefault(); this.$root.$emit('skip-navigation'); }, + toLogs() { + this.$router.push('/logs/event-logs').catch(() => {}); + }, + toOperations() { + this.$router.push('/operations/server-power-operations').catch(() => {}); + }, }, }; </script> @@ -241,6 +292,7 @@ export default { box-shadow: inset 0 0 0 3px $padding-color, inset 0 0 0 5px $outline-color; } .app-header { + flex-grow: 1; .link-skip-nav { position: absolute; top: -60px; @@ -252,39 +304,52 @@ export default { transition-timing-function: $entrance-easing--expressive; } } + + .app-logo { + margin-top: 10px; + } + .nav-top-button { + color: $white; + padding: 0px; + margin: 0px 8px; + } + + .active-route-top-nav { + background-color: $faint-brand-primary-40; + border-radius: 4px; + } + .navbar-text, .nav-link, .btn-link { - color: color('white') !important; - fill: currentColor; - padding: 0.68rem 1rem !important; + border-radius: 4px; + color: $white !important; &:hover { - background-color: theme-color-level(light, 10); + background-color: $faint-brand-primary-40; } &:active { - background-color: theme-color-level(light, 9); - } - &:focus { - @include focus-box-shadow; - outline: 0; + background-color: $faint-brand-primary-40; } } - .nav-item { - fill: theme-color('light'); - } - .navbar { padding: 0; background-color: $navbar-color; + svg { + margin-right: 2px; + } + .status-icon { + svg { + vertical-align: text-bottom; + } + } @include media-breakpoint-up($responsive-layout-bp) { - height: $header-height; + height: $first-header-height; } .helper-menu { @include media-breakpoint-down(sm) { - background-color: gray('800'); width: 100%; justify-content: flex-end; @@ -292,11 +357,6 @@ export default { .btn { padding: $spacer / 1.125 $spacer / 2; } - - .nav-link:focus, - .btn:focus { - @include focus-box-shadow($gray-800); - } } .responsive-text { @@ -311,14 +371,13 @@ export default { @include media-breakpoint-up($responsive-layout-bp) { padding: 0 $spacer; } - align-items: center; + align-items: baseline; .navbar-brand, .nav-link { transition: $focus-transition; } .nav-tags { - color: theme-color-level(light, 3); @include media-breakpoint-down(xs) { @include sr-only; } @@ -332,8 +391,8 @@ export default { .nav-trigger { fill: theme-color('light'); - width: $header-height; - height: $header-height; + width: $first-header-height; + height: $first-header-height; transition: none; display: inline-flex; flex: 0 0 20px; @@ -374,11 +433,25 @@ export default { .navbar-brand { padding: $spacer/2; - height: $header-height; + height: $first-header-height; line-height: 1; &:focus { box-shadow: inset 0 0 0 3px $navbar-color, inset 0 0 0 5px color('white'); outline: 0; } } + +.app-header-logout, +.app-header-notification { + margin: 0 5px; + background: none; + border: none; + width: 30px; + height: 30px; + &:hover { + background: $faint-brand-primary-40; + border-radius: 5px; + border: none; + } +} </style> diff --git a/src/components/_sila/AppNavigation/AppNavigation.vue b/src/components/_sila/AppNavigation/AppNavigation.vue index acfabe76..1228968d 100644 --- a/src/components/_sila/AppNavigation/AppNavigation.vue +++ b/src/components/_sila/AppNavigation/AppNavigation.vue @@ -2,8 +2,8 @@ <div> <div class="nav-container" :class="{ open: isNavigationOpen }"> <nav ref="nav" :aria-label="$t('appNavigation.primaryNavigation')"> - <b-nav vertical class="mb-4"> - <template v-for="(navItem, index) in navigationItems"> + <b-nav vertical> + <template v-for="(navItem, index) in sideBar"> <!-- Navigation items with no children --> <b-nav-item v-if="!navItem.children" @@ -22,9 +22,9 @@ variant="link" :data-test-id="`nav-button-${navItem.id}`" > - <component :is="navItem.icon" /> {{ navItem.label }} - <icon-expand class="icon-expand" /> + <component :is="navItem.icon" class="icon-expand" /> + <!-- <icon-expand class="icon-expand" /> --> </b-button> <b-collapse :id="navItem.id" tag="ul" class="nav-item__nav"> <li class="nav-item"> @@ -59,16 +59,40 @@ //Do not change Mixin import. //Exact match alias set to support //dotenv customizations. -import AppNavigationMixin from './AppNavigationMixin'; +import { AppNavigationMixin, KvmNavigationMixin } from './AppNavigationMixin'; export default { name: 'AppNavigation', - mixins: [AppNavigationMixin], + mixins: [AppNavigationMixin, KvmNavigationMixin], data() { return { isNavigationOpen: false, + server: 1, + servers: [ + { + value: 1, + text: 'Сервер №1', + }, + { + value: 2, + text: 'Сервер №2', + }, + ], }; }, + computed: { + sideBar() { + if ( + this.$route.path === '/console/settings' || + this.$route.path === '/operations/serial-over-lan' || + this.$route.path === '/operations/kvm' + ) { + return this.kvmNavigationItems; + } else { + return this.navigationItems; + } + }, + }, watch: { $route: function () { this.isNavigationOpen = false; @@ -100,10 +124,16 @@ svg { } } -.nav { - padding-top: $spacer / 4; - @include media-breakpoint-up($responsive-layout-bp) { - padding-top: $spacer; +.nav-link { + display: flex; + align-items: center; + padding-left: $spacer * 4; + outline: none; + box-sizing: border-box; + height: 68px; + border-top: 1px solid rgba(26, 62, 91, 0.2); + &:not(.nav-link--current) { + font-weight: normal; } } @@ -117,32 +147,43 @@ svg { } .nav-link { + display: flex; + align-items: center; padding-left: $spacer * 4; outline: none; - + height: 68px; + border-top: 1px solid rgba(26, 62, 91, 0.2); &:not(.nav-link--current) { font-weight: normal; } } } +.server-form { + height: 48px; + width: 272px; + border-radius: 8px; + padding: 8px; + background-color: $faint-secondary-primary-5; +} .btn-link { display: inline-block; width: 100%; text-align: left; text-decoration: none !important; border-radius: 0; - - &.collapsed { - .icon-expand { - transform: rotate(180deg); - } + height: 68px; + border-top: 1px solid rgba(26, 62, 91, 0.2); + font-weight: 600; + line-height: 20px; + &.not-collapsed { + font-weight: 600; + line-height: 20px; } } .icon-expand { - float: right; - margin-top: $spacer / 4; + margin: 0; } .btn-link, @@ -151,16 +192,16 @@ svg { font-weight: $headings-font-weight; padding-left: $spacer; // defining consistent padding for links and buttons padding-right: $spacer; - color: theme-color('secondary'); + color: $text-primary; &:hover { - background-color: theme-color-level(dark, -10.5); + background-color: $faint-secondary-primary-5-hover; color: theme-color('dark'); } &:focus { - background-color: theme-color-level(light, 0); - box-shadow: inset 0 0 0 2px theme-color('primary'); + background-color: $faint-secondary-primary-5-hover; + box-shadow: none; color: theme-color('dark'); outline: 0; } @@ -173,11 +214,10 @@ svg { .nav-link--current { font-weight: $headings-font-weight; - background-color: theme-color('secondary'); - color: theme-color('light'); + background-color: $red-brand-primary-5; + color: $red-brand-primary; cursor: default; box-shadow: none; - &::before { content: ''; position: absolute; @@ -185,13 +225,13 @@ svg { bottom: 0; left: 0; width: 4px; - background-color: theme-color('primary'); + background-color: $red-brand-primary; } &:hover, &:focus { - background-color: theme-color('secondary'); - color: theme-color('light'); + background-color: $red-brand-primary-5; + color: $red-brand-primary; } } @@ -201,12 +241,12 @@ svg { top: $header-height; bottom: 0; left: 0; - z-index: $zindex-fixed; - overflow-y: auto; + z-index: 10; + overflow-y: overlay; background-color: theme-color('light'); transform: translateX(-$navigation-width); transition: transform $exit-easing--productive $duration--moderate-02; - border-right: 1px solid theme-color-level('light', 2.85); + border-right: 1px solid rgba(19, 46, 68, 0.247); @include media-breakpoint-down(md) { z-index: $zindex-fixed + 2; @@ -222,6 +262,17 @@ svg { transition-duration: $duration--fast-01; transform: translateX(0); } + + &::-webkit-scrollbar { + position: absolute; + width: 10px; + } + &::-webkit-scrollbar-thumb { + border: 4px solid transparent; + background: $faint-secondary-primary-20; + border-radius: 16px; + background-clip: content-box; + } } .nav-overlay { @@ -252,4 +303,98 @@ svg { display: none; } } + +.navbar__search_select_container { + display: flex; + flex-flow: column nowrap; + justify-content: space-between; + align-items: flex-start; +} + +.server__icon { + width: 20px; + height: 20px; +} + +.server-form { + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; + margin: 16px 1rem; + height: 48px; + width: 272px; + + .options { + background-color: $white; + height: 48px; + width: 272px; + border-radius: 8px; + } + option { + background-color: $white; + height: 48px; + width: 272px; + border-radius: 8px; + } +} + +.nav-line { + height: 1px; + width: 272px; + border-bottom: 1px solid rgba(26, 62, 91, 0.2); + margin: 0 1rem; +} + +.server-pagination-select { + margin: 0; +} + +.server-select { + font-weight: 500; + line-height: 20px; + width: 237px; + border: none; + height: 48px; + background-image: url('../../../assets/images/_sila/icon-chevron.svg'); + &:focus { + box-shadow: none; + } + + .options { + background-color: $white; + height: 48px; + width: 272px; + border-radius: 8px; + } + option { + background-color: $white; + height: 48px; + width: 272px; + border-radius: 8px; + } +} + +.server-search { + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; + border: none; + box-shadow: none; + height: 40px; + margin: 16px 1rem; + width: 272px; +} + +.search-button { + border: none; + background: none; +} + +.nav-search__input { + border: none; + background: none; + box-shadow: none; +} </style> diff --git a/src/components/_sila/AppNavigation/AppNavigationMixin.js b/src/components/_sila/AppNavigation/AppNavigationMixin.js index bbbbb1ee..13125fdf 100644 --- a/src/components/_sila/AppNavigation/AppNavigationMixin.js +++ b/src/components/_sila/AppNavigation/AppNavigationMixin.js @@ -1,36 +1,227 @@ -import IconDashboard from '@carbon/icons-vue/es/dashboard/16'; -import IconTextLinkAnalysis from '@carbon/icons-vue/es/text-link--analysis/16'; -import IconDataCheck from '@carbon/icons-vue/es/data--check/16'; -import IconSettingsAdjust from '@carbon/icons-vue/es/settings--adjust/16'; -import IconSettings from '@carbon/icons-vue/es/settings/16'; -import IconSecurity from '@carbon/icons-vue/es/security/16'; -import IconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; -import IconDataBase from '@carbon/icons-vue/es/data--base--alt/16'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; -const AppNavigationMixin = { +export const AppNavigationMixin = { components: { - iconOverview: IconDashboard, - iconLogs: IconTextLinkAnalysis, - iconHealth: IconDataCheck, - iconControl: IconSettingsAdjust, - iconSettings: IconSettings, - iconSecurityAndAccess: IconSecurity, - iconExpand: IconChevronUp, - iconResourceManagement: IconDataBase, + IconChevronUp: iconChevronUp, }, data() { return { navigationItems: [ { - id: 'overview', - label: this.$t('appNavigation.overview'), - route: '/', - icon: 'iconOverview', + id: 'system', + label: this.$t('appNavigation.systemInformaion'), + icon: 'iconChevronUp', + children: [ + { + id: 'info', + label: this.$t('appNavigation.overviewInfo'), + route: '/hardware-status/inventory', + }, + { + id: 'network', + label: this.$t('appNavigation.networkParametrs'), + route: '/settings/network', + }, + { + id: 'date-time', + label: this.$t('appNavigation.dateTime'), + route: '/settings/date-time', + }, + ], + }, + { + id: 'bmc', + label: this.$t('appNavigation.bmc'), + icon: 'iconChevronUp', + children: [ + { + id: 'bmc-configuration', + label: this.$t('appNavigation.config'), + route: '/bmc-configuration', + }, + // { + // id: 'bmc-firmware', + // label: this.$t('appNavigation.deviceFirmware'), + // route: '/bmc-firmware', + // }, + // { + // id: 'bmc-settings', + // label: this.$t('appNavigation.broadcast'), + // route: '/bmc-settings', + // }, + ], + }, + // { + // id: 'analytical-panel', + // label: this.$t('appNavigation.analyticalPanel'), + // route: '/analytical-panel', + // }, + // { + // id: 'RAID', + // label: this.$t('appNavigation.raidControllers'), + // icon: 'iconChevronUp', + // children: [ + // { + // id: 'raid-specification', + // label: this.$t('appNavigation.specification'), + // route: '/raid-specification', + // }, + // { + // id: 'raid-settings', + // label: this.$t('appNavigation.settings'), + // route: '/raid-settings', + // }, + // { + // id: 'raid-cache', + // label: this.$t('RAID.cache'), + // route: '/raid-cache', + // }, + // ], + // }, + // { + // id: 'processors', + // label: this.$t('appNavigation.processors'), + // icon: 'iconChevronUp', + // children: [ + // { + // id: 'processors-specification', + // label: this.$t('appNavigation.specification'), + // route: '/processors-specification', + // }, + // { + // id: 'processors-dynamic-info', + // label: this.$t('appNavigation.analyticalPanel'), + // route: '/processors-dynamic-info', + // }, + // ], + // }, + /*{ + id: 'power', + label: this.$t('appNavigation.powerSupplies'), + icon: 'iconChevronUp', + children: [ + { + id: 'power-specification', + label: this.$t('appNavigation.specification'), + route: '/power-specification', + }, + { + id: 'power-dynamic-info', + label: this.$t('appNavigation.analyticalPanel'), + route: '/power-dynamic-info', + }, + ], + },*/ + { + id: 'memory', + label: this.$t('appNavigation.memoryModules'), + icon: 'iconChevronUp', + children: [ + /*{ + id: 'memory-specification', + label: this.$t('appNavigation.specification'), + route: '/memory-specification', + },*/ + { + id: 'memory-dynamic-info', + label: this.$t('appNavigation.dynamicInformation'), + route: '/memory-dynamic-info', + }, + ], + }, + { + id: 'fans', + label: this.$t('appNavigation.fans'), + icon: 'iconChevronUp', + children: [ + { + id: 'fans-static', + label: this.$t('appNavigation.statisticInformation'), + route: '/fans-static', + }, + { + id: 'fans', + label: this.$t('appNavigation.dynamicInformation'), + route: '/fans', + }, + ], + }, + // { + // id: 'physical-drives', + // label: this.$t('appNavigation.physicalDrives'), + // icon: 'iconChevronUp', + // children: [ + // { + // id: 'drivers-static', + // label: this.$t('appNavigation.statisticInformation'), + // route: '/drivers-static', + // }, + // { + // id: 'drivers', + // label: this.$t('appNavigation.analyticalPanel'), + // route: '/drivers', + // }, + // ], + // }, + // { + // id: 'virtual-drivers', + // label: this.$t('appNavigation.virtualDrivers'), + // route: '/virtual-drivers', + // }, + { + id: 'motherboard', + label: this.$t('appNavigation.motherboard'), + icon: 'iconChevronUp', + children: [ + /*{ + id: 'motherboard-specification', + label: this.$t('appNavigation.specification'), + route: '/motherboard-specification', + },*/ + { + id: 'motherboard-dynamic-info', + label: this.$t('appNavigation.dynamicInformation'), + route: '/motherboard-dynamic-info', + }, + ], }, + // { + // id: 'network-adapters', + // label: this.$t('appNavigation.networkAdapters'), + // icon: 'iconChevronUp', + // children: [ + // { + // id: 'network-adapters-ethernet', + // label: this.$t('appNavigation.ethernetAdapters'), + // route: '/network-adapters-ethernet', + // }, + // { + // id: 'network-adapters-fc-hba', + // label: this.$t('appNavigation.fcHbaAdapters'), + // route: '/network-adapters-fc-hba', + // }, + // { + // id: 'network-adapters-pannel', + // label: this.$t('appNavigation.analyticalPanel'), + // route: '/network-adapters-pannel', + // }, + // ], + // }, + // { + // id: 'pci-devices', + // label: this.$t('appNavigation.pciDevices'), + // route: '/pci-devices', + // }, + ///////////////////////////old tabs + // { + // id: 'overview', + // label: 'Обзор', + // route: '/Info', + // }, { id: 'logs', label: this.$t('appNavigation.logs'), - icon: 'iconLogs', + icon: 'iconChevronUp', children: [ { id: 'event-logs', @@ -47,14 +238,9 @@ const AppNavigationMixin = { { id: 'hardware-status', label: this.$t('appNavigation.hardwareStatus'), - icon: 'iconHealth', + icon: 'iconChevronUp', children: [ { - id: 'inventory', - label: this.$t('appNavigation.inventory'), - route: '/hardware-status/inventory', - }, - { id: 'sensors', label: this.$t('appNavigation.sensors'), route: '/hardware-status/sensors', @@ -64,7 +250,7 @@ const AppNavigationMixin = { { id: 'operations', label: this.$t('appNavigation.operations'), - icon: 'iconControl', + icon: 'iconChevronUp', children: [ { id: 'factory-reset', @@ -72,13 +258,8 @@ const AppNavigationMixin = { route: '/operations/factory-reset', }, { - id: 'kvm', - label: this.$t('appNavigation.kvm'), - route: '/operations/kvm', - }, - { id: 'key-clear', - label: this.$t('appNavigation.keyClear'), + label: this.$t('appPageTitle.keyClear'), route: '/operations/key-clear', }, { @@ -92,11 +273,6 @@ const AppNavigationMixin = { route: '/operations/reboot-bmc', }, { - id: 'serial-over-lan', - label: this.$t('appNavigation.serialOverLan'), - route: '/operations/serial-over-lan', - }, - { id: 'server-power-operations', label: this.$t('appNavigation.serverPowerOperations'), route: '/operations/server-power-operations', @@ -108,32 +284,27 @@ const AppNavigationMixin = { }, ], }, - { - id: 'settings', - label: this.$t('appNavigation.settings'), - icon: 'iconSettings', - children: [ - { - id: 'date-time', - label: this.$t('appNavigation.dateTime'), - route: '/settings/date-time', - }, - { - id: 'network', - label: this.$t('appNavigation.network'), - route: '/settings/network', - }, - { - id: 'power-restore-policy', - label: this.$t('appNavigation.powerRestorePolicy'), - route: '/settings/power-restore-policy', - }, - ], - }, + // { + // id: 'settings', + // label: this.$t('appNavigation.settings'), + // icon: 'iconChevronUp', + // children: [ + // { + // id: 'network', + // label: this.$t('appNavigation.network'), + // route: '/settings/network', + // }, + // { + // id: 'power-restore-policy', + // label: this.$t('appNavigation.powerRestorePolicy'), + // route: '/settings/power-restore-policy', + // }, + // ], + // }, { id: 'security-and-access', label: this.$t('appNavigation.securityAndAccess'), - icon: 'iconSecurityAndAccess', + icon: 'iconChevronUp', children: [ { id: 'sessions', @@ -162,21 +333,43 @@ const AppNavigationMixin = { }, ], }, + // { + // id: 'resource-management', + // label: this.$t('appNavigation.resourceManagement'), + // icon: 'iconChevronUp', + // children: [ + // { + // id: 'power', + // label: this.$t('appNavigation.power'), + // route: '/resource-management/power', + // }, + // ], + // }, + ], + }; + }, +}; + +export const KvmNavigationMixin = { + data() { + return { + kvmNavigationItems: [ + // { + // id: 'console-settings', + // label: this.$t('appPageTitle.consoleSettings'), + // route: '/console/settings', + // }, + { + id: 'kvm', + label: this.$t('appNavigation.kvm'), + route: '/operations/kvm', + }, { - id: 'resource-management', - label: this.$t('appNavigation.resourceManagement'), - icon: 'iconResourceManagement', - children: [ - { - id: 'power', - label: this.$t('appNavigation.power'), - route: '/resource-management/power', - }, - ], + id: 'serial-over-lan', + label: this.$t('appPageTitle.serialOverLan'), + route: '/operations/serial-over-lan', }, ], }; }, }; - -export default AppNavigationMixin; diff --git a/src/components/_sila/Global/Alert.vue b/src/components/_sila/Global/Alert.vue index e8de9e27..8b9b0d90 100644 --- a/src/components/_sila/Global/Alert.vue +++ b/src/components/_sila/Global/Alert.vue @@ -12,7 +12,7 @@ <status-icon :status="variant" /> </div> <div class="alert-content"> - <div class="alert-msg"> + <div class="alert-msg regular-14px"> <slot /> </div> </div> @@ -23,7 +23,7 @@ </template> <script> -import StatusIcon from '@/components/Global/StatusIcon'; +import StatusIcon from '@/components/_sila/Global/StatusIcon'; import { BAlert } from 'bootstrap-vue'; export default { diff --git a/src/components/_sila/Global/FormFile.vue b/src/components/_sila/Global/FormFile.vue index cf713acf..50af468e 100644 --- a/src/components/_sila/Global/FormFile.vue +++ b/src/components/_sila/Global/FormFile.vue @@ -1,26 +1,38 @@ <template> <div class="custom-form-file-container"> <label> - <b-form-file - :id="id" - v-model="file" - :accept="accept" - :disabled="disabled" - :state="state" - plain - @input="$emit('input', file)" + <b-modal + :id="`modal-${id}`" + body-class="modal-file-body" + :title="$t('pageKvm.addImage_modal')" + hide-footer > - </b-form-file> - <span - class="add-file-btn btn" + <div class="file-input_container"> + <b-form-file + :id="id" + v-model="file" + :accept="accept" + :disabled="disabled" + :state="state" + placeholder="Нажмите на область или перетащите в нее файл" + drop-placeholder="Отпустите, чтобы добавить файл" + @input="$emit('input', file)" + > + </b-form-file> + </div> + </b-modal> + <b-button + size="lg" + class="add-file-btn" :class="{ disabled, 'btn-secondary': isSecondary, 'btn-primary': !isSecondary, }" + @click="$bvModal.show(`modal-${id}`)" > {{ $t('global.fileUpload.browseText') }} - </span> + </b-button> <slot name="invalid"></slot> </label> <div v-if="file" class="clear-selected-file px-3 py-2 mt-2"> @@ -78,9 +90,17 @@ export default { return this.variant === 'secondary'; }, }, + watch: { + file() { + if (this.file) { + this.$bvModal.hide(`modal-${this.id}`); + } else { + this.$emit('input', this.file); + } + }, + }, }; </script> - <style lang="scss" scoped> .form-control-file { opacity: 0; @@ -116,4 +136,22 @@ export default { } } } + +.file-input_container { + width: 100%; + height: 400px; + background-color: $surface-secondary; + border-top: 1px solid #f3f4f5; + border-radius: 0 0 16px 16px; + + display: flex; + align-items: center; + justify-content: center; +} + +.custom-form-file-container { + label { + margin-bottom: 0; + } +} </style> diff --git a/src/components/_sila/Global/InfoTooltip.vue b/src/components/_sila/Global/InfoTooltip.vue index c91109d1..950b6c3e 100644 --- a/src/components/_sila/Global/InfoTooltip.vue +++ b/src/components/_sila/Global/InfoTooltip.vue @@ -28,6 +28,7 @@ export default { .btn-tooltip { padding: 0; line-height: 1em; + height: auto; svg { vertical-align: baseline; } diff --git a/src/components/_sila/Global/InputPasswordToggle.vue b/src/components/_sila/Global/InputPasswordToggle.vue index d2c0d4a6..be4fdc6c 100644 --- a/src/components/_sila/Global/InputPasswordToggle.vue +++ b/src/components/_sila/Global/InputPasswordToggle.vue @@ -16,8 +16,8 @@ </template> <script> -import IconView from '@carbon/icons-vue/es/view/20'; -import IconViewOff from '@carbon/icons-vue/es/view--off/20'; +import IconView from '@carbon/icons-vue/es/view--filled/32'; +import IconViewOff from '@carbon/icons-vue/es/view--off--filled/32'; export default { name: 'InputPasswordToggle', @@ -48,6 +48,28 @@ export default { </script> <style lang="scss" scoped> +.input-action-btn, +.btn-icon-only { + margin: auto; + padding: 10px; + &:hover { + border-radius: 8px; + background-color: transparent; + } + &:focus { + box-shadow: none; + color: none; + } + &:active { + background-color: transparent; + } +} + +.btn-icon-only svg { + width: 30px; + height: 20px; +} + .input-password-toggle-container { position: relative; } diff --git a/src/components/_sila/Global/LoadingBar.vue b/src/components/_sila/Global/LoadingBar.vue index 0e9551b5..b65f97a7 100644 --- a/src/components/_sila/Global/LoadingBar.vue +++ b/src/components/_sila/Global/LoadingBar.vue @@ -74,12 +74,13 @@ export default { <style lang="scss" scoped> .progress { position: absolute; - left: 0; right: 0; bottom: -0.4rem; opacity: 1; transition: opacity $duration--moderate-01 $standard-easing--productive; height: 0.4rem; + width: calc(100vw - 320px); + border-radius: 0px; &.fade-enter, // Remove this vue2 based only class when switching to vue3 &.fade-enter-from, // This is vue3 based only class modified from 'fade-enter' @@ -89,5 +90,6 @@ export default { } .progress-bar { background-color: $loading-color; + border-radius: 0px; } </style> diff --git a/src/components/_sila/Global/PageContainer.vue b/src/components/_sila/Global/PageContainer.vue index ab4adb63..4c4c502d 100644 --- a/src/components/_sila/Global/PageContainer.vue +++ b/src/components/_sila/Global/PageContainer.vue @@ -1,11 +1,11 @@ <template> - <main id="main-content" class="page-container"> + <main id="main-content" class="page-container scroll-container"> <slot /> </main> </template> <script> -import JumpLinkMixin from '@/components/Mixins/JumpLinkMixin'; +import JumpLinkMixin from '@/components/_sila/Mixins/JumpLinkMixin'; export default { name: 'PageContainer', mixins: [JumpLinkMixin], @@ -19,19 +19,10 @@ export default { <style lang="scss" scoped> main { width: 100%; - height: 100%; - padding-top: $spacer * 1.5; - padding-bottom: $spacer * 3; - padding-left: $spacer; - padding-right: $spacer; &:focus-visible { box-shadow: inset 0 0 0 2px theme-color('primary'); outline: none; } - - @include media-breakpoint-up($responsive-layout-bp) { - padding-left: $spacer * 2; - } } </style> diff --git a/src/components/_sila/Global/PageSection.vue b/src/components/_sila/Global/PageSection.vue index f86649fe..0521dc06 100644 --- a/src/components/_sila/Global/PageSection.vue +++ b/src/components/_sila/Global/PageSection.vue @@ -1,6 +1,9 @@ <template> <div class="page-section"> <h2 v-if="sectionTitle">{{ sectionTitle }}</h2> + <span v-if="sectionSmallTitle" class="bold-16px"> + {{ sectionSmallTitle }} + </span> <slot /> </div> </template> @@ -13,17 +16,26 @@ export default { type: String, default: '', }, + sectionSmallTitle: { + type: String, + default: '', + }, }, }; </script> <style lang="scss" scoped> .page-section { - margin-bottom: $spacer; + margin-bottom: $spacer * 1; } h2 { @include font-size($h3-font-size); margin-bottom: $spacer; } + +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} </style> diff --git a/src/components/_sila/Global/PageTitle.vue b/src/components/_sila/Global/PageTitle.vue index 45c75edb..3f8ffe66 100644 --- a/src/components/_sila/Global/PageTitle.vue +++ b/src/components/_sila/Global/PageTitle.vue @@ -1,7 +1,11 @@ <template> - <div class="page-title"> - <h1>{{ title }}</h1> - <p v-if="description">{{ description }}</p> + <div v-if="description" class="page-title"> + <h1 class="bold-24px text-title">{{ title }}</h1> + <p class="page-description">{{ description }}</p> + </div> + <div v-else class="page-title no_description"> + <h1 class="bold-24px text-title">{{ title }}</h1> + <p class="page-description">{{ description }}</p> </div> </template> @@ -24,9 +28,33 @@ export default { <style lang="scss" scoped> .page-title { - margin-bottom: $spacer * 2; + width: 100%; + height: 72px; + border-bottom: 1px solid $faint-secondary-primary-10; + background-color: $white; + z-index: 1001; + + display: flex; + flex-flow: column nowrap; + align-items: flex-start; + justify-content: center; + &.no_description { + height: 64px; + } +} + +.text-title { + margin-left: 2rem; } -p { - max-width: 72ch; + +.page-description { + color: $text-secondary; + font-family: 'Inter', sans-serif; + font-size: 12px; + font-weight: 400; + line-height: 16px; + + margin: 4px 0 0 2rem; + padding: 0; } </style> diff --git a/src/components/_sila/Global/Popover.vue b/src/components/_sila/Global/Popover.vue new file mode 100644 index 00000000..0d52cfa5 --- /dev/null +++ b/src/components/_sila/Global/Popover.vue @@ -0,0 +1,287 @@ +<template> + <div id="popover-container"> + <div v-if="isclear" :id="id" ref="button" variant="primary" class="pointer"> + <img src="@/assets/images/icon-clear-red.svg" /> + <span class="regular-12px red-font">{{ $t(description) }}</span> + </div> + <div + v-else-if="isMicrocode" + :id="id" + ref="button" + variant="primary" + class="pointer" + > + <img src="@/assets/images/icon-reload-red.svg" /> + <span class="regular-12px red-font">{{ $t(description) }}</span> + </div> + + <div + v-else-if="isMicrocodeDrivers" + :id="id" + ref="button" + variant="primary" + class="pointer" + > + <img src="@/assets/images/icon-reload-red.svg" /> + <span class="semi-bold-16px red-font">{{ $t(description) }}</span> + </div> + + <span + v-else + :id="id" + ref="button" + class="regular-12px underline" + variant="primary" + > + {{ $t(description) }} + </span> + <!-- Our popover title and content render container --> + <b-popover + ref="popover" + :target="id" + triggers="click" + :show.sync="popoverShow" + placement="auto" + container="popover-container" + @show="onShow" + @shown="onShown" + @hidden="onHidden" + > + <template #title> + <div class="popup-title"> + <span class="bold-16px__caps">{{ $t(popup) }}</span> + <b-button class="popup-title__button_close" @click="onClose"> + <img src="@/assets/images/_sila/popups/x-icon.svg" /> + </b-button> + </div> + </template> + + <div class="popup-body"> + <div> + <label class="light-12px" style="margin-right: 5px">{{ + 'HEX для ввода' + }}</label> + <img id="popover-tooltip" src="@/assets/images/popups/red-sign.svg" /> + <popover-info + id="popover-tooltip" + description="Введите HEX в поле для подтверждения действия" + /> + <div class="hex-label"> + <span class="medium-12px"> 9c1735b3f819142393146a5d03314f0a </span> + </div> + </div> + <div class="popup-body__input-container"> + <span style="margin-left: 12px" class="regular-12px tretiatry" + >Поле для ввода</span + > + <b-form-input + id="popover-input-1" + v-model="input" + class="medium-12px" + ></b-form-input> + </div> + <b-button + class="popup-button" + variant="primary" + :disabled="!input" + @click="onOk" + > + {{ $t(button) }} + </b-button> + </div> + </b-popover> + </div> +</template> + +<script> +import PopoverInfo from './PopoverInfo'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +export default { + components: { + PopoverInfo, + }, + mixins: [BVToastMixin], + props: { + id: { + type: String, + default: '', + }, + description: { + type: String, + default: '', + }, + popup: { + type: String, + default: '', + }, + placement: { + type: String, + default: 'auto', + }, + button: { + type: String, + default: 'global.action.reload', + }, + isMicrocode: { + type: Boolean, + default: false, + }, + isMicrocodeDrivers: { + type: Boolean, + default: false, + }, + isclear: { + type: Boolean, + default: false, + }, + action: { + type: Function, + default: null, + }, + }, + data() { + return { + input: '', + popoverShow: false, + }; + }, + methods: { + onClose() { + this.popoverShow = false; + }, + onOk() { + if (this.input === '9c1735b3f819142393146a5d03314f0a') { + this.action(); + this.onClose(); + } else { + this.warningToast( + this.$t('Неправильный HEX в поле для подтверждения действия'), + { + title: this.$t('Неправильный НЕХ'), + } + ); + } + }, + onShow() { + // This is called just before the popover is shown + // Reset our popover form variables + this.$root.$emit('bv::hide::popover'); + this.input = ''; + }, + onShown() { + // Called just after the popover has been shown + // Transfer focus to the first input + this.focusRef(this.$refs.input1); + }, + onHidden() { + // Called just after the popover has finished hiding + // Bring focus back to the button + this.focusRef(this.$refs.button); + }, + focusRef(ref) { + // Some references may be a component, functional component, or plain element + // This handles that check before focusing, assuming a `focus()` method exists + // We do this in a double `$nextTick()` to ensure components have + // updated & popover positioned first + this.$nextTick(() => { + this.$nextTick(() => { + (ref.$el || ref).focus(); + }); + }); + }, + }, +}; +</script> +<style lang="scss" scoped> +.form-group { + margin: 0; +} + +.popup-title { + display: flex; + flex-flow: row nowrap; + align-items: baseline; +} + +.popup-title__button_close { + margin: 0 28px 0 auto; + background: none; + border: none; + &:active { + background-color: $faint-secondary-primary-5-hover !important; + box-shadow: none !important; + border-radius: 8px; + } + &:focus-visible { + border: none !important; + border-radius: 8px; + } + &:focus { + box-shadow: none; + border-radius: 8px; + } +} + +.popup-body { + display: flex; + flex-direction: column; + align-content: center; + justify-content: center; + align-items: center; +} + +.form-control { + width: 341px; + height: 52px; + margin: -25px auto; + padding-top: 30px; +} + +.hex-label { + height: 32px; + width: 341px; + display: flex; + justify-content: center; + align-items: center; + background: $faint-secondary-primary-5; + border-radius: 8px; + border: none; + margin: 0 auto; +} + +.popup-button { + width: 341px; + height: 40px; + margin-bottom: 10px; +} + +.popup-body__input-container { + height: 52px; + margin: 24px auto 16px auto; +} + +.tretiatry { + margin-left: 12px; +} + +.light-12px { + margin: 0px 5px 0px 15px; +} + +.popover-info { + background-color: $on-surface-primary; + // border: 1px solid $text-primary; + // box-shadow: 0px 6px 16px -12px rgba(23, 40, 77, 0.06); + border-radius: 8px; + width: 168px; + height: 76px; + &.arrow { + display: block; + } +} + +.red-font { + padding-left: 5px; + color: $red-brand-primary; +} +</style> diff --git a/src/components/_sila/Global/PopoverInfo.vue b/src/components/_sila/Global/PopoverInfo.vue new file mode 100644 index 00000000..4b1b0b0d --- /dev/null +++ b/src/components/_sila/Global/PopoverInfo.vue @@ -0,0 +1,40 @@ +<template> + <b-popover :target="id" triggers="hover" placement="top"> + <span class="regular-12px">{{ description }}</span> + </b-popover> +</template> + +<script> +export default { + props: { + description: { + type: String, + default: '', + }, + id: { + type: String, + default: '', + }, + }, +}; +</script> +<style lang="scss" scoped> +.popover::v-deep { + background-color: #040a0f; + width: 168px; + height: 76px; +} + +.popover::v-deep .arrow { + visibility: visible; +} + +.popover::v-deep .arrow::after { + border-top-color: #040a0f; + border-bottom-color: #040a0f; +} + +.regular-12px { + color: $white; +} +</style> diff --git a/src/components/_sila/Global/Search.vue b/src/components/_sila/Global/Search.vue index ac8f9bfb..ce097fcb 100644 --- a/src/components/_sila/Global/Search.vue +++ b/src/components/_sila/Global/Search.vue @@ -1,11 +1,12 @@ <template> <div class="search-global"> - <b-form-group - :label="$t('global.form.search')" + <!--<b-form-group + :label="$t('global.form.search')" :label-for="`searchInput-${_uid}`" label-class="invisible" - class="mb-2" - > + class="mb-2" + >--> + <b-form-group> <b-input-group size="md" class="align-items-center"> <b-input-group-prepend> <icon-search class="search-icon" /> diff --git a/src/components/_sila/Global/SilaComponents/ApplySettingsPopover.vue b/src/components/_sila/Global/SilaComponents/ApplySettingsPopover.vue new file mode 100644 index 00000000..57e0844c --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/ApplySettingsPopover.vue @@ -0,0 +1,163 @@ +<template> + <div class="popover-applay-container"> + <span class="apply-label regular-12px tretiatry"> + {{ $t('global.applySettings.apply') }}</span + > + <b-button :id="`popover-apply-ractive${id}`" variant="unstiled"> + <span class="regular-12px apply-variant"> + {{ $t(`global.applySettings.${applyType}`) }}</span + > + <img class="apply-chevron" src="@/assets/images/icon-chevron-red.svg" /> + </b-button> + <b-popover + placement="bottom" + triggers="focus" + :show.sync="show" + custom-class="apply-reload-popover" + :target="`popover-apply-ractive${id}`" + @hidden="onHidden" + > + <b-button + id="popover-apply-button" + variant="popover" + :class="{ 'hovered-apply-button': scale === topPosition }" + @mouseover="scale = topPosition" + @click=" + () => { + show = false; + appalyOnReload(); + } + " + > + При перезагрузке + </b-button> + <b-button + id="popover-apply-button" + variant="popover" + :class="{ 'hovered-apply-button': scale === middlePosition }" + @mouseover="scale = middlePosition" + @click=" + () => { + show = false; + appalyOption1(); + } + " + > + Опция 1 + </b-button> + <b-button + id="popover-apply-button" + variant="popover" + :class="{ 'hovered-apply-button': scale === bottomPosition }" + @mouseover="scale = bottomPosition" + @click=" + () => { + show = false; + appalyOption2(); + } + " + > + Опция 2 + </b-button> + <div class="slider" :style="`left: 5px; top: ${scale}px;`"></div> + </b-popover> + </div> +</template> + +<script> +export default { + props: { + id: { + type: Number, + default: 1, + }, + appalyOnReload: { + type: Function, + default: null, + }, + appalyOption1: { + type: Function, + default: null, + }, + appalyOption2: { + type: Function, + default: null, + }, + applyType: { + type: String, + default: 'reload', + }, + }, + data() { + return { + topPosition: 5, + middlePosition: 33, + bottomPosition: 60, + show: false, + scale: 5, + }; + }, + methods: { + onHidden() { + if (this.applyType === 'reload') { + this.scale = this.topPosition; + } else if (this.applyType === 'option1') { + this.scale = this.middlePosition; + } else { + this.scale = this.bottomPosition; + } + }, + }, +}; +</script> +<style lang="scss"> +.popover-applay-container { + display: flex; + align-items: baseline; + justify-content: flex-end; + margin-left: auto; +} + +#popover-apply-ractive { + padding-left: 5px; +} + +.hovered-apply-button { + color: $white; +} +</style> +<style lang="scss" scoped> +.apply-label { + margin-left: auto; +} + +.apply-chevron { + margin: 0 10px 0 5px; + cursor: pointer; +} + +#popover-apply-ractive { + padding-left: 5px; +} + +.apply-variant { + cursor: pointer; + color: $red-brand-primary; +} + +#popover-apply-button { + justify-content: flex-start; + width: 240px; +} + +.slider { + width: 240px; + height: 28px; + border-radius: 8px; + background-color: $red-brand-primary; + box-shadow: 1px 2px 4px -1px rgb(79 37 37 / 40%) inset; + position: absolute; + transition: ease-in 0.2s; + z-index: -1; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/DataTabs.vue b/src/components/_sila/Global/SilaComponents/DataTabs.vue new file mode 100644 index 00000000..49afbbfb --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/DataTabs.vue @@ -0,0 +1,136 @@ +<template> + <div> + <div + ref="content" + :class="'tabs-switch'" + :style="gridStyle" + @wheel.prevent="wheelItBetter($event)" + > + <span + v-for="item in slots" + :key="item.id" + class="medium-12px scale-item" + :class="{ 'tab-active': currentTab === item.id }" + :style="`width: ${slotWidth}px`" + @click="switchTab(item.id)" + >{{ $t(item.name) }}</span + > + <div class="slider" :style="sliderStyle" /> + </div> + </div> +</template> + +<script> +export default { + props: { + slots: { + type: Array, + default: null, + }, + currentTab: { + type: Number, + default: 1, + }, + switchTab: { + type: Function, + required: true, + }, + slotWidth: { + type: Number, + default: null, + }, + sliderWidth: { + type: Number, + default: null, + }, + }, + data() { + return { + upHere: false, + container: this.$refs.content, + }; + }, + computed: { + sliderStyle() { + return { + width: `${this.sliderWidth}px`, + left: `${ + ((this.currentTab ? this.currentTab : 1) - 1) * this.slotWidth + }px`, + }; + }, + gridStyle() { + return { + gridTemplateColumns: `repeat(${this.slots.length}, ${this.slotWidth}px)`, + }; + }, + }, + methods: { + wheelItBetter(event) { + if (event.deltaY < 0) { + this.$refs.content.scrollLeft -= 25; + } else { + this.$refs.content.scrollLeft += 25; + } + }, + }, +}; +</script> +<style lang="scss" scoped> +.tabs-switch { + position: relative; + width: calc(95vw - 320px); + height: 45px; + margin-left: 32px; + display: grid; + grid-auto-flow: column; + grid-template-rows: 32px; + align-items: end; + border-bottom: 1px solid $faint-secondary-primary-10; + overflow-x: auto; + white-space: nowrap; + &::-webkit-scrollbar { + height: 2px; + } + &::-webkit-scrollbar-thumb { + background: rgba(26, 62, 91, 0.2); + border-radius: 16px; + background-clip: content-box; + height: 10px; + } +} + +.tab-active { + color: $red-brand-primary; + transition: ease-in 0.15s; +} + +.slider { + position: absolute; + height: 0px; + border-bottom: 4px solid $red-brand-primary; + transition: ease-in 0.2s; + bottom: 0px; +} + +.scale-item { + display: inline-block; + margin-right: 6px; + cursor: pointer; + &:hover { + transition: ease-in 0.2s; + color: #e11717; + } +} + +.date-picker { + display: flex; + align-items: center; + gap: 9px; +} + +.date-clear { + margin-left: auto; + cursor: pointer; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/DateSwitch.vue b/src/components/_sila/Global/SilaComponents/DateSwitch.vue new file mode 100644 index 00000000..4df70ba4 --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/DateSwitch.vue @@ -0,0 +1,123 @@ +<template> + <div class="fans-date-switch"> + <span + class="medium-12px scale-item" + :class="{ 'switch-active': timeScale === 'hour' }" + @click="switchTimeScale('hour')" + >{{ $t('global.date.lastHour') }}</span + > + <span + class="medium-12px scale-item" + :class="{ 'switch-active': timeScale === 'day' }" + @click="switchTimeScale('day')" + >{{ $t('global.date.lastDay') }}</span + > + <span + class="medium-12px scale-item" + :class="{ 'switch-active': timeScale === 'week' }" + @click="switchTimeScale('week')" + >{{ $t('global.date.lastWeek') }}</span + > + <span + class="medium-12px scale-item" + :class="{ 'switch-active': timeScale === 'mounth' }" + @click="switchTimeScale('mounth')" + >{{ $t('global.date.lastMounth') }}</span + > + <span + class="medium-12px scale-item" + :class="{ 'switch-active': timeScale === 'year' }" + @click="switchTimeScale('year')" + >{{ $t('global.date.lastYear') }}</span + > + <div class="slider" /> + <div class="date-picker"> + <img src="@/assets/images/calendar-icon.svg" /> + <span class="medium-12px scale-item">{{ + $t('global.date.selectDate') + }}</span> + </div> + <img class="date-clear" src="@/assets/images/icon-clear-red.svg" /> + </div> +</template> + +<script> +export default { + props: { + timeScale: { + type: String, + default: 'hour', + }, + switchTimeScale: { + type: Function, + required: true, + }, + }, +}; +</script> +<style lang="scss" scoped> +.fans-date-switch { + position: relative; + + height: 48px; + padding: 0 16px 0 32px; + display: flex; + flex-flow: row nowrap; + align-items: center; + gap: 24px; + border-bottom: 1px solid $faint-secondary-primary-10; +} + +.switch-active { + color: $red-brand-primary; + transition: ease-in 0.15s; +} + +.slider { + position: absolute; + height: 0px; + border-bottom: 4px solid $red-brand-primary; + transition: ease-in 0.2s; + bottom: 0px; +} + +.scale-item { + cursor: pointer; +} + +.scale-item:nth-child(1).switch-active ~ .slider { + width: 92px; + left: 31px; +} + +.scale-item:nth-child(2).switch-active ~ .slider { + left: 135px; + width: 105px; +} + +.scale-item:nth-child(3).switch-active ~ .slider { + left: 255px; + width: 112px; +} + +.scale-item:nth-child(4).switch-active ~ .slider { + left: 383px; + width: 107px; +} + +.scale-item:nth-child(5).switch-active ~ .slider { + left: 508px; + width: 90px; +} + +.date-picker { + display: flex; + align-items: center; + gap: 9px; +} + +.date-clear { + margin-left: auto; + cursor: pointer; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/InventoryControlSystem.vue b/src/components/_sila/Global/SilaComponents/InventoryControlSystem.vue new file mode 100644 index 00000000..18f1d9ff --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/InventoryControlSystem.vue @@ -0,0 +1,195 @@ +<template> + <page-section class="system-control-section"> + <div class="system-control__table"> + <div class="system-control__table__row"> + <div class="system-control__table__cell"> + <div> + <span class="semi-bold-12px"> + {{ $t('SystemDescription.title.ReloadServer') }} + </span> + </div> + <popover + id="popover-reactive-1" + description="SystemDescription.ReloadOSAndServer" + popup="SystemDescription.ReloadOSAndServer_popup" + /> + <popover + id="popover-reactive-2" + description="SystemDescription.ReloadServer" + popup="SystemDescription.ReloadServer_popup" + /> + <div> + <span class="regular-12px underline" @click="redirectConsole"> + {{ $t('SystemDescription.ConnectToDesktop') }} + </span> + </div> + </div> + + <div class="system-control__table__cell system-control__table__cell__2"> + <div class="reload-progress__container"> + <span class="regular-12px"> + {{ $t('SystemDescription.status') }} + </span> + <span class="semi-bold-12px progress_bar_percent" + >{{ progress1.value }}%</span + > + <b-progress + class="reload-progress" + :value="progress1.value" + ></b-progress> + </div> + <div class="reload-progress__container"> + <span class="regular-12px"> + {{ $t('SystemDescription.status') }} + </span> + <span + v-if="progress2.value === null" + class="semi-bold-12px progress_bar_percent" + >{{ $t('SystemDescription.NotRunning') }}</span + > + <span v-else class="semi-bold-12px progress_bar_percent" + >{{ progress2.value }}%</span + > + <b-progress + class="reload-progress" + :value="progress2.value" + ></b-progress> + </div> + </div> + + <div class="system-control__table__cell"> + <div> + <span class="semi-bold-12px"> + {{ $t('SystemDescription.title.OnOffServer') }} + </span> + </div> + <div> + <popover + id="popover-reactive-3" + description="SystemDescription.OffOsAndServer" + popup="SystemDescription.OffOsAndServer_popup" + button="global.action.off" + /> + <popover + id="popover-reactive-4" + description="SystemDescription.OffServer" + popup="SystemDescription.OffServer_popup" + button="global.action.off" + /> + </div> + </div> + </div> + <div class="system-control__table__row"> + <div class="system-control__table__cell system-control__table__cell__4"> + <div> + <span class="semi-bold-12px"> + {{ $t('SystemDescription.title.setupDatetime') }} + </span> + </div> + <b-form @submit.prevent="onResetSubmit"> + <b-form-radio-group + v-model="timeOption" + class="system-control__radio regular-12px" + > + <b-form-radio variant="radio" value="NTP"> + {{ $t('SystemDescription.GetNtpFromServer') }} + </b-form-radio> + <b-form-radio variant="radio" value="serverDate"> + {{ $t('SystemDescription.UseServerDatettime') }} + </b-form-radio> + </b-form-radio-group> + </b-form> + <ntp-popover + id="popover-reactive-5" + description="SystemDescription.NtpSettings" + /> + </div> + </div> + </div> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import Popover from '@/components/Global/Popover'; +import NtpPopover from '@/components/Global/SilaComponents/NtpPopover'; + +export default { + components: { + PageSection, + NtpPopover, + Popover, + }, + data() { + return { + timeOption: 'resetBios', + picked: '', + options: [ + { text: 'Toggle this custom radio', value: 'first' }, + { text: 'Or toggle this other custom radio', value: 'second' }, + ], + progress1: { + value: 90, + }, + progress2: { + value: null, + }, + }; + }, + methods: { + redirectConsole() { + this.$router.push('/console/kvm'); + }, + }, +}; +</script> +<style lang="scss" scoped> +a { + list-style-type: none; +} + +.system-control-section { + position: relative; + margin: 16px 2rem 2rem !important; + width: 90%; +} + +.system-control__table__row { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + width: 85%; +} + +.system-control__table__cell { + display: flex; + flex-flow: column nowrap; + align-items: flex-start; + row-gap: 6px; +} + +.system-control__table__cell__2 { + margin-top: 37px; + row-gap: 14px; +} + +.reload-progress__container { + display: flex; + flex-flow: row nowrap; + align-items: baseline; + justify-content: space-between; + width: 100%; + column-gap: 4px; +} + +.semi-bold-12px { + display: inline-block; +} + +label { + padding-top: 5px; +} +.system-control__table__cell__4 { + margin-top: 26px; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/NtpPopover.vue b/src/components/_sila/Global/SilaComponents/NtpPopover.vue new file mode 100644 index 00000000..0b476f72 --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/NtpPopover.vue @@ -0,0 +1,132 @@ +<template> + <div id="my-container"> + <span :id="id" class="regular-12px underline" variant="primary"> + {{ $t(description) }} + </span> + <!-- Our popover title and content render container --> + <b-popover + :target="id" + placement="auto" + container="my-container" + :show.sync="popoverShow" + @show="onShow" + > + <template #title> + <div class="popup-title"> + <span class="bold-16px__caps">{{ $t(description) }}</span> + <b-button class="popup-title__button_close" @click="onClose"> + <img src="@/assets/images/_sila/popups/x-icon.svg" /> + </b-button> + </div> + </template> + + <div class="popup-body"> + <div class="popup-body__input-container"> + <span class="regular-12px tretiatry" + >Введите адрес сервера (IP, FQDM)</span + > + <b-form-input + id="popover-input-1" + v-model="input1" + class="medium-12px" + ></b-form-input> + </div> + <b-button class="popup-button" variant="primary" @click="onClose"> + {{ $t('global.action.save') }} + </b-button> + </div> + </b-popover> + </div> +</template> + +<script> +export default { + props: { + description: { + type: String, + default: '', + }, + id: { + type: String, + default: '', + }, + button: { + type: String, + default: 'Reload', + }, + }, + data() { + return { + input1: '', + popoverShow: false, + }; + }, + methods: { + onShow() { + this.$root.$emit('bv::hide::popover'); + }, + onClose() { + this.popoverShow = false; + }, + }, +}; +</script> +<style lang="scss" scoped> +.form-group { + margin: 0; +} + +.popup-title { + display: flex; + flex-flow: row nowrap; + align-items: baseline; +} + +.popup-title__button_close { + margin: 0 28px 0 auto; + background: none; + border: none; + &:active { + background-color: $faint-secondary-primary-5-hover !important; + box-shadow: none !important; + border-radius: 8px; + } + &:focus-visible { + border: none !important; + border-radius: 8px; + } + &:focus { + box-shadow: none; + border-radius: 8px; + } +} + +.popup-body { + display: flex; + flex-direction: column; + align-content: center; + justify-content: center; +} + +.form-control { + width: 341px; + height: 52px; + margin: -25px auto; + padding-top: 30px; +} + +.popup-button { + width: 341px; + height: 40px; + margin: 0 auto 10px; +} + +.popup-body__input-container { + height: 52px; + margin: 24px auto 16px auto; +} + +.tretiatry { + margin-left: 12px; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/PopoverWithSlot.vue b/src/components/_sila/Global/SilaComponents/PopoverWithSlot.vue new file mode 100644 index 00000000..4c908083 --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/PopoverWithSlot.vue @@ -0,0 +1,228 @@ +<template> + <div id="popover-container"> + <slot /> + <b-popover + ref="popover" + :target="id" + triggers="focus" + :show.sync="popoverShow" + :placement="placement" + container="popover-container" + @show="onShow" + @shown="onShown" + @hidden="onHidden" + > + <template #title> + <div class="popup-title"> + <span class="bold-16px__caps">{{ popupLabel }}</span> + <b-button class="popup-title__button_close" @click="onClose"> + <img src="@/assets/images/_sila/popups/x-icon.svg" /> + </b-button> + </div> + </template> + + <div class="popup-body"> + <div> + <label class="light-12px" style="margin-right: 5px">{{ + 'HEX для ввода' + }}</label> + <img + id="popover-tooltip" + src="@/assets/images/_sila/popups/red-sign.svg" + /> + <popover-info + id="popover-tooltip" + description="Введите HEX в поле для подтверждения действия" + /> + <div class="hex-label"> + <span class="medium-12px"> 9c1735b3f819142393146a5d03314f0a </span> + </div> + </div> + <div class="popup-body__input-container"> + <span style="margin-left: 12px" class="regular-12px tretiatry" + >Поле для ввода</span + > + <b-form-input + id="popover-input-1" + ref="input" + v-model="input" + class="medium-12px" + ></b-form-input> + </div> + <b-button + class="popup-button" + variant="primary" + :disabled="!input" + @click="onOk" + > + {{ buttonLabel }} + </b-button> + </div> + </b-popover> + </div> +</template> + +<script> +import PopoverInfo from '../PopoverInfo'; +import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; +export default { + components: { + PopoverInfo, + }, + mixins: [BVToastMixin], + props: { + id: { + type: String, + default: '', + }, + placement: { + type: String, + default: 'auto', + }, + popupLabel: { + type: String, + default: '', + }, + buttonLabel: { + type: String, + default: 'global.action.reload', + }, + action: { + type: Function, + default: null, + }, + }, + data() { + return { + input: '', + popoverShow: false, + }; + }, + methods: { + onClose() { + this.popoverShow = false; + }, + onOk() { + if (this.input === '9c1735b3f819142393146a5d03314f0a') { + this.action(); + this.onClose(); + } else { + this.warningToast( + this.$t('Неправильный HEX в поле для подтверждения действия'), + { + title: this.$t('Неправильный НЕХ'), + } + ); + } + }, + onShow() { + // This is called just before the popover is shown + // Reset our popover form variables + this.$root.$emit('bv::hide::popover'); + this.input = ''; + }, + onShown() { + // Called just after the popover has been shown + // Transfer focus to the first input + this.focusRef(this.$refs.input); + }, + onHidden() { + // Called just after the popover has finished hiding + // Bring focus back to the button + // this.focusRef(this.$refs.button); + }, + focusRef(ref) { + // Some references may be a component, functional component, or plain element + // This handles that check before focusing, assuming a `focus()` method exists + // We do this in a double `$nextTick()` to ensure components have + // updated & popover positioned first + this.$nextTick(() => { + this.$nextTick(() => { + (ref.$el || ref).focus(); + }); + }); + }, + }, +}; +</script> +<style lang="scss" scoped> +.form-group { + margin: 0; +} + +.popup-title { + display: flex; + flex-flow: row nowrap; + align-items: baseline; +} + +.popup-title__button_close { + margin: 0 28px 0 auto; + background: none; + border: none; + height: 25px; + &:active { + background-color: $faint-secondary-primary-5-hover !important; + box-shadow: none !important; + border-radius: 8px; + } + &:focus-visible { + border: none !important; + border-radius: 8px; + } + &:focus { + box-shadow: none; + border-radius: 8px; + } +} + +.popup-body { + display: flex; + flex-direction: column; + align-content: center; + justify-content: center; + align-items: center; +} + +.form-control { + width: 341px; + height: 52px; + margin: -25px auto; + padding-top: 30px; +} + +.hex-label { + height: 32px; + width: 341px; + display: flex; + justify-content: center; + align-items: center; + background: $faint-secondary-primary-5; + border-radius: 8px; + border: none; + margin: 0 auto; +} + +.popup-button { + width: 341px; + height: 40px; + margin-bottom: 10px; +} + +.popup-body__input-container { + height: 52px; + margin: 24px auto 16px auto; +} + +.popover-info { + background-color: $on-surface-primary; + // border: 1px solid $text-primary; + // box-shadow: 0px 6px 16px -12px rgba(23, 40, 77, 0.06); + border-radius: 8px; + width: 168px; + height: 76px; + &.arrow { + display: block; + } +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/Tables/AccessoryTable.vue b/src/components/_sila/Global/SilaComponents/Tables/AccessoryTable.vue new file mode 100644 index 00000000..4cb64929 --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/Tables/AccessoryTable.vue @@ -0,0 +1,88 @@ +<template> + <section class="bootstrap-table__section"> + <b-table + id="table-accessory" + responsive="md" + class="table-accessory" + no-border-collapse + :items="records.items" + :fields="records.fields" + > + <template #cell(name)="{ value, index }"> + <div + class="fans-colors" + :style="`background-color: ${colors[index]}`" + ></div> + <span class="light-12px"> + {{ value }} + </span> + </template> + <template #cell(minDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #cell(maxDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #head(currentPower)="{ label }"> + <span class="semi-bold-12px"> + {{ label }} + </span> + <span class="semi-bold-12px errors"> + {{ '(15)' }} + </span> + </template> + <template #cell(currentPower)="{ value }"> + <row> + <span class="light-12px"> + {{ value }} + </span> + </row> + </template> + </b-table> + </section> +</template> + +<script> +import { colors } from '../colors'; + +export default { + props: { + records: { + type: Object, + default: null, + }, + }, + data() { + return { + colors, + }; + }, +}; +</script> +<style lang="scss" scoped> +.fans-colors { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 2px; +} +.row { + align-items: center; + flex-wrap: nowrap; + justify-content: flex-end; +} + +.errors { + color: $indicators-errors; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/Tables/AccessoryTableDrivers.vue b/src/components/_sila/Global/SilaComponents/Tables/AccessoryTableDrivers.vue new file mode 100644 index 00000000..1f511ffc --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/Tables/AccessoryTableDrivers.vue @@ -0,0 +1,90 @@ +<template> + <section class="bootstrap-table__section"> + <span class="bold-12px__caps"> + {{ $t('fansPage.valueIndicators') }} + </span> + <b-table + responsive="md" + class="table-accessory" + no-border-collapse + :items="records.items" + :fields="records.fields" + > + <template #cell(name)="{ value, index }"> + <div + class="fans-colors" + :style="`background-color: ${colors[index]}`" + ></div> + <span class="light-12px"> + {{ value }} + </span> + </template> + <template #cell(minDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #cell(maxDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #head(currentPower)="{ label }"> + <span class="semi-bold-12px"> + {{ label }} + </span> + <span class="semi-bold-12px errors"> + {{ '(15)' }} + </span> + </template> + <template #cell(currentPower)="{ value }"> + <row> + <span class="light-12px"> + {{ value }} + </span> + </row> + </template> + </b-table> + </section> +</template> + +<script> +import { colors } from '../colors'; + +export default { + props: { + records: { + type: Object, + default: null, + }, + }, + data() { + return { + colors, + }; + }, +}; +</script> +<style lang="scss" scoped> +.fans-colors { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 2px; +} +.row { + align-items: center; + flex-wrap: nowrap; + justify-content: flex-end; +} + +.errors { + color: $indicators-errors; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/Tables/AccessoryTablePower.vue b/src/components/_sila/Global/SilaComponents/Tables/AccessoryTablePower.vue new file mode 100644 index 00000000..4e957c6e --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/Tables/AccessoryTablePower.vue @@ -0,0 +1,91 @@ +<template> + <section class="bootstrap-table__section"> + <span class="bold-12px__caps"> + {{ $t('fansPage.valueIndicators') }} + </span> + <b-table + responsive="md" + class="table-accessory" + no-border-collapse + :items="records.items" + :fields="records.fields" + > + <template #cell(name)="{ value, index }"> + <div + class="fans-colors" + :style="`background-color: ${colors[index]}`" + ></div> + <span class="light-12px"> + {{ value }} + </span> + </template> + <template #cell(minDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #cell(maxDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #head(currentPower)="{ label }"> + <span class="semi-bold-12px"> + {{ label }} + </span> + <span class="semi-bold-12px errors"> + {{ '(15)' }} + </span> + </template> + <template #cell(currentPower)="{ value }"> + <row> + <span class="light-12px"> + {{ value }} + </span> + <img src="@/assets/images/power-error-icon.svg" /> + </row> + </template> + </b-table> + </section> +</template> + +<script> +import { colors } from '../../../../views/Processors/DynamicInfo/helpers'; + +export default { + props: { + records: { + type: Object, + default: null, + }, + }, + data() { + return { + colors, + }; + }, +}; +</script> +<style lang="scss" scoped> +.fans-colors { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 2px; +} +.row { + align-items: center; + flex-wrap: nowrap; + justify-content: flex-end; +} + +.errors { + color: $indicators-errors; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/Tables/AccessoryTableWithLabel.vue b/src/components/_sila/Global/SilaComponents/Tables/AccessoryTableWithLabel.vue new file mode 100644 index 00000000..37de1c6b --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/Tables/AccessoryTableWithLabel.vue @@ -0,0 +1,99 @@ +<template> + <section class="bootstrap-table__section"> + <span class="bold-12px__caps"> + {{ $t('fansPage.valueIndicators') }} + </span> + <b-table + responsive="md" + class="table-accessory" + no-border-collapse + :items="records.items" + :fields="records.fields" + > + <template #cell(name)="{ value, index }"> + <div + class="fans-colors" + :style="`background-color: ${colors[index]}`" + ></div> + <span class="light-12px"> + {{ value }} + </span> + </template> + <template #cell(minDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #cell(maxDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #head(currentPower)="{ label }"> + <span class="semi-bold-12px"> + {{ label }} + </span> + <span class="semi-bold-12px errors"> + {{ '(15)' }} + </span> + </template> + <template #cell(currentPower)="{ value }"> + <row> + <span class="light-12px"> + {{ value }} + </span> + </row> + </template> + </b-table> + </section> +</template> + +<script> +export default { + props: { + records: { + type: Object, + default: null, + }, + }, + data() { + return { + colors: [], + }; + }, + watch: { + 'records.items'(value) { + if (value && value.length > 0) { + this.colors = this.$randomColor({ + count: value.length, + hue: 'random', + luminosity: 'random', + }); + } + }, + }, +}; +</script> +<style lang="scss" scoped> +.fans-colors { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 2px; +} +.row { + align-items: center; + flex-wrap: nowrap; + justify-content: flex-end; +} + +.errors { + color: $indicators-errors; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/TwoChiocePopover.vue b/src/components/_sila/Global/SilaComponents/TwoChiocePopover.vue new file mode 100644 index 00000000..c89c5a81 --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/TwoChiocePopover.vue @@ -0,0 +1,117 @@ +<template> + <b-popover + :placement="placement" + triggers="focus" + :show.sync="show" + custom-class="popover-heigth-100" + :target="`popover-choice-${id}`" + @hidden="onHidden" + > + <b-button + id="popover-choice-button" + variant="popover" + :class="{ 'selected-choice-button': scale === topPosition }" + @mouseover="scale = topPosition" + @click=" + () => { + show = false; + firstAction(); + } + " + > + {{ fitstOption }} + </b-button> + <b-button + id="popover-choice-button" + variant="popover" + :class="{ 'selected-choice-button': scale === bottomPosition }" + @mouseover="scale = bottomPosition" + @click=" + () => { + show = false; + secondAction(); + } + " + > + {{ secondOption }} + </b-button> + <div class="slider" :style="`left: 5px; top: ${scale}px;`"></div> + </b-popover> +</template> + +<script> +export default { + props: { + id: { + type: Number, + default: null, + }, + fitstOption: { + type: String, + default: null, + }, + secondOption: { + type: String, + default: null, + }, + chosenOption: { + type: String, + default: null, + }, + firstAction: { + type: Function, + default: null, + }, + secondAction: { + type: Function, + default: null, + }, + placement: { + type: String, + default: 'bottom', + }, + }, + data() { + return { + topPosition: 5, + bottomPosition: 33, + show: false, + scale: 5, + }; + }, + methods: { + onHidden() { + if (this.secondOption === this.chosenOption) { + this.scale = this.bottomPosition; + } else { + this.scale = this.topPosition; + } + }, + }, +}; +</script> +<style lang="scss"> +#popover-unit-ractive { + padding-left: 5px; +} + +.hovered-unit-button { + color: $white; +} +</style> +<style lang="scss" scoped> +#popover-choice-button { + width: 89px; +} + +.slider { + width: 89px; + height: 28px; + border-radius: 8px; + background-color: $red-brand-primary; + box-shadow: 1px 2px 4px -1px rgb(79 37 37 / 40%) inset; + position: absolute; + transition: ease-in 0.2s; + z-index: -1; +} +</style> diff --git a/src/components/_sila/Global/SilaComponents/colors.js b/src/components/_sila/Global/SilaComponents/colors.js new file mode 100644 index 00000000..de832de2 --- /dev/null +++ b/src/components/_sila/Global/SilaComponents/colors.js @@ -0,0 +1,8 @@ +export const colors = [ + '#CB32F1', + '#F18638', + '#139BB9', + '#E1AB17', + '#175AE1', + '#13B937', +]; diff --git a/src/components/_sila/Global/StatusIcon.vue b/src/components/_sila/Global/StatusIcon.vue index 4552633e..8f9e3d53 100644 --- a/src/components/_sila/Global/StatusIcon.vue +++ b/src/components/_sila/Global/StatusIcon.vue @@ -9,20 +9,20 @@ </template> <script> -import IconInfo from '@carbon/icons-vue/es/information--filled/20'; -import IconCheckmark from '@carbon/icons-vue/es/checkmark--filled/20'; -import IconWarning from '@carbon/icons-vue/es/warning--filled/20'; -import IconError from '@carbon/icons-vue/es/error--filled/20'; -import IconMisuse from '@carbon/icons-vue/es/misuse/20'; +import IconInfo from '@carbon/icons-vue/es/information/20'; +import IconCheckmark from '@carbon/icons-vue/es/checkmark--outline/20'; +import IconWarning from '@carbon/icons-vue/es/warning/20'; +import IconError from '@carbon/icons-vue/es/error--outline/20'; +import IconMisuse from '@carbon/icons-vue/es/misuse--outline/20'; export default { name: 'StatusIcon', components: { IconInfo: IconInfo, iconSuccess: IconCheckmark, - iconDanger: IconMisuse, iconSecondary: IconError, iconWarning: IconWarning, + iconDanger: IconMisuse, }, props: { status: { diff --git a/src/components/_sila/Global/TableCellCount.vue b/src/components/_sila/Global/TableCellCount.vue index acb4d443..e64475bc 100644 --- a/src/components/_sila/Global/TableCellCount.vue +++ b/src/components/_sila/Global/TableCellCount.vue @@ -1,5 +1,5 @@ <template> - <div class="mt-2"> + <div class="semi-bold-14px"> <p v-if="!filterActive"> {{ $t('global.table.items', { count: totalNumberOfCells }) }} </p> diff --git a/src/components/_sila/Global/TableDateFilter.vue b/src/components/_sila/Global/TableDateFilter.vue index aa10cb5c..49e4b8ff 100644 --- a/src/components/_sila/Global/TableDateFilter.vue +++ b/src/components/_sila/Global/TableDateFilter.vue @@ -1,9 +1,10 @@ <template> - <b-row class="mb-2"> + <b-row class="mb-2 mt-2"> <b-col class="d-sm-flex"> <b-form-group :label="$t('global.table.fromDate')" label-for="input-from-date" + label-class="caption-12px mb-0" class="mr-3 my-0 w-100" > <b-input-group> @@ -12,7 +13,7 @@ v-model="fromDate" placeholder="YYYY-MM-DD" :state="getValidationState($v.fromDate)" - class="form-control-with-button mb-3 mb-md-0" + class="form-control-with-button mb-md-0" @blur="$v.fromDate.$touch()" /> <b-form-invalid-feedback role="alert"> @@ -50,6 +51,7 @@ <b-form-group :label="$t('global.table.toDate')" label-for="input-to-date" + label-class="caption-12px mb-0" class="my-0 w-100" > <b-input-group> @@ -101,7 +103,7 @@ import IconCalendar from '@carbon/icons-vue/es/calendar/20'; import { helpers } from 'vuelidate/lib/validators'; -import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; +import VuelidateMixin from '@/components/_sila/Mixins/VuelidateMixin.js'; const isoDateRegex = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/; diff --git a/src/components/_sila/Global/TableToolbar.vue b/src/components/_sila/Global/TableToolbar.vue index 5235feae..4137d98c 100644 --- a/src/components/_sila/Global/TableToolbar.vue +++ b/src/components/_sila/Global/TableToolbar.vue @@ -7,7 +7,7 @@ </p> <div class="toolbar-actions d-flex"> <slot name="toolbar-buttons"></slot> - <b-button + <!-- <b-button v-for="(action, index) in actions" :key="index" :data-test-id="`table-button-${action.value}Selected`" @@ -16,14 +16,14 @@ @click="$emit('batch-action', action.value)" > {{ action.label }} - </b-button> - <b-button + </b-button> --> + <!-- <b-button variant="secondary" class="d-block" @click="$emit('clear-selected')" > {{ $t('global.action.cancel') }} - </b-button> + </b-button> --> </div> </div> </div> @@ -67,27 +67,26 @@ export default { }, }; </script> - <style lang="scss" scoped> -$toolbar-height: 46px; - .toolbar-container { - width: 100%; - position: relative; + width: calc(100vw - 320px); + height: $toolbar-height; + border-radius: 0px; + position: fixed; + bottom: 0; + right: 0; z-index: $zindex-dropdown + 1; } .toolbar-content { - height: $toolbar-height; - background-color: theme-color('primary'); + background-color: $white; color: $white; - position: absolute; - left: 0; - right: 0; - top: -$toolbar-height; display: flex; flex-direction: row; + align-items: center; justify-content: space-between; + box-shadow: 0px -4px 12px rgba(0, 0, 0, 0.06); + border-radius: 0px; } .toolbar-selected { diff --git a/src/components/_sila/Global/TableToolbarExport.vue b/src/components/_sila/Global/TableToolbarExport.vue index 69646ea6..152a4f68 100644 --- a/src/components/_sila/Global/TableToolbarExport.vue +++ b/src/components/_sila/Global/TableToolbarExport.vue @@ -1,16 +1,15 @@ <template> - <b-button - class="d-flex align-items-center" - variant="primary" - :download="download" - :href="href" - > - {{ $t('global.action.export') }} + <b-button size="md" variant="primary" :download="download" :href="href"> + <icon-export /> {{ $t('global.action.export') }} </b-button> </template> <script> +import IconExport from '@carbon/icons-vue/es/document--export/20'; export default { + components: { + IconExport, + }, props: { data: { type: Array, diff --git a/src/components/_sila/SubHeader/SubHeader.vue b/src/components/_sila/SubHeader/SubHeader.vue new file mode 100644 index 00000000..086fc7b8 --- /dev/null +++ b/src/components/_sila/SubHeader/SubHeader.vue @@ -0,0 +1,179 @@ +<template> + <div> + <section id="sub-header"> + <b-navbar type="dark" :aria-label="$t('appHeader.applicationHeader')"> + <!-- top navigation menu --> + <b-navbar-nav> + <b-nav-item + to="/hardware-status/inventory" + data-test-id="appHeader-container-health" + class="subheader-button" + :class="{ + 'active-route-top': ![ + 'profile-settings', + 'information-and-faq', + 'support', + 'console-settings', + 'console', + 'operations', + 'security-and-access', + ].includes($route.path.split('/')[1]), + }" + > + {{ $t('subHeader.serverInfo') }} + </b-nav-item> + + <b-nav-item + to="/operations/kvm" + data-test-id="appHeader-container-health" + class="subheader-button" + :class="{ + 'active-route-top': + ''.includes($route.path.split('/console/settings')[1]) || + ''.includes( + $route.path.split('/operations/serial-over-lan')[1] + ) || + ''.includes($route.path.split('/operations/kvm')[1]), + }" + > + {{ $t('subHeader.console') }} + </b-nav-item> + + <b-nav-item + to="/security-and-access/user-management" + data-test-id="appHeader-container-power" + class="subheader-button" + :class="{ + 'active-route-top': ''.includes( + $route.path.split('security-and-access/user-management')[1] + ), + }" + > + {{ $t('subHeader.administration') }} + </b-nav-item> + <!-- Using LI elements instead of b-nav-item to support semantic button elements --> + </b-navbar-nav> + </b-navbar> + </section> + <loading-bar /> + </div> +</template> + +<script> +import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; +import LoadingBar from '@/components/_sila/Global/LoadingBar'; + +export default { + name: 'AppHeader', + components: { + LoadingBar, + }, + mixins: [BVToastMixin], + props: { + routerKey: { + type: Number, + default: 0, + }, + }, + data() { + return { + isNavigationOpen: false, + }; + }, + computed: { + isNavTagPresent() { + return this.assetTag || this.modelType || this.serialNumber; + }, + }, + mounted() { + this.$root.$on( + 'change-is-navigation-open', + (isNavigationOpen) => (this.isNavigationOpen = isNavigationOpen) + ); + }, + methods: { + toggleNavigation() { + this.$root.$emit('toggle-navigation'); + }, + setFocus(event) { + event.preventDefault(); + this.$root.$emit('skip-navigation'); + }, + }, +}; +</script> + +<style lang="scss"> +@mixin focus-box-shadow($padding-color: $navbar-color, $outline-color: $white) { + box-shadow: inset 0 0 0 3px $padding-color, inset 0 0 0 5px $outline-color; +} +.sub-header { + height: $second-header-height; + + .subheader-button { + margin-left: 10px; + } + + .navbar-text, + .nav-link, + .btn-link { + border-radius: 14% 14% 0px 0px; + padding: 0.68rem 1rem !important; + + &:hover { + background-color: $white; + color: #1a3e5b !important; + border-radius: 14% 14% 0px 0px; + } + &:active { + background-color: $white; + color: #1a3e5b !important; + border-radius: 14% 14% 0px 0px; + } + } + + .navbar { + padding: 0; + background-color: $navbar-color; + @include media-breakpoint-up($responsive-layout-bp) { + height: $second-header-height; + } + + .helper-menu { + @include media-breakpoint-down(sm) { + width: 100%; + justify-content: flex-end; + .nav-link, + .btn { + padding: $spacer / 1.125 $spacer / 2; + margin-left: 10px; + } + } + } + } + + .navbar-nav { + align-items: baseline; + padding-left: 24px; + .nav-tags { + @include media-breakpoint-down(xs) { + @include sr-only; + } + .asset-tag { + @include media-breakpoint-down($responsive-layout-bp) { + @include sr-only; + } + } + } + } +} +#sub-header .nav-item.active-route-top > a { + background-color: $white; + color: #1a3e5b; + border-radius: 14% 14% 0px 0px; +} + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 1); +} +</style> diff --git a/src/components/_sila/SubHeader/index.js b/src/components/_sila/SubHeader/index.js new file mode 100644 index 00000000..ad26fced --- /dev/null +++ b/src/components/_sila/SubHeader/index.js @@ -0,0 +1 @@ +export { default } from './SubHeader.vue'; |