diff options
Diffstat (limited to 'src/views')
160 files changed, 20573 insertions, 792 deletions
diff --git a/src/views/_sila/BMC/Configuration/BMCConfiguration.vue b/src/views/_sila/BMC/Configuration/BMCConfiguration.vue new file mode 100644 index 00000000..111fa81a --- /dev/null +++ b/src/views/_sila/BMC/Configuration/BMCConfiguration.vue @@ -0,0 +1,105 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.description.configuration')" /> + <!-- BMC table --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_1 + variant="link" + class="collapse-button semi-bold-16px" + > + {{ $t('BMC.BmcTitle') }} + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_1" visible class="nav-item__nav"> + <span class="semi-bold-12px">{{ $t('BMC.BmcTable') }}</span> + <b-m-c-configuration-table /> + <span class="semi-bold-12px">{{ $t('BMC.Bios') }}</span> + <div> + <span class="regular-12px bmc-configuration__bios-version">{{ + $t('BMC.BiosV') + }}</span> + <span class="medium-12px">V 3.2.10.0</span> + </div> + </b-collapse> + </div> + <!-- Control --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_2 + variant="link" + class="collapse-button semi-bold-16px" + > + {{ $t('BMC.ControlTitle') }} + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_2" visible class="nav-item__nav"> + <b-m-c-configuration-control /> + </b-collapse> + </div> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import BMCConfigurationTable from './BMCConfigurationTable'; +import BMCConfigurationControl from './BMCConfigurationControl'; +import PageSection from '@/components/Global/PageSection'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; + +export default { + components: { + PageTitle, + BMCConfigurationControl, + BMCConfigurationTable, + PageSection, + }, + mixins: [LoadingBarMixin], + data() { + return { + text: '', + iconChevronUp: iconChevronUp, + }; + }, + created() { + this.startLoader(); + const bmcManagerTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-bmc-manager-complete', () => resolve()); + }); + Promise.all([bmcManagerTablePromise]).finally(() => this.endLoader()); + }, +}; +</script> +<style lang="scss" scoped> +//nav items style +.nav-item, +.nav-link { + padding: 0; +} + +.nav-item { + list-style-type: none; +} + +.semi-bold-12px { + display: inline-block; + padding: 16px 0 0 2rem; +} + +.bmc-configuration__bios-version { + display: inline-block; + color: #0c1c29; + padding: 8px 2px 2rem 2rem; +} + +a { + color: $text-primary !important; + &:hover { + color: $text-primary !important; + } +} +</style> diff --git a/src/views/_sila/BMC/Configuration/BMCConfigurationControl.vue b/src/views/_sila/BMC/Configuration/BMCConfigurationControl.vue new file mode 100644 index 00000000..e75b5f7d --- /dev/null +++ b/src/views/_sila/BMC/Configuration/BMCConfigurationControl.vue @@ -0,0 +1,121 @@ +<template> + <page-section class="bmc-control-section"> + <div class="bmc-control__table"> + <div class="bmc-control__table__cell"> + <div> + <span class="semi-bold-12px"> {{ $t('BMC.ControlBmc') }} </span> + </div> + <popover + id="popover-reactive-1" + description="BMC.ReloadBmc" + popup="BMC.ReloadBmc_popup" + button="BMC.ReloadBmc" + :action="rebootBmc" + /> + <settings-import-popup + id="popover-reactive-2" + description="BMC.ExportImport" + popup="BMC.ExportImport" + button="BMC.ExportImport_button" + /> + <div> + <span + class="regular-12px underline" + @click="redirectNetworkParametrs" + >{{ $t('BMC.Parametrs') }}</span + > + </div> + </div> + + <div class="bmc-control__table__cell"> + <div> + <span class="semi-bold-12px">{{ $t('BMC.microcode') }}</span> + </div> + <b-button variant="unstyled" class="p-0" @click="redirectUpdateBmc"> + <img src="@/assets/images/icon-reload-red.svg" /> + <span style="margin-left: 5px" class="regular-12px red">{{ + $t('BMC.ReloadMicrocodeBios') + }}</span> + </b-button> + </div> + </div> + </page-section> +</template> + +<script> +import SettingsImportPopup from './SettingsImportPopup'; +import PageSection from '@/components/Global/PageSection'; +import Popover from '@/components/Global/Popover'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +export default { + components: { + PageSection, + Popover, + SettingsImportPopup, + }, + mixins: [BVToastMixin], + 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: { + redirectNetworkParametrs() { + this.$router.push('/network-parametrs'); + }, + redirectUpdateBmc() { + this.$router.push('/operations/firmware'); + }, + rebootBmc() { + this.$store + .dispatch('controls/rebootBmc') + .then((message) => this.successToast(message)) + .catch(({ message }) => this.errorToast(message)); + }, + }, +}; +</script> +<style lang="scss" scoped> +a { + list-style-type: none; +} + +.bmc-control-section { + position: relative; + margin: 16px 2rem 2rem; + width: 70%; +} + +.bmc-control__table { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + width: 85%; +} + +.bmc-control__table__cell { + display: flex; + flex-flow: column nowrap; + align-items: flex-start; + row-gap: 6px; +} + +.semi-bold-12px { + display: inline-block; +} + +label { + padding-top: 5px; +} +</style> diff --git a/src/views/_sila/BMC/Configuration/BMCConfigurationTable.vue b/src/views/_sila/BMC/Configuration/BMCConfigurationTable.vue new file mode 100644 index 00000000..f7b6d951 --- /dev/null +++ b/src/views/_sila/BMC/Configuration/BMCConfigurationTable.vue @@ -0,0 +1,91 @@ +<template> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :fields="fields" + :busy="isBusy" + :empty-text="$t('global.table.emptyMessage')" + > + </b-table> + </page-section> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import PageSection from '@/components/Global/PageSection'; + +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { PageSection }, + mixins: [BVToastMixin, TableRowExpandMixin], + data() { + return { + isBusy: true, + isAddersСolon: false, + fields: [ + { + key: 'param', + label: 'Параметр', + formatter: this.dataFormatter, + thStyle: { width: '50%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + expandRowLabel: expandRowLabel, + items: null, + }; + }, + computed: { + bmc() { + return this.$store.getters['bmc/bmc']; + }, + }, + watch: { + bmc() { + this.items = [ + { + param: 'Время сервера', + value: this.bmc.dateTime, + }, + { + param: 'uuid', + value: this.bmc.uuid, + }, + { + param: 'Версия прошивки', + value: this.bmc.firmwareVersion, + }, + { + param: 'Модель', + value: this.bmc.model, + }, + { + param: 'Описание', + value: this.bmc.description, + }, + { + param: 'Максимальное количество сессий', + value: this.bmc.graphicalConsoleMaxSessions, + }, + ]; + }, + }, + created() { + this.$store.dispatch('bmc/getBmcInfo').finally(() => { + this.$root.$emit('hardware-status-bmc-manager-complete'); + this.isBusy = false; + }); + }, +}; +</script> diff --git a/src/views/_sila/BMC/Configuration/SettingsImportPopup.vue b/src/views/_sila/BMC/Configuration/SettingsImportPopup.vue new file mode 100644 index 00000000..3807a694 --- /dev/null +++ b/src/views/_sila/BMC/Configuration/SettingsImportPopup.vue @@ -0,0 +1,291 @@ +<template> + <div id="my-container"> + <span + :id="id" + ref="button" + class="regular-12px underline" + variant="primary" + > + {{ $t(description) }} + </span> + <!-- Our popover title and content render container --> + <b-popover + id="export-popup" + ref="popover" + :target="id" + triggers="click" + :show.sync="popoverShow" + placement="auto" + container="my-container" + @show="onShow" + @shown="onShown" + @hidden="onHidden" + > + <template #title> + <div class="popup-title"> + <span class="semi-bold-20px">{{ $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-switch"> + <span + class="medium-16px popup-item" + :class="{ 'switch-active': isExport }" + @click="switchExport" + >{{ $t('global.action.export') }}</span + > + <span + class="medium-16px popup-item" + :class="{ 'switch-active': !isExport }" + @click="switchImport" + >{{ $t('global.action.import') }}</span + > + <div class="slider" /> + </div> + <div v-if="isExport" class="popup-body"> + <div class="ip-container"> + <span class="regular-16px" + >Оставить IP адрес из настроек сервера</span + > + <b-form-checkbox switch> </b-form-checkbox> + </div> + <b-button class="popover-button" variant="primary" @click="onClose"> + {{ $t(button) }} + </b-button> + </div> + <div v-else class="settings-import_container"> + <b-form-file + id="settings-import__file-input" + placeholder="Нажмите на область или перетащите в нее файл с настройками" + ></b-form-file> + </div> + </b-popover> + </div> +</template> + +<script> +export default { + props: { + description: { + type: String, + default: '', + }, + id: { + type: String, + default: '', + }, + button: { + type: String, + default: 'global.action.reload', + }, + popup: { + type: String, + default: '', + }, + }, + data() { + return { + input1: '', + input1state: null, + input1Return: '', + popoverShow: false, + isExport: true, + }; + }, + watch: { + input1(val) { + if (val) { + this.input1state = true; + } + }, + }, + methods: { + onClose() { + this.popoverShow = false; + }, + onOk() { + if (!this.input1) { + this.input1state = false; + } + if (this.input1) { + this.onClose(); + // Return our popover form results + this.input1Return = this.input1; + } + }, + onShow() { + this.$root.$emit('bv::hide::popover'); + // This is called just before the popover is shown + // Reset our popover form variables + this.input1 = ''; + this.input1state = null; + this.input1Return = ''; + }, + 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(); + }); + }); + }, + switchExport() { + this.isExport = true; + }, + switchImport() { + this.isExport = false; + }, + }, +}; +</script> +<style lang="scss"> +.custom-file { + width: 432px; + height: 108px; +} + +#settings-import__file-input ~ .custom-file-label { + background-color: transparent; + border: 1px dashed rgba(12, 28, 41, 0.6); + box-sizing: border-box; + border-radius: 8px; + width: 432px; + height: 108px; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + white-space: normal; + padding: 0 65px; +} + +#settings-import__file-input ~ .custom-file-label::after { + display: none; +} +</style> +<style lang="scss" scoped> +#export-popup { + flex-direction: column; + align-items: flex-start; + background: #ffffff; + box-shadow: 0px -4px 12px rgb(0 0 0 / 5%); + border-radius: 16px !important; + border-radius: 4px; + max-width: 480px; + width: 480px; + height: auto; +} + +.form-group { + margin: 0; +} + +.popup-title { + display: flex; + align-items: baseline; + width: 465px; +} + +.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; +} + +.medium-16px { + display: inline-block; + height: 45px; + margin: 10px; + cursor: pointer; +} + +.popup-switch { + position: relative; + display: flex; + flex-flow: row nowrap; + border-bottom: 1px solid #f3f4f5; +} + +.switch-active { + color: $red-brand-primary; + transition: ease-in 0.15s; +} + +.slider { + position: absolute; + width: 130px; + height: 0px; + border-bottom: 4px solid $red-brand-primary; + transition: ease-in 0.2s; + bottom: 14px; + left: 10px; +} + +.popup-item:nth-child(1).switch-active ~ .slider { + left: 10px; +} + +.popup-item:nth-child(2).switch-active ~ .slider { + left: 160px; +} + +.ip-container { + display: flex; + width: 461px; + height: 75px; + padding: 30px 15px 25px 15px; +} + +.popover-button { + width: 432px; + height: 52px; + margin: 0 auto 10px; +} + +.settings-import_container { + width: 478px; + height: 160px; + background-color: $surface-secondary; + margin: -15px -15px -8px -12px; + border-radius: 0 0 16px 16px; + + display: flex; + align-items: center; + justify-content: center; +} +</style> diff --git a/src/views/_sila/BMC/Configuration/index.js b/src/views/_sila/BMC/Configuration/index.js new file mode 100644 index 00000000..da796489 --- /dev/null +++ b/src/views/_sila/BMC/Configuration/index.js @@ -0,0 +1,2 @@ +import BMCConfiguration from './BMCConfiguration.vue'; +export default BMCConfiguration; diff --git a/src/views/_sila/BMC/Firmware/FirmwarePage.vue b/src/views/_sila/BMC/Firmware/FirmwarePage.vue new file mode 100644 index 00000000..8ffd9db1 --- /dev/null +++ b/src/views/_sila/BMC/Firmware/FirmwarePage.vue @@ -0,0 +1,273 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.deviceFirmware')" /> + <!-- BMC table --> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + sort-by="id" + class="table-firmware" + no-border-collapse + sort-icon-left + no-sort-reset + :sort-desc="sortDesc" + :items="items" + :fields="fields" + > + <template #cell(name)="{ value }"> + <span class="regular-12px tretiatry"> + {{ value }} + </span> + </template> + <template #cell(minSpeedDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #cell(maxSpeedDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + </b-table> + </page-section> + </b-container> +</template> + +<script> +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import TableFilterMixin from '@/components/Mixins/TableFilterMixin'; +import BVPaginationMixin from '@/components/Mixins/BVPaginationMixin'; +import BVTableSelectableMixin from '@/components/Mixins/BVTableSelectableMixin'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import TableRowExpandMixin from '@/components/Mixins/TableRowExpandMixin'; + +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +export default { + components: { + PageTitle, + PageSection, + }, + mixins: [ + BVPaginationMixin, + BVTableSelectableMixin, + BVToastMixin, + LoadingBarMixin, + TableFilterMixin, + DataFormatterMixin, + TableSortMixin, + TableRowExpandMixin, + ], + data() { + return { + text: '', + iconChevronUp: iconChevronUp, + sortDesc: true, + fields: [ + { + key: 'name', + label: 'Устройство', + sortable: true, + formatter: this.dataFormatter, + thStyle: { paddingLeft: 'calc(1.2rem + 0.65em) !important' }, + }, + { + key: 'version', + label: 'Версия прошивки', + sortable: false, + formatter: this.dataFormatter, + }, + { + key: 'date', + label: 'Дата прошивки', + sortable: true, + formatter: this.dataFormatter, + thStyle: { paddingLeft: '12px' }, + }, + ], + items: [ + { + name: 'Встроенный контроллер Порт 1|Бокс 1|Вау 1', + version: 'HGP1', + date: '03.11.2021', + }, + { + name: 'Встроенный девайс', + version: '2.5', + date: '03.11.2021', + }, + { + name: 'Встроенный ALOM', + version: '10.52.7', + date: '03.11.2021', + }, + { + name: 'Встроенный RAID', + version: '3.00', + date: '03.11.2021', + }, + { + name: 'Системная плата', + version: '2.30 Aug 24 2020', + date: '03.11.2021', + }, + + { + name: 'Системная плата', + version: '11.0.0 Build 23', + date: '03.11.2021', + }, + { + name: 'Системная плата', + version: '3.46.4', + date: '03.11.2021', + }, + { + name: 'Системная плата', + version: '1.0.7', + date: '03.11.2021', + }, + { + name: 'Встроенный контроллер Порт 1|Бокс 1|Вау 1', + version: 'HPG0.9', + date: '02.11.2021', + }, + { + name: 'Встроенный девайс', + version: '2.5', + date: '02.11.2021', + }, + { + name: 'Встроенный ALOM', + version: '10.52.7', + date: '02.11.2021', + }, + { + name: 'Встроенный RAID', + version: '3.00', + date: '02.11.2021', + }, + { + name: 'Системная плата', + version: '2.30 Aug 24 2020', + date: '02.11.2021', + }, + { + name: 'Системная плата', + version: '11.0.0 Build 23', + date: '02.11.2021', + }, + { + name: 'Системная плата', + version: '3.46.4', + date: '02.11.2021', + }, + { + name: 'Системная плата', + version: '1.0.7', + date: '02.11.2021', + }, + { + name: 'Встроенный контроллер Порт 1|Бокс 1|Вау 1', + version: 'HPG0.8', + date: '01.11.2021', + }, + { + name: 'Встроенный девайс', + version: '2.5', + date: '01.11.2021', + }, + { + name: 'Встроенный ALOM', + version: '10.52.7', + date: '01.11.2021', + }, + { + name: 'Встроенный RAID', + version: '3.00', + date: '01.11.2021', + }, + { + name: 'Системная плата', + version: '2.30 Aug 24 2020', + date: '01.11.2021', + }, + + { + name: 'Системная плата', + version: '11.0.0 Build 23', + date: '01.11.2021', + }, + { + name: 'Системная плата', + version: '3.46.4', + date: '01.11.2021', + }, + { + name: 'Системная плата', + version: '1.0.7', + date: '01.11.2021', + }, + ], + }; + }, +}; +</script> +<style lang="scss" scoped> +//nav items style +.nav-item, +.nav-link { + padding: 0; +} + +.nav-item { + list-style-type: none; +} + +.semi-bold-12px { + display: inline-block; + padding: 16px 0 0 2rem; +} + +.bmc-configuration__bios-version { + display: inline-block; + color: #0c1c29; + padding: 8px 2px 2rem 2rem; +} + +a { + color: $text-primary !important; + &:hover { + color: $text-primary !important; + } +} + +.fans-colors { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 2px; +} +.row { + align-items: center; + flex-wrap: nowrap; + justify-content: flex-end; +} +.medium-12px { + color: $text-primary !important; +} +</style> diff --git a/src/views/_sila/BMC/Firmware/index.js b/src/views/_sila/BMC/Firmware/index.js new file mode 100644 index 00000000..55a8c296 --- /dev/null +++ b/src/views/_sila/BMC/Firmware/index.js @@ -0,0 +1,2 @@ +import FirmwarePage from './FirmwarePage.vue'; +export default FirmwarePage; diff --git a/src/views/_sila/BMC/Settings/SettingsPage.vue b/src/views/_sila/BMC/Settings/SettingsPage.vue new file mode 100644 index 00000000..97b0ce9d --- /dev/null +++ b/src/views/_sila/BMC/Settings/SettingsPage.vue @@ -0,0 +1,439 @@ +<template> + <b-container + id="page-bmc-settings" + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.broadcastSettings')" /> + <!-- BMC table --> + <div class="bmc-settings-section"> + <div class="smnp-settings-container"> + <span class="bold-12px__caps section-label">{{ $t('BMC.smnp') }}</span> + <b-form-group class="form-group"> + <label class="regular-12px tretiatry">{{ $t('BMC.system') }}</label> + <b-form-input + v-model="system" + type="text" + class="form-control medium-16px" + > + </b-form-input> + <img class="clear-icon" src="@/assets/images/_sila/popups/x-icon.svg" /> + </b-form-group> + <b-form-group class="form-group"> + <label class="regular-12px tretiatry">{{ $t('BMC.contacts') }}</label> + <b-form-input + type="text" + class="form-control medium-16px" + placeholder="Введите значение" + > + </b-form-input> + </b-form-group> + <b-form-group class="form-group"> + <label class="regular-12px tretiatry">{{ + $t('BMC.community') + }}</label> + <b-form-input + v-model="community" + type="text" + class="form-control medium-16px" + > + </b-form-input> + <img class="clear-icon" src="@/assets/images/_sila/popups/x-icon.svg" /> + </b-form-group> + <b-form-group class="form-group"> + <label class="regular-12px tretiatry">{{ + $t('global.status.status') + }}</label> + <b-form-select + v-model="smnpStatus" + :options="smnpStatuses" + class="select-connection medium-16px" + /> + </b-form-group> + <b-form-group class="form-group"> + <label class="regular-12px tretiatry">{{ $t('BMC.smnpIp') }}</label> + <b-form-input + v-model="smnpIp" + type="text" + class="form-control medium-16px" + > + </b-form-input> + <img class="clear-icon" src="@/assets/images/_sila/popups/x-icon.svg" /> + </b-form-group> + <b-form-group class="form-group"> + <label class="regular-12px tretiatry">{{ $t('BMC.smnpPort') }}</label> + <b-form-input + v-model="smnpPort" + type="text" + class="form-control medium-16px" + > + </b-form-input> + <img class="clear-icon" src="@/assets/images/_sila/popups/x-icon.svg" /> + </b-form-group> + <div class="accept-container"> + <b-button variant="primary" class="accept-button"> + {{ $t('global.action.acceptChanges') }} + </b-button> + </div> + </div> + </div> + <div class="bmc-settings-section"> + <div class="smnp-settings-container"> + <span class="bold-12px__caps section-label">{{ + $t('BMC.smnpWarning') + }}</span> + <div class="table-section"> + <b-table + responsive="md" + show-empty + no-border-collapse + :items="items" + :fields="fields" + > + <template #cell(value)="{ index }"> + <b-row> + <b-col> + <span v-if="items[index].value"> + {{ $t('global.status.enabled') }} + </span> + <span v-else> + {{ $t('global.status.disabled') }} + </span> + </b-col> + <b-col> + <b-form-checkbox v-model="items[index].value" switch> + </b-form-checkbox> + </b-col> + </b-row> + </template> + </b-table> + </div> + <div class="accept-container"> + <b-button variant="secondary" class="test-message-button"> + {{ $t('BMC.testMessage') }} + </b-button> + <b-button variant="primary" class="accept-button"> + {{ $t('global.action.acceptChanges') }} + </b-button> + </div> + </div> + </div> + + <div class="bmc-settings-section"> + <div class="smnp-settings-container"> + <span class="bold-12px__caps section-label">{{ + $t('BMC.smtpWarning') + }}</span> + <div class="table-section"> + <b-table + responsive="md" + show-empty + no-border-collapse + :items="items2" + :fields="fields2" + > + <template #cell(value)="data"> + <b-row v-if="data.index === 0"> + <b-col> + <span v-if="items2[data.index].value"> + {{ $t('global.status.enabled') }} + </span> + <span v-else> + {{ $t('global.status.disabled') }} + </span> + </b-col> + <b-col> + <b-form-checkbox v-model="items2[data.index].value" switch> + </b-form-checkbox> + </b-col> + </b-row> + <b-row v-else-if="data.index === 4"> + <b-col> + <span> + {{ data.value ? 'Да' : 'Нет' }} + </span> + </b-col> + <b-col> + <b-button + :id="`popover-choice-${data.index}`" + class="popover-option-ractive" + variant="toogle-popover" + > + <img :is="iconChevron" class="icon-chevron" /> + </b-button> + <two-chioce-popover + :id="data.index" + fitst-option="Да" + second-option="Нет" + :chosen-option="chosenOption" + :first-action="setYes" + :second-action="setNo" + placement="leftbottom" + /> + </b-col> + </b-row> + <b-row v-else> + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + <b-col> + <img + src="@/assets/images/icon-edit.svg" + class="icon-chevron icon-edit" + /> + </b-col> + </b-row> + </template> + </b-table> + </div> + </div> + </div> + + <div class="bmc-settings-section last"> + <div class="smnp-settings-container"> + <span class="bold-12px__caps section-label">{{ + $t('BMC.syslogSettings') + }}</span> + <b-form-group class="form-group"> + <label class="regular-12px tretiatry">{{ + $t('global.status.status') + }}</label> + <b-form-select + v-model="syslogStatus" + :options="syslogStatuses" + class="select-connection medium-16px" + /> + </b-form-group> + <b-form-group class="form-group"> + <label class="regular-12px tretiatry">{{ $t('BMC.syslogIP') }}</label> + <b-form-input + v-model="syslogIp" + type="text" + class="form-control medium-16px" + > + </b-form-input> + <img class="clear-icon" src="@/assets/images/_sila/popups/x-icon.svg" /> + </b-form-group> + <b-form-group class="form-group"> + <label class="regular-12px tretiatry">{{ + $t('BMC.syslogPort') + }}</label> + <b-form-input + v-model="syslogPort" + type="text" + class="form-control medium-16px" + > + </b-form-input> + <img class="clear-icon" src="@/assets/images/_sila/popups/x-icon.svg" /> + </b-form-group> + <div class="accept-container"> + <b-button variant="primary" class="accept-button"> + {{ $t('global.action.acceptChanges') }} + </b-button> + </div> + </div> + </div> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import TwoChiocePopover from '@/components/Global/SilaComponents/TwoChiocePopover'; +import iconChevron from '@carbon/icons-vue/es/chevron--down/16'; + +export default { + components: { + PageTitle, + TwoChiocePopover, + }, + data() { + return { + iconChevron, + text: '', + system: '2КА04.02_г17', + community: 'public', + smnpIp: '0.0.0.0', + smnpPort: '161', + syslogIp: '0.0.0.0', + syslogPort: '161', + chosenOption: 'Да', + smnpStatus: true, + smnpStatuses: [ + { + value: true, + text: 'Включена', + }, + { + value: false, + text: 'Выключена', + }, + ], + syslogStatus: true, + syslogStatuses: [ + { + value: true, + text: 'Включена', + }, + { + value: false, + text: 'Выключена', + }, + ], + fields: [ + { + key: 'name', + label: 'Тип предупреждения', + formatter: this.dataFormatter, + thStyle: { width: '70%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + fields2: [ + { + key: 'attributes', + label: 'Атрибуты', + formatter: this.dataFormatter, + thStyle: { width: '70%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + items: [ + { + name: 'SNMPv1', + value: true, + }, + { + name: 'SNMPv3', + value: true, + }, + { + name: 'Постоянные запросы и предупреждения SNMPv1', + value: true, + }, + ], + items2: [ + { + attributes: 'Авторизация', + value: true, + }, + { + attributes: 'Логин пользователя', + value: 'admin', + }, + { + attributes: 'Пароль', + value: '******', + }, + { + attributes: 'SMTP-сервер', + value: 'smtp.domian.ru', + }, + { + attributes: 'Поддержка SSI', + value: true, + }, + { + attributes: 'SMTP-порт', + value: 465, + }, + ], + }; + }, + methods: { + setYes() { + this.chosenOption = 'Да'; + this.items2[4].value = true; + }, + setNo() { + this.chosenOption = 'Нет'; + this.items2[4].value = false; + }, + }, +}; +</script> +<style lang="scss" scoped> +.main-container { + margin-top: 16px; +} + +.bmc-settings-section { + border-bottom: 1px solid $faint-secondary-primary-10; + &.last { + margin-bottom: 40px; + } +} +.smnp-settings-container { + display: flex; + flex-flow: row wrap; + gap: 16px; + justify-content: flex-start; + align-items: flex-start; + margin: 16px 0 16px 28px; +} + +.section-label { + display: block; + width: 100%; +} +.form-group { + width: 341px; + height: 35px; +} +.form-control { + margin: -31px 0px 0 -15px; + height: 52px; + width: 341px; + padding: 17px 0 0 15px; +} +.clear-icon { + margin: -80px 0px 0px 300px; +} + +.select-connection { + height: 52px; + width: 341px; + padding-top: 30px; + border: none; + border-radius: 8px; + margin: -31px 0 18px -15px; + background-color: $faint-secondary-primary-5; + background-image: url('../../../../assets/images/_sila/icon-chevron.svg'); +} +.accept-container { + width: 100%; + display: flex; + justify-content: flex-end; +} + +.accept-button { + width: 245px; + height: 36px; + margin-right: 33px; +} +.test-message-button { + width: 245px; + height: 36px; + margin-right: 16px; + background-color: $faint-secondary-primary-5; +} + +.table-section { + width: 100%; + margin: 0 20px 0 0; +} + +.popover-option-ractive { + display: block; + margin: -6px 6px 0 auto; +} +</style> diff --git a/src/views/_sila/BMC/Settings/index.js b/src/views/_sila/BMC/Settings/index.js new file mode 100644 index 00000000..f74e1f4d --- /dev/null +++ b/src/views/_sila/BMC/Settings/index.js @@ -0,0 +1,2 @@ +import SettingsPage from './SettingsPage.vue'; +export default SettingsPage; diff --git a/src/views/_sila/Fans/DynamicInformation/FansDynamicPage.vue b/src/views/_sila/Fans/DynamicInformation/FansDynamicPage.vue new file mode 100644 index 00000000..fe997c58 --- /dev/null +++ b/src/views/_sila/Fans/DynamicInformation/FansDynamicPage.vue @@ -0,0 +1,118 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('SystemDescription.FansDynamicInformation')" /> + <date-switch :switch-time-scale="switchTimeScale" :time-scale="timeScale" /> + <div class="speed-description"> + <img src="@/assets/images/fans-page/fans-icon.svg" /> + <span class="bold-16px">{{ $t('fansPage.speedDescription') }}</span> + </div> + <div class="limit-speed-container"> + <div class="speed-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ $t('fansPage.speedWarhihg') }}</span> + <b-form-input + v-model="fanSpeedWarninigInput" + type="number" + :min="0" + :max="fanSpeedShutdownInput" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="speed-limt"> + <img src="@/assets/images/labels/shutdown.svg" /> + <span class="semi-bold-12px">{{ $t('fansPage.speedShutdown') }}</span> + <b-form-input + v-model="fanSpeedShutdownInput" + :min="fanSpeedWarninigInput" + :max="4000" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button class="save-button" variant="primary" @click="updateFansSpeed"> + {{ $t('global.action.save') }} + </b-button> + </div> + + <fans-dynamic-table + :speed-warninig="fanSpeedWarninig" + :speed-shutdown="fanSpeedShutdown" + :time-scale="timeScale" + /> + <indicators-table /> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import DateSwitch from '@/components/Global/SilaComponents/DateSwitch'; +import FansDynamicTable from './FansDynamicTable'; +import IndicatorsTable from './IndicatorsTable'; + +export default { + components: { PageTitle, DateSwitch, FansDynamicTable, IndicatorsTable }, + data() { + return { + timeScale: 'hour', + fanSpeedWarninigInput: 2450, + fanSpeedShutdownInput: 3150, + fanSpeedWarninig: 2450, + fanSpeedShutdown: 3150, + }; + }, + methods: { + switchTimeScale(period) { + this.timeScale = period; + }, + updateFansSpeed() { + this.fanSpeedWarninig = +this.fanSpeedWarninigInput; + this.fanSpeedShutdown = +this.fanSpeedShutdownInput; + }, + }, +}; +</script> +<style lang="scss" scoped> +.speed-description { + height: 56px; + padding-left: 36px; + display: flex; + align-items: center; + gap: 8px; + border-bottom: 1px solid $faint-secondary-primary-10; +} + +.limit-speed-container { + height: 85px; + padding: 0 0 10px 32px; + display: flex; + align-items: flex-end; + gap: 24px; +} + +.speed-limt { + height: 60px; + max-width: 312px; + display: flex; + align-items: baseline; + flex-flow: row wrap; + gap: 8px; +} + +.save-button { + width: 151px; + height: 36px; +} + +.form-control { + height: 36px; +} + +.main-container { + overflow: auto; +} +</style> diff --git a/src/views/_sila/Fans/DynamicInformation/FansDynamicTable.vue b/src/views/_sila/Fans/DynamicInformation/FansDynamicTable.vue new file mode 100644 index 00000000..b0818255 --- /dev/null +++ b/src/views/_sila/Fans/DynamicInformation/FansDynamicTable.vue @@ -0,0 +1,126 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setSpeed } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + speedWarninig: { + type: Number, + default: 2450, + }, + speedShutdown: { + type: Number, + default: 3150, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, this.timeScale), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setSpeed(4000), + min: 0, + title: null, + minRange: 4000, + minTickInterval: 1000, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.speedWarninig, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значения предупреждение', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + { + color: '#1A3E5B', + dashStyle: 'solid', + value: this.speedShutdown, + width: 2, + label: { + text: 'Пороговое значения отказ', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + }, + series: Series[this.timeScale].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/Fans/DynamicInformation/IndicatorsTable.vue b/src/views/_sila/Fans/DynamicInformation/IndicatorsTable.vue new file mode 100644 index 00000000..4da9a556 --- /dev/null +++ b/src/views/_sila/Fans/DynamicInformation/IndicatorsTable.vue @@ -0,0 +1,167 @@ +<template> + <page-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="filteredSensors" + :busy="isBusy" + :fields="fields" + > + <template #cell(name)="{ value, index }"> + <div + class="fans-colors" + :style="`background-color: ${colors[index]}`" + ></div> + <span class="regular-12px tretiatry"> + {{ value }} + </span> + </template> + <template #cell(middleSpeed)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #cell(minSpeedDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + <template #cell(maxSpeedDate)="{ value }"> + <span class="regular-12px"> + {{ value.time }} + </span> + <span class="regular-12px tretiatry"> + {{ value.date }} + </span> + </template> + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import { colors } from './helpers'; + +import TableFilterMixin from '@/components/Mixins/TableFilterMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; + +export default { + components: { PageSection }, + mixins: [TableFilterMixin, DataFormatterMixin, LoadingBarMixin], + data() { + return { + isBusy: true, + colors, + fields: [ + { + key: 'name', + label: 'Имя модуля', + formatter: this.dataFormatter, + }, + { + key: 'currentValue', + label: 'Текущая', + formatter: this.dataFormatter, + }, + { + key: 'middleSpeed', + label: 'Средняя', + formatter: this.dataFormatter, + }, + { + key: 'lowerCaution', + label: 'Минимальная', + formatter: this.dataFormatter, + }, + { + key: 'minSpeedDate', + label: 'Дата минимальной', + formatter: this.dataFormatter, + }, + { + key: 'upperCaution', + label: 'Максимальная', + formatter: this.dataFormatter, + }, + { + key: 'maxSpeedDate', + label: 'Дата максимальной', + formatter: this.dataFormatter, + }, + ], + }; + }, + + computed: { + allSensors() { + let sensors = this.$store.getters['sensors/fanSensors']; + if (this.isSensorsExist) { + sensors.forEach((sensor) => { + sensor.type = sensor.name.toLowerCase().includes('cpu') + ? this.$t('tablesDescription.cpu') + : this.$t('tablesDescription.system'); + }); + } + return sensors; + }, + + isSensorsExist() { + return ( + this.$store.getters['sensors/fanSensors'] && + this.$store.getters['sensors/fanSensors'].length > 0 + ); + }, + + filteredSensors() { + return this.getFilteredTableData(this.allSensors, this.activeFilters); + }, + }, + + watch: { + filteredSensors(value) { + if (value && value.length > 0) { + this.colors = this.$randomColor({ + count: value.length, + hue: 'random', + luminosity: 'random', + }); + } + }, + }, + + created() { + this.startLoader(); + this.$store.dispatch('sensors/getFanSensors').finally(() => { + this.endLoader(); + this.isBusy = false; + }); + }, +}; +</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; +} +.medium-12px { + color: $text-primary !important; +} +</style> diff --git a/src/views/_sila/Fans/DynamicInformation/helpers.js b/src/views/_sila/Fans/DynamicInformation/helpers.js new file mode 100644 index 00000000..1268d34a --- /dev/null +++ b/src/views/_sila/Fans/DynamicInformation/helpers.js @@ -0,0 +1,820 @@ +export const colors = [ + '#CB32F1', + '#F18638', + '#139BB9', + '#E1AB17', + '#175AE1', + '#13B937', +]; + +export const Series = { + hour: [ + { + name: 'Sean', + data: [ + 526, + 526, + 526, + 526, + 526, + 1100, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 2100, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 1526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 1526, + 526, + 526, + 526, + 526, + 526, + 526, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 315, + 315, + 315, + 315, + 315, + 315, + 1315, + 315, + 315, + 315, + 315, + 315, + 315, + 2200, + 315, + 315, + 315, + 315, + 315, + 315, + 1100, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 1600, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 1400, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 1359, + 359, + 359, + 359, + 359, + 2000, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 2100, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 1400, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 1350, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 1590, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 1490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 1487, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 794, + 467, + 467, + 467, + 467, + 467, + 1924, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 1924, + 467, + 467, + 794, + 467, + 467, + 467, + 467, + 467, + 1924, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 794, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 410, + 410, + 410, + 410, + 1300, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 2110, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 1410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 1410, + 410, + 410, + 410, + 410, + 410, + 410, + ], + color: '#139BB9', + }, + ], + day: [ + { + name: 'Sean', + data: [ + 526, + 526, + 526, + 526, + 626, + 626, + 626, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 726, + 1026, + 726, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 526, + 1326, + 1526, + 1326, + 526, + 526, + 526, + 526, + 526, + 526, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 315, + 315, + 315, + 315, + 315, + 315, + 815, + 315, + 315, + 315, + 315, + 315, + 315, + 1100, + 315, + 315, + 315, + 315, + 315, + 315, + 800, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 900, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 900, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + 315, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 1500, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 1500, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 500, + 1200, + 500, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + 359, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 950, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 890, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 990, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + 490, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 1087, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 794, + 467, + 467, + 467, + 467, + 467, + 1424, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 1424, + 467, + 467, + 794, + 467, + 467, + 467, + 467, + 467, + 1224, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + 794, + 467, + 467, + 467, + 467, + 467, + 467, + 467, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 410, + 1410, + 410, + 410, + 1300, + 410, + 410, + 410, + 410, + 1410, + 410, + 410, + 410, + 410, + 410, + 2110, + 410, + 410, + 1410, + 410, + 410, + 410, + 410, + 1410, + 410, + 410, + 410, + 410, + 410, + 410, + 410, + 1410, + 410, + 410, + 410, + 410, + 1410, + 410, + 410, + 410, + 410, + 410, + 410, + 1410, + 410, + 410, + 410, + 410, + 1410, + 410, + 410, + 410, + 410, + 1410, + 410, + 410, + 410, + 1410, + 410, + 410, + ], + color: '#139BB9', + }, + ], +}; + +export const setTime = (count) => { + const arr = [...new Array(count)].map(() => ''); + for (let i = 0; i < arr.length; i++) { + arr[i] = `15:${String(i).padStart(2, '0')}`; + } + return arr; +}; + +export const setSpeed = (count) => { + const arr = [...new Array(count)].map((i, k) => `${k}`); + return arr; +}; diff --git a/src/views/_sila/Fans/DynamicInformation/index.js b/src/views/_sila/Fans/DynamicInformation/index.js new file mode 100644 index 00000000..a3dadd5a --- /dev/null +++ b/src/views/_sila/Fans/DynamicInformation/index.js @@ -0,0 +1,2 @@ +import FansDynamicPage from './FansDynamicPage.vue'; +export default FansDynamicPage; diff --git a/src/views/_sila/Fans/StaticInformation/FansStaticPage.vue b/src/views/_sila/Fans/StaticInformation/FansStaticPage.vue new file mode 100644 index 00000000..b661bfdf --- /dev/null +++ b/src/views/_sila/Fans/StaticInformation/FansStaticPage.vue @@ -0,0 +1,152 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.specification')" /> + <span class="bold-16px">{{ $t('tablesDescription.installedFans') }}</span> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :busy="isBusy" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(status)="{ value }"> + <div v-if="value" class="fans-table-col-first__cell"> + <img class="status__img" src="@/assets/images/status/on.svg" /> + <span> + {{ $t('global.status.inWork') }} + </span> + </div> + <div v-else class="fans-table-col-first__cell"> + <img class="status__img" src="@/assets/images/_sila/status/off.svg" /> + <span> + {{ $t('global.status.outWorking') }} + </span> + </div> + </template> + <template #cell(currentValue)="data"> + {{ data.value }} + </template> + </b-table> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; + +import TableFilterMixin from '@/components/Mixins/TableFilterMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; + +export default { + components: { PageTitle, PageSection }, + mixins: [TableFilterMixin, DataFormatterMixin, LoadingBarMixin], + data() { + return { + isBusy: true, + isAddersСolon: false, + fields: [ + { + key: 'status', + label: 'Статус', + formatter: this.dataFormatter, + thStyle: { width: '25%' }, + }, + { + key: 'name', + label: 'Имя', + formatter: this.dataFormatter, + thStyle: { width: '25%' }, + }, + { + key: 'type', + label: 'Тип', + formatter: this.dataFormatter, + thStyle: { width: '25%' }, + }, + { + key: 'currentValue', + label: 'Текущая скорость, об/мин', + formatter: this.dataFormatter, + thStyle: { width: '25%' }, + }, + ], + items: [ + { + status: true, + name: 'Венититор 1', + type: 'Системная плата', + value: '2100', + }, + { + status: true, + name: 'Венититор 2', + type: 'Системная плата', + value: '2300', + }, + { + status: false, + name: 'Венититор 3', + type: 'Системная плата', + value: '2400', + }, + ], + activeFilters: [], + }; + }, + + computed: { + allSensors() { + let sensors = this.$store.getters['sensors/fanSensors']; + if (this.isSensorsExist) { + sensors.forEach((sensor) => { + sensor.type = sensor.name.toLowerCase().includes('cpu') + ? this.$t('tablesDescription.cpu') + : this.$t('tablesDescription.system'); + }); + } + return sensors; + }, + + isSensorsExist() { + return ( + this.$store.getters['sensors/fanSensors'] && + this.$store.getters['sensors/fanSensors'].length > 0 + ); + }, + + filteredSensors() { + return this.getFilteredTableData(this.allSensors, this.activeFilters); + }, + }, + + created() { + this.startLoader(); + this.$store.dispatch('sensors/getFanSensors').finally(() => { + this.endLoader(); + this.isBusy = false; + }); + }, +}; +</script> +<style lang="scss" scoped> +.row { + margin: 0px; +} + +.status__img { + margin-right: 7px; +} + +.bold-16px { + margin: 24px 0 0 2rem; +} +</style> diff --git a/src/views/_sila/Fans/StaticInformation/index.js b/src/views/_sila/Fans/StaticInformation/index.js new file mode 100644 index 00000000..9a5d913d --- /dev/null +++ b/src/views/_sila/Fans/StaticInformation/index.js @@ -0,0 +1,2 @@ +import FansStaticPage from './FansStaticPage.vue'; +export default FansStaticPage; diff --git a/src/views/_sila/HardwareStatus/Inventory/Inventory.vue b/src/views/_sila/HardwareStatus/Inventory/Inventory.vue new file mode 100644 index 00000000..dac395c5 --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/Inventory.vue @@ -0,0 +1,196 @@ +<template> + <b-container id="page-inventory" fluid class="p-0 m-0"> + <page-title :description="$t('appNavigation.overviewInfo')" /> + <!-- Service indicators --> + <service-indicator /> + + <!-- Quicklinks section --> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.quicklinkTitle')" + > + <b-row> + <b-col v-for="column in quicklinkColumns" :key="column.id"> + <div v-for="item in column" :key="item.id"> + <b-link + :href="item.href" + :data-ref="item.dataRef" + @click.prevent="scrollToOffset" + > + {{ item.linkText }} + </b-link> + </div> + </b-col> + </b-row> + </page-section> + + <!-- System table --> + <table-system ref="system" /> + + <!-- BMC manager table --> + <table-bmc-manager ref="bmc" /> + + <!-- Chassis table --> + <table-chassis ref="chassis" /> + + <!-- DIMM slot table --> + <table-dimm-slot ref="dimms" /> + + <!-- Fans table --> + <table-fans ref="fans" /> + + <!-- Power supplies table --> + <table-power-supplies ref="powerSupply" /> + + <!-- Processors table --> + <table-processors ref="processors" /> + + <!-- Assembly table --> + <table-assembly ref="assembly" /> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import ServiceIndicator from './InventoryServiceIndicator'; +import TableSystem from './InventoryTableSystem'; +import TablePowerSupplies from './InventoryTablePowerSupplies'; +import TableDimmSlot from './InventoryTableDimmSlot'; +import TableFans from './InventoryTableFans'; +import TableBmcManager from './InventoryTableBmcManager'; +import TableChassis from './InventoryTableChassis'; +import TableProcessors from './InventoryTableProcessors'; +import TableAssembly from './InventoryTableAssembly'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import PageSection from '@/components/Global/PageSection'; +import JumpLinkMixin from '@/components/Mixins/JumpLinkMixin'; +import { chunk } from 'lodash'; + +export default { + components: { + PageTitle, + ServiceIndicator, + TableDimmSlot, + TablePowerSupplies, + TableSystem, + TableFans, + TableBmcManager, + TableChassis, + TableProcessors, + TableAssembly, + PageSection, + }, + mixins: [LoadingBarMixin, JumpLinkMixin], + beforeRouteLeave(to, from, next) { + // Hide loader if user navigates away from page + // before requests complete + this.hideLoader(); + next(); + }, + data() { + return { + links: [ + { + id: 'system', + dataRef: 'system', + href: '#system', + linkText: this.$t('pageInventory.system'), + }, + { + id: 'bmc', + dataRef: 'bmc', + href: '#bmc', + linkText: this.$t('pageInventory.bmcManager'), + }, + { + id: 'chassis', + dataRef: 'chassis', + href: '#chassis', + linkText: this.$t('pageInventory.chassis'), + }, + { + id: 'dimms', + dataRef: 'dimms', + href: '#dimms', + linkText: this.$t('pageInventory.dimmSlot'), + }, + { + id: 'fans', + dataRef: 'fans', + href: '#fans', + linkText: this.$t('pageInventory.fans'), + }, + { + id: 'powerSupply', + dataRef: 'powerSupply', + href: '#powerSupply', + linkText: this.$t('pageInventory.powerSupplies'), + }, + { + id: 'processors', + dataRef: 'processors', + href: '#processors', + linkText: this.$t('pageInventory.processors'), + }, + { + id: 'assembly', + dataRef: 'assembly', + href: '#assembly', + linkText: this.$t('pageInventory.assemblies'), + }, + ], + }; + }, + computed: { + quicklinkColumns() { + // Chunk links array to 3 array's to display 3 items per column + return chunk(this.links, 3); + }, + }, + created() { + this.startLoader(); + const bmcManagerTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-bmc-manager-complete', () => resolve()); + }); + const chassisTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-chassis-complete', () => resolve()); + }); + const dimmSlotTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-dimm-slot-complete', () => resolve()); + }); + const fansTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-fans-complete', () => resolve()); + }); + const powerSuppliesTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-power-supplies-complete', () => + resolve() + ); + }); + const processorsTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-processors-complete', () => resolve()); + }); + const serviceIndicatorPromise = new Promise((resolve) => { + this.$root.$on('hardware-status-service-complete', () => resolve()); + }); + const systemTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-system-complete', () => resolve()); + }); + const assemblyTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-assembly-complete', () => resolve()); + }); + // Combine all child component Promises to indicate + // when page data load complete + Promise.all([ + bmcManagerTablePromise, + chassisTablePromise, + dimmSlotTablePromise, + fansTablePromise, + powerSuppliesTablePromise, + processorsTablePromise, + serviceIndicatorPromise, + systemTablePromise, + assemblyTablePromise, + ]).finally(() => this.endLoader()); + }, +}; +</script> diff --git a/src/views/_sila/HardwareStatus/Inventory/InventoryServiceIndicator.vue b/src/views/_sila/HardwareStatus/Inventory/InventoryServiceIndicator.vue new file mode 100644 index 00000000..0589aed8 --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/InventoryServiceIndicator.vue @@ -0,0 +1,82 @@ +<template> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.systemIndicator.sectionTitle')" + > + <div class="form-background"> + <b-row> + <b-col md="4"> + <dl> + <dt>{{ $t('pageInventory.systemIndicator.powerStatus') }}</dt> + <dd> + {{ $t(powerStatus) }} + </dd> + </dl> + </b-col> + <b-col md="6"> + <dl> + <dt> + {{ $t('pageInventory.systemIndicator.identifyLed') }} + </dt> + <dd> + <b-form-checkbox + id="identifyLedSwitchService" + v-model="systems.locationIndicatorActive" + data-test-id="inventoryService-toggle-identifyLed" + switch + @change="toggleIdentifyLedSwitch" + > + <span v-if="systems.locationIndicatorActive"> + {{ $t('global.status.on') }} + </span> + <span v-else>{{ $t('global.status.off') }}</span> + </b-form-checkbox> + </dd> + </dl> + </b-col> + </b-row> + </div> + </page-section> +</template> +<script> +import PageSection from '@/components/Global/PageSection'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; + +export default { + components: { PageSection }, + mixins: [BVToastMixin], + computed: { + systems() { + let systemData = this.$store.getters['system/systems'][0]; + return systemData ? systemData : {}; + }, + serverStatus() { + return this.$store.getters['global/serverStatus']; + }, + powerStatus() { + if (this.serverStatus === 'unreachable') { + return `global.status.off`; + } + return `global.status.${this.serverStatus}`; + }, + }, + created() { + this.$store.dispatch('system/getSystem').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-service-complete'); + }); + }, + methods: { + toggleIdentifyLedSwitch(state) { + this.$store + .dispatch('system/changeIdentifyLedState', state) + .catch(({ message }) => this.errorToast(message)); + }, + }, +}; +</script> +<style lang="scss" scoped> +.custom-switch { + margin: 0; +} +</style> diff --git a/src/views/_sila/HardwareStatus/Inventory/InventoryTableAssembly.vue b/src/views/_sila/HardwareStatus/Inventory/InventoryTableAssembly.vue new file mode 100644 index 00000000..7683ef93 --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/InventoryTableAssembly.vue @@ -0,0 +1,160 @@ +<template> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.assemblies')" + > + <b-table + sort-icon-left + no-sort-reset + no-border-collapse + responsive="md" + :items="items" + :fields="fields" + :fixed="true" + show-empty + :empty-text="$t('global.table.emptyMessage')" + :busy="isBusy" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandAssembly" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Toggle identify LED --> + <template #cell(identifyLed)="row"> + <b-form-checkbox + v-if="hasIdentifyLed(row.item.identifyLed)" + v-model="row.item.identifyLed" + name="switch" + switch + @change="toggleIdentifyLedValue(row.item)" + > + <span v-if="row.item.identifyLed"> + {{ $t('global.status.on') }} + </span> + <span v-else> {{ $t('global.status.off') }} </span> + </b-form-checkbox> + <div v-else>--</div> + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <!-- Nmae --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ dataFormatter(item.name) }}</dd> + <!-- Serial number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ dataFormatter(item.serialNumber) }}</dd> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <!-- Model--> + <dt>Model</dt> + <dd>{{ dataFormatter(item.model) }}</dd> + <!-- Spare Part Number --> + <dt>Spare Part Number</dt> + <dd>{{ dataFormatter(item.sparePartNumber) }}</dd> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; + +export default { + components: { IconChevron, PageSection }, + mixins: [BVToastMixin, TableRowExpandMixin, DataFormatterMixin], + data() { + return { + isBusy: true, + fields: [ + { + key: 'expandRow', + label: '', + thStyle: { width: '4%' }, + }, + { + key: 'name', + label: this.$t('pageInventory.table.id'), + formatter: this.dataFormatter, + sortable: true, + thStyle: { width: '20%' }, + }, + { + key: 'partNumber', + label: this.$t('pageInventory.table.partNumber'), + formatter: this.dataFormatter, + sortable: true, + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.dataFormatter, + sortable: true, + thStyle: { width: '20%' }, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + ], + expandRowLabel: expandRowLabel, + }; + }, + computed: { + assemblies() { + return this.$store.getters['assemblies/assemblies']; + }, + items() { + if (this.assemblies) { + return this.assemblies; + } else { + return []; + } + }, + }, + created() { + this.$store.dispatch('assemblies/getAssemblyInfo').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-assembly-complete'); + this.isBusy = false; + }); + }, + methods: { + toggleIdentifyLedValue(row) { + this.$store + .dispatch('assemblies/updateIdentifyLedValue', { + uri: row.uri, + memberId: row.id, + identifyLed: row.identifyLed, + }) + .catch(({ message }) => this.errorToast(message)); + }, + hasIdentifyLed(identifyLed) { + return typeof identifyLed === 'boolean'; + }, + }, +}; +</script> diff --git a/src/views/_sila/HardwareStatus/Inventory/InventoryTableBmcManager.vue b/src/views/_sila/HardwareStatus/Inventory/InventoryTableBmcManager.vue new file mode 100644 index 00000000..f3cd1f05 --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/InventoryTableBmcManager.vue @@ -0,0 +1,254 @@ +<template> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.bmcManager')" + > + <b-table + responsive="md" + show-empty + no-border-collapse + :items="items" + :fields="fields" + :fixed="true" + :empty-text="$t('global.table.emptyMessage')" + :busy="isBusy" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandBmc" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <!-- Toggle identify LED --> + <template #cell(identifyLed)="row"> + <b-form-checkbox + v-if="hasIdentifyLed(row.item.identifyLed)" + v-model="row.item.identifyLed" + name="switch" + switch + @change="toggleIdentifyLedValue(row.item)" + > + <span v-if="row.item.identifyLed"> + {{ $t('global.status.on') }} + </span> + <span v-else> {{ $t('global.status.off') }} </span> + </b-form-checkbox> + <div v-else>--</div> + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ dataFormatter(item.name) }}</dd> + <!-- Part number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ dataFormatter(item.partNumber) }}</dd> + <!-- Serial number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ dataFormatter(item.serialNumber) }}</dd> + <!-- Spare part number --> + <dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt> + <dd>{{ dataFormatter(item.sparePartNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd>{{ dataFormatter(item.model) }}</dd> + <!-- UUID --> + <dt>{{ $t('pageInventory.table.uuid') }}:</dt> + <dd>{{ dataFormatter(item.uuid) }}</dd> + <!-- Service entry point UUID --> + <dt>{{ $t('pageInventory.table.serviceEntryPointUuid') }}:</dt> + <dd>{{ dataFormatter(item.serviceEntryPointUuid) }}</dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ dataFormatter(item.statusState) }}</dd> + <!-- Power state --> + <dt>{{ $t('pageInventory.table.power') }}:</dt> + <dd>{{ dataFormatter(item.powerState) }}</dd> + <!-- Health rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ dataFormatter(item.healthRollup) }}</dd> + <!-- BMC date and time --> + <dt>{{ $t('pageInventory.table.bmcDateTime') }}:</dt> + <dd> + {{ item.dateTime | formatDate }} + {{ item.dateTime | formatTime }} + </dd> + <!-- Reset date and time --> + <dt>{{ $t('pageInventory.table.lastResetTime') }}:</dt> + <dd> + {{ item.lastResetTime | formatDate }} + {{ item.lastResetTime | formatTime }} + </dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ dataFormatter(item.manufacturer) }}</dd> + <!-- Description --> + <dt>{{ $t('pageInventory.table.description') }}:</dt> + <dd>{{ dataFormatter(item.description) }}</dd> + <!-- Manager type --> + <dt>{{ $t('pageInventory.table.managerType') }}:</dt> + <dd>{{ dataFormatter(item.managerType) }}</dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <!-- Firmware Version --> + <dl> + <dt>{{ $t('pageInventory.table.firmwareVersion') }}:</dt> + <dd>{{ item.firmwareVersion }}</dd> + </dl> + <!-- Graphical console --> + <p class="mt-1 mb-2 h6 float-none m-0"> + {{ $t('pageInventory.table.graphicalConsole') }} + </p> + <dl class="ml-4"> + <dt>{{ $t('pageInventory.table.connectTypesSupported') }}:</dt> + <dd> + {{ dataFormatterArray(item.graphicalConsoleConnectTypes) }} + </dd> + <dt>{{ $t('pageInventory.table.maxConcurrentSessions') }}:</dt> + <dd> + {{ dataFormatter(item.graphicalConsoleMaxSessions) }} + </dd> + <dt>{{ $t('pageInventory.table.serviceEnabled') }}:</dt> + <dd> + {{ dataFormatter(item.graphicalConsoleEnabled) }} + </dd> + </dl> + <!-- Serial console --> + <p class="mt-1 mb-2 h6 float-none m-0"> + {{ $t('pageInventory.table.serialConsole') }} + </p> + <dl class="ml-4"> + <dt>{{ $t('pageInventory.table.connectTypesSupported') }}:</dt> + <dd> + {{ dataFormatterArray(item.serialConsoleConnectTypes) }} + </dd> + <dt>{{ $t('pageInventory.table.maxConcurrentSessions') }}:</dt> + <dd>{{ dataFormatter(item.serialConsoleMaxSessions) }}</dd> + <dt>{{ $t('pageInventory.table.serviceEnabled') }}:</dt> + <dd>{{ dataFormatter(item.serialConsoleEnabled) }}</dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; +import StatusIcon from '@/components/Global/StatusIcon'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon }, + mixins: [BVToastMixin, TableRowExpandMixin, DataFormatterMixin], + data() { + return { + isBusy: true, + fields: [ + { + key: 'expandRow', + label: '', + thStyle: { width: '4%' }, + tdClass: 'table-row-expand ', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + tdClass: 'regular-12px ', + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.dataFormatter, + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + ], + expandRowLabel: expandRowLabel, + }; + }, + computed: { + bmc() { + return this.$store.getters['bmc/bmc']; + }, + items() { + if (this.bmc) { + return [this.bmc]; + } else { + return []; + } + }, + }, + created() { + this.$store.dispatch('bmc/getBmcInfo').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-bmc-manager-complete'); + this.isBusy = false; + }); + }, + methods: { + toggleIdentifyLedValue(row) { + this.$store + .dispatch('bmc/updateIdentifyLedValue', { + uri: row.uri, + identifyLed: row.identifyLed, + }) + .catch(({ message }) => this.errorToast(message)); + }, + // TO DO: remove hasIdentifyLed method once the following story is merged: + // https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/43179 + hasIdentifyLed(identifyLed) { + return typeof identifyLed === 'boolean'; + }, + }, +}; +</script> diff --git a/src/views/_sila/HardwareStatus/Inventory/InventoryTableChassis.vue b/src/views/_sila/HardwareStatus/Inventory/InventoryTableChassis.vue new file mode 100644 index 00000000..ed8787f9 --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/InventoryTableChassis.vue @@ -0,0 +1,199 @@ +<template> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.chassis')" + > + <b-table + responsive="md" + show-empty + no-border-collapse + :items="chassis" + :fields="fields" + :fixed="true" + :empty-text="$t('global.table.emptyMessage')" + :busy="isBusy" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandChassis" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + <!-- Toggle identify LED --> + <template #cell(identifyLed)="row"> + <b-form-checkbox + v-if="hasIdentifyLed(row.item.identifyLed)" + v-model="row.item.identifyLed" + name="switch" + switch + @change="toggleIdentifyLedValue(row.item)" + > + <span v-if="row.item.identifyLed"> + {{ $t('global.status.on') }} + </span> + <span v-else> {{ $t('global.status.off') }} </span> + </b-form-checkbox> + <div v-else>--</div> + </template> + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ dataFormatter(item.name) }}</dd> + <!-- Part number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ dataFormatter(item.partNumber) }}</dd> + <!-- Serial Number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ dataFormatter(item.serialNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd class="mb-2"> + {{ dataFormatter(item.model) }} + </dd> + <!-- Asset tag --> + <dt>{{ $t('pageInventory.table.assetTag') }}:</dt> + <dd class="mb-2"> + {{ dataFormatter(item.assetTag) }} + </dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ dataFormatter(item.statusState) }}</dd> + <!-- Power state --> + <dt>{{ $t('pageInventory.table.power') }}:</dt> + <dd>{{ dataFormatter(item.power) }}</dd> + <!-- Health rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ dataFormatter(item.healthRollup) }}</dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ dataFormatter(item.manufacturer) }}</dd> + <!-- Chassis Type --> + <dt>{{ $t('pageInventory.table.chassisType') }}:</dt> + <dd>{{ dataFormatter(item.chassisType) }}</dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Min power --> + <dt>{{ $t('pageInventory.table.minPowerWatts') }}:</dt> + <dd>{{ dataFormatter(item.minPowerWatts) }}</dd> + <!-- Max power --> + <dt>{{ $t('pageInventory.table.maxPowerWatts') }}:</dt> + <dd>{{ dataFormatter(item.maxPowerWatts) }}</dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import StatusIcon from '@/components/Global/StatusIcon'; + +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon }, + mixins: [BVToastMixin, TableRowExpandMixin, DataFormatterMixin], + data() { + return { + isBusy: true, + fields: [ + { + key: 'expandRow', + label: '', + thStyle: { width: '4%' }, + tdClass: 'table-row-expand', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.dataFormatter, + tdClass: 'text-nowrap', + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + ], + expandRowLabel: expandRowLabel, + }; + }, + computed: { + chassis() { + return this.$store.getters['chassis/chassis']; + }, + }, + created() { + this.$store.dispatch('chassis/getChassisInfo').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-chassis-complete'); + this.isBusy = false; + }); + }, + methods: { + toggleIdentifyLedValue(row) { + this.$store + .dispatch('chassis/updateIdentifyLedValue', { + uri: row.uri, + identifyLed: row.identifyLed, + }) + .catch(({ message }) => this.errorToast(message)); + }, + // TO DO: Remove this method when the LocationIndicatorActive is added from backend. + hasIdentifyLed(identifyLed) { + return typeof identifyLed === 'boolean'; + }, + }, +}; +</script> diff --git a/src/views/_sila/HardwareStatus/Inventory/InventoryTableDimmSlot.vue b/src/views/_sila/HardwareStatus/Inventory/InventoryTableDimmSlot.vue new file mode 100644 index 00000000..fa25919f --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/InventoryTableDimmSlot.vue @@ -0,0 +1,263 @@ +<template> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.dimmSlot')" + > + <b-row class="align-items-end"> + <b-col sm="6" md="5" xl="4"> + <search + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </b-col> + <b-col sm="6" md="3" xl="2"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="dimms.length" + ></table-cell-count> + </b-col> + </b-row> + <b-table + sort-icon-left + no-sort-reset + sort-by="health" + responsive="md" + show-empty + no-border-collapse + :items="dimms" + :fields="fields" + :sort-desc="true" + :fixed="true" + :sort-compare="sortCompare" + :filter="searchFilter" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + :busy="isBusy" + @filtered="onFiltered" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandDimms" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + <!-- Toggle identify LED --> + <template #cell(identifyLed)="row"> + <b-form-checkbox + v-model="row.item.identifyLed" + name="switch" + switch + @change="toggleIdentifyLedValue(row.item)" + > + <span v-if="row.item.identifyLed"> + {{ $t('global.status.on') }} + </span> + <span v-else> {{ $t('global.status.off') }} </span> + </b-form-checkbox> + </template> + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col sm="6" xl="6"> + <dl> + <!-- Part Number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ dataFormatter(item.partNumber) }}</dd> + </dl> + <dl> + <!-- Serial Number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ dataFormatter(item.serialNumber) }}</dd> + </dl> + <dl> + <!-- Spare Part Number --> + <dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt> + <dd>{{ dataFormatter(item.sparePartNumber) }}</dd> + </dl> + <dl> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd>{{ dataFormatter(item.model) }}</dd> + </dl> + </b-col> + <b-col sm="6" xl="6"> + <dl> + <!-- Memory Size in kb --> + <dt>{{ $t('pageInventory.table.memorySize') }}:</dt> + <dd>{{ dataFormatter(item.memorySize) }} KB</dd> + </dl> + <dl> + <!-- Status--> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ dataFormatter(item.statusState) }}</dd> + </dl> + <dl> + <!-- Enabled--> + <dt>{{ $t('pageInventory.table.enabled') }}:</dt> + <dd>{{ dataFormatter(item.enabled) }}</dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col sm="6" xl="6"> + <dl> + <!-- Description --> + <dt>{{ $t('pageInventory.table.description') }}:</dt> + <dd>{{ dataFormatter(item.description) }}</dd> + </dl> + <dl> + <!-- Memory Type --> + <dt>{{ $t('pageInventory.table.memoryType') }}:</dt> + <dd>{{ dataFormatter(item.memoryType) }}</dd> + </dl> + <dl> + <!-- Base Module Type --> + <dt>{{ $t('pageInventory.table.baseModuleType') }}:</dt> + <dd>{{ dataFormatter(item.baseModuleType) }}</dd> + </dl> + <dl> + <!-- Capacity MiB --> + <dt>{{ $t('pageInventory.table.capacityMiB') }}:</dt> + <dd>{{ dataFormatter(item.capacityMiB) }}</dd> + </dl> + </b-col> + <b-col sm="6" xl="6"> + <dl> + <!-- Bus Width Bits --> + <dt>{{ $t('pageInventory.table.busWidthBits') }}:</dt> + <dd>{{ dataFormatter(item.busWidthBits) }}</dd> + </dl> + <dl> + <!-- Data Width Bits --> + <dt>{{ $t('pageInventory.table.dataWidthBits') }}:</dt> + <dd>{{ dataFormatter(item.dataWidthBits) }}</dd> + </dl> + <dl> + <!-- Operating Speed Mhz --> + <dt>{{ $t('pageInventory.table.operatingSpeedMhz') }}:</dt> + <dd>{{ dataFormatter(item.operatingSpeedMhz) }} MHz</dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; + +import StatusIcon from '@/components/Global/StatusIcon'; +import TableCellCount from '@/components/Global/TableCellCount'; + +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import Search from '@/components/Global/Search'; +import SearchFilterMixin, { + searchFilter, +} from '@/components/Mixins/SearchFilterMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount }, + mixins: [ + TableRowExpandMixin, + DataFormatterMixin, + TableSortMixin, + SearchFilterMixin, + ], + data() { + return { + isBusy: true, + fields: [ + { + key: 'expandRow', + label: '', + thStyle: { width: '4%' }, + tdClass: 'table-row-expand', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.dataFormatter, + tdClass: 'text-nowrap ', + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + ], + searchFilter: searchFilter, + searchTotalFilteredRows: 0, + expandRowLabel: expandRowLabel, + }; + }, + computed: { + filteredRows() { + return this.searchFilter + ? this.searchTotalFilteredRows + : this.dimms.length; + }, + dimms() { + return this.$store.getters['memory/dimms']; + }, + }, + created() { + this.$store.dispatch('memory/getDimms').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-dimm-slot-complete'); + this.isBusy = false; + }); + }, + methods: { + sortCompare(a, b, key) { + if (key === 'health') { + return this.sortStatus(a, b, key); + } + }, + onFiltered(filteredItems) { + this.searchTotalFilteredRows = filteredItems.length; + }, + toggleIdentifyLedValue(row) { + this.$store + .dispatch('memory/updateIdentifyLedValue', { + uri: row.uri, + identifyLed: row.identifyLed, + }) + .catch(({ message }) => this.errorToast(message)); + }, + }, +}; +</script> diff --git a/src/views/_sila/HardwareStatus/Inventory/InventoryTableFans.vue b/src/views/_sila/HardwareStatus/Inventory/InventoryTableFans.vue new file mode 100644 index 00000000..d110ad3f --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/InventoryTableFans.vue @@ -0,0 +1,198 @@ +<template> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.fans')" + > + <b-row class="align-items-end"> + <b-col sm="6" md="5" xl="4"> + <search + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </b-col> + <b-col sm="6" md="3" xl="2"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="fans.length" + ></table-cell-count> + </b-col> + </b-row> + <b-table + sort-icon-left + no-sort-reset + responsive="md" + sort-by="health" + show-empty + no-border-collapse + :items="fans" + :fields="fields" + :sort-desc="true" + :fixed="true" + :sort-compare="sortCompare" + :filter="searchFilter" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + :busy="isBusy" + @filtered="onFiltered" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandFans" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col sm="6" xl="4"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ dataFormatter(item.name) }}</dd> + </dl> + <dl> + <!-- Serial number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ dataFormatter(item.serialNumber) }}</dd> + </dl> + <dl> + <!-- Part number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ dataFormatter(item.partNumber) }}</dd> + </dl> + <dl> + <!-- Fan speed --> + <dt>{{ $t('pageInventory.table.fanSpeed') }}:</dt> + <dd>{{ dataFormatter(item.speed) }}</dd> + </dl> + </b-col> + <b-col sm="6" xl="4"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ dataFormatter(item.statusState) }}</dd> + </dl> + <dl> + <!-- Health Rollup state --> + <dt>{{ $t('pageInventory.table.statusHealthRollup') }}:</dt> + <dd>{{ dataFormatter(item.healthRollup) }}</dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; +import TableCellCount from '@/components/Global/TableCellCount'; + +import StatusIcon from '@/components/Global/StatusIcon'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import Search from '@/components/Global/Search'; +import SearchFilterMixin, { + searchFilter, +} from '@/components/Mixins/SearchFilterMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount }, + mixins: [ + TableRowExpandMixin, + DataFormatterMixin, + TableSortMixin, + SearchFilterMixin, + ], + data() { + return { + isBusy: true, + fields: [ + { + key: 'expandRow', + label: '', + sortable: false, + thStyle: { width: '4%' }, + tdClass: 'table-row-expand', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.dataFormatter, + sortable: true, + thStyle: { width: '20%' }, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.dataFormatter, + sortable: true, + tdClass: 'text-nowrap', + }, + { + key: 'partNumber', + label: this.$t('pageInventory.table.partNumber'), + formatter: this.dataFormatter, + sortable: true, + thStyle: { width: '20%' }, + }, + { + key: 'serialNumber', + label: this.$t('pageInventory.table.serialNumber'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + ], + searchFilter: searchFilter, + searchTotalFilteredRows: 0, + expandRowLabel: expandRowLabel, + }; + }, + computed: { + filteredRows() { + return this.searchFilter + ? this.searchTotalFilteredRows + : this.fans.length; + }, + fans() { + return this.$store.getters['fan/fans']; + }, + }, + created() { + this.$store.dispatch('fan/getFanInfo').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-fans-complete'); + this.isBusy = false; + }); + }, + methods: { + sortCompare(a, b, key) { + if (key === 'health') { + return this.sortStatus(a, b, key); + } + }, + onFiltered(filteredItems) { + this.searchTotalFilteredRows = filteredItems.length; + }, + }, +}; +</script> diff --git a/src/views/_sila/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue b/src/views/_sila/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue new file mode 100644 index 00000000..35e6e14d --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue @@ -0,0 +1,216 @@ +<template> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.powerSupplies')" + > + <b-row class="align-items-end"> + <b-col sm="6" md="5" xl="4"> + <search + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </b-col> + <b-col sm="6" md="3" xl="2"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="powerSupplies.length" + ></table-cell-count> + </b-col> + </b-row> + <b-table + sort-icon-left + no-sort-reset + hover + responsive="md" + sort-by="health" + show-empty + no-border-collapse + :items="powerSupplies" + :fields="fields" + :sort-desc="true" + :sort-compare="sortCompare" + :filter="searchFilter" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + :busy="isBusy" + @filtered="onFiltered" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandPowerSupplies" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col sm="6" xl="4"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ dataFormatter(item.name) }}</dd> + <!-- Part number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ dataFormatter(item.partNumber) }}</dd> + <!-- Serial number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ dataFormatter(item.serialNumber) }}</dd> + <!-- Spare part number --> + <dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt> + <dd>{{ dataFormatter(item.sparePartNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd>{{ dataFormatter(item.model) }}</dd> + </dl> + </b-col> + <b-col sm="6" xl="4"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ dataFormatter(item.statusState) }}</dd> + <!-- Status Health rollup state --> + <dt>{{ $t('pageInventory.table.statusHealthRollup') }}:</dt> + <dd>{{ dataFormatter(item.statusHealth) }}</dd> + <!-- Efficiency percent --> + <dt>{{ $t('pageInventory.table.efficiencyPercent') }}:</dt> + <dd>{{ dataFormatter(item.efficiencyPercent) }}</dd> + <!-- Power input watts --> + <dt>{{ $t('pageInventory.table.powerInputWatts') }}:</dt> + <dd>{{ dataFormatter(item.powerInputWatts) }}</dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col sm="6" xl="4"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ dataFormatter(item.manufacturer) }}</dd> + </dl> + </b-col> + <b-col sm="6" xl="4"> + <dl> + <!-- Firmware version --> + <dt>{{ $t('pageInventory.table.firmwareVersion') }}:</dt> + <dd>{{ dataFormatter(item.firmwareVersion) }}</dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; + +import StatusIcon from '@/components/Global/StatusIcon'; +import TableCellCount from '@/components/Global/TableCellCount'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import Search from '@/components/Global/Search'; +import SearchFilterMixin, { + searchFilter, +} from '@/components/Mixins/SearchFilterMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount }, + mixins: [ + TableRowExpandMixin, + DataFormatterMixin, + TableSortMixin, + SearchFilterMixin, + ], + data() { + return { + isBusy: true, + fields: [ + { + key: 'expandRow', + label: '', + sortable: false, + thStyle: { width: '4%' }, + tdClass: 'table-row-expand', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.dataFormatter, + sortable: true, + thStyle: { width: '20%' }, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.dataFormatter, + sortable: true, + tdClass: 'text-nowrap', + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.dataFormatter, + sortable: true, + thStyle: { width: '20%' }, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + ], + searchFilter: searchFilter, + searchTotalFilteredRows: 0, + expandRowLabel: expandRowLabel, + }; + }, + computed: { + filteredRows() { + return this.searchFilter + ? this.searchTotalFilteredRows + : this.powerSupplies.length; + }, + powerSupplies() { + return this.$store.getters['powerSupply/powerSupplies']; + }, + }, + created() { + this.$store.dispatch('powerSupply/getAllPowerSupplies').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-power-supplies-complete'); + this.isBusy = false; + }); + }, + methods: { + sortCompare(a, b, key) { + if (key === 'health') { + return this.sortStatus(a, b, key); + } + }, + onFiltered(filteredItems) { + this.searchTotalFilteredRows = filteredItems.length; + }, + }, +}; +</script> diff --git a/src/views/_sila/HardwareStatus/Inventory/InventoryTableProcessors.vue b/src/views/_sila/HardwareStatus/Inventory/InventoryTableProcessors.vue new file mode 100644 index 00000000..08128da4 --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/InventoryTableProcessors.vue @@ -0,0 +1,275 @@ +<template> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.processors')" + > + <!-- Search --> + <b-row class="align-items-end"> + <b-col sm="6" md="5" xl="4"> + <search + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </b-col> + <b-col sm="6" md="3" xl="2"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="processors.length" + > + </table-cell-count> + </b-col> + </b-row> + <b-table + sort-icon-left + no-sort-reset + hover + responsive="md" + show-empty + no-border-collapse + :items="processors" + :fields="fields" + :sort-desc="true" + :fixed="true" + :filter="searchFilter" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + :busy="isBusy" + @filtered="onFiltered" + > + <!-- Expand button --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandProcessors" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <!-- Toggle identify LED --> + <template #cell(identifyLed)="row"> + <b-form-checkbox + v-if="hasIdentifyLed(row.item.identifyLed)" + v-model="row.item.identifyLed" + name="switch" + switch + @change="toggleIdentifyLedValue(row.item)" + > + <span v-if="row.item.identifyLed"> + {{ $t('global.status.on') }} + </span> + <span v-else> {{ $t('global.status.off') }} </span> + </b-form-checkbox> + <div v-else>--</div> + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ dataFormatter(item.name) }}</dd> + <!-- Part Number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ dataFormatter(item.partNumber) }}</dd> + <!-- Serial Number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ dataFormatter(item.serialNumber) }}</dd> + <!-- Spare Part Number --> + <dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt> + <dd>{{ dataFormatter(item.sparePartNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd>{{ dataFormatter(item.model) }}</dd> + <!-- Asset Tag --> + <dt>{{ $t('pageInventory.table.assetTag') }}:</dt> + <dd>{{ dataFormatter(item.assetTag) }}</dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ dataFormatter(item.statusState) }}</dd> + <!-- Health Rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ dataFormatter(item.healthRollup) }}</dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col class="mt-1" sm="6" xl="6"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ dataFormatter(item.manufacturer) }}</dd> + <!-- Processor Type --> + <dt>{{ $t('pageInventory.table.processorType') }}:</dt> + <dd>{{ dataFormatter(item.processorType) }}</dd> + <!-- Processor Architecture --> + <dt>{{ $t('pageInventory.table.processorArchitecture') }}:</dt> + <dd>{{ dataFormatter(item.processorArchitecture) }}</dd> + <!-- Instruction Set --> + <dt>{{ $t('pageInventory.table.instructionSet') }}:</dt> + <dd>{{ dataFormatter(item.instructionSet) }}</dd> + <!-- Version --> + <dt>{{ $t('pageInventory.table.version') }}:</dt> + <dd>{{ dataFormatter(item.version) }}</dd> + </dl> + </b-col> + <b-col class="mt-1" sm="6" xl="6"> + <dl> + <!-- Min Speed MHz --> + <dt>{{ $t('pageInventory.table.minSpeedMHz') }}:</dt> + <dd>{{ dataFormatter(item.minSpeedMHz) }}</dd> + <!-- Max Speed MHz --> + <dt>{{ $t('pageInventory.table.maxSpeedMHz') }}:</dt> + <dd>{{ dataFormatter(item.maxSpeedMHz) }}</dd> + <!-- Total Cores --> + <dt>{{ $t('pageInventory.table.totalCores') }}:</dt> + <dd>{{ dataFormatter(item.totalCores) }}</dd> + <!-- Total Threads --> + <dt>{{ $t('pageInventory.table.totalThreads') }}:</dt> + <dd>{{ dataFormatter(item.totalThreads) }}</dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; +import StatusIcon from '@/components/Global/StatusIcon'; +import TableCellCount from '@/components/Global/TableCellCount'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import Search from '@/components/Global/Search'; +import SearchFilterMixin, { + searchFilter, +} from '@/components/Mixins/SearchFilterMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount }, + mixins: [ + BVToastMixin, + TableRowExpandMixin, + DataFormatterMixin, + TableSortMixin, + SearchFilterMixin, + ], + data() { + return { + isBusy: true, + fields: [ + { + key: 'expandRow', + label: '', + sortable: false, + thStyle: { width: '4%' }, + tdClass: 'table-row-expand ', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.dataFormatter, + sortable: true, + thStyle: { width: '20%' }, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.dataFormatter, + sortable: true, + tdClass: 'text-nowrap', + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.dataFormatter, + sortable: true, + thStyle: { width: '20%' }, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.dataFormatter, + sortable: false, + thStyle: { width: '20%' }, + }, + ], + processors: [ + { + id: 'Процессор №1', + health: 'v3.1.5', + locationNumber: '2.4', + identifyLed: '4', + }, + { + id: 'Процессор №1', + health: 'v3.1.5', + locationNumber: '2.4', + identifyLed: '4', + }, + ], + searchFilter: searchFilter, + searchTotalFilteredRows: 0, + expandRowLabel: expandRowLabel, + }; + }, + computed: { + filteredRows() { + return this.searchFilter + ? this.searchTotalFilteredRows + : this.processors.length; + }, + // processors() { + // return this.$store.getters['processors/processors']; + // }, + }, + created() { + this.$store.dispatch('processors/getProcessorsInfo').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-processors-complete'); + this.isBusy = false; + }); + }, + methods: { + onFiltered(filteredItems) { + this.searchTotalFilteredRows = filteredItems.length; + }, + toggleIdentifyLedValue(row) { + this.$store + .dispatch('processors/updateIdentifyLedValue', { + uri: row.uri, + identifyLed: row.identifyLed, + }) + .catch(({ message }) => this.errorToast(message)); + }, + // TO DO: remove hasIdentifyLed when the following is merged: + // https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/37045 + hasIdentifyLed(identifyLed) { + return typeof identifyLed === 'boolean'; + }, + }, +}; +</script> diff --git a/src/views/_sila/HardwareStatus/Inventory/InventoryTableSystem.vue b/src/views/_sila/HardwareStatus/Inventory/InventoryTableSystem.vue new file mode 100644 index 00000000..c3b7bd87 --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/InventoryTableSystem.vue @@ -0,0 +1,236 @@ +<template> + <page-section + class="bootstrap-table__section" + :section-small-title="$t('pageInventory.system')" + > + <b-table + responsive="md" + show-empty + no-border-collapse + :items="systems" + :fields="fields" + :fixed="true" + :empty-text="$t('global.table.emptyMessage')" + :busy="isBusy" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandSystem" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ statusIcon(value) }} + </template> + + <template #cell(locationIndicatorActive)="{ item }"> + <b-form-checkbox + id="identifyLedSwitchSystem" + v-model="item.locationIndicatorActive" + data-test-id="inventorySystem-toggle-identifyLed" + switch + @change="toggleIdentifyLedSwitch" + > + <span v-if="item.locationIndicatorActive"> + {{ $t('global.status.on') }} + </span> + <span v-else>{{ $t('global.status.off') }}</span> + </b-form-checkbox> + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col class="mt-2" sm="6"> + <dl> + <!-- Serial number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ dataFormatter(item.serialNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd>{{ dataFormatter(item.model) }}</dd> + <!-- Asset tag --> + <dt>{{ $t('pageInventory.table.assetTag') }}:</dt> + <dd class="mb-2"> + {{ dataFormatter(item.assetTag) }} + </dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ dataFormatter(item.statusState) }}</dd> + <!-- Power state --> + <dt>{{ $t('pageInventory.table.power') }}:</dt> + <dd>{{ dataFormatter(item.powerState) }}</dd> + <!-- Health rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ dataFormatter(item.healthRollup) }}</dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col class="mt-1" sm="6"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ dataFormatter(item.manufacturer) }}</dd> + <!-- Description --> + <dt>{{ $t('pageInventory.table.description') }}:</dt> + <dd>{{ dataFormatter(item.description) }}</dd> + <!-- Sub Model --> + <dt>{{ $t('pageInventory.table.subModel') }}:</dt> + <dd> + {{ dataFormatter(item.subModel) }} + </dd> + <!-- System Type --> + <dt>{{ $t('pageInventory.table.systemType') }}:</dt> + <dd> + {{ dataFormatter(item.systemType) }} + </dd> + </dl> + </b-col> + <b-col sm="6"> + <!-- Memory Summary --> + <p class="mt-1 mb-2 h6 float-none m-0"> + {{ $t('pageInventory.table.memorySummary') }} + </p> + <dl class="ml-4"> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ dataFormatter(item.memorySummaryState) }}</dd> + <!-- Health --> + <dt>{{ $t('pageInventory.table.health') }}:</dt> + <dd>{{ dataFormatter(item.memorySummaryHealth) }}</dd> + <!-- Health Roll --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ dataFormatter(item.memorySummaryHealthRollup) }}</dd> + <!-- Total system memory --> + <dt>{{ $t('pageInventory.table.totalSystemMemoryGiB') }}:</dt> + <dd>{{ dataFormatter(item.totalSystemMemoryGiB) }}GB</dd> + </dl> + <!-- Processor Summary --> + <p class="mt-1 mb-2 h6 float-none m-0"> + {{ $t('pageInventory.table.processorSummary') }} + </p> + <dl class="ml-4"> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ dataFormatter(item.processorSummaryState) }}</dd> + <!-- Health --> + <dt>{{ $t('pageInventory.table.health') }}:</dt> + <dd>{{ dataFormatter(item.processorSummaryHealth) }}</dd> + <!-- Health Rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ dataFormatter(item.processorSummaryHealthRoll) }}</dd> + <!-- Count --> + <dt>{{ $t('pageInventory.table.count') }}:</dt> + <dd>{{ dataFormatter(item.processorSummaryCount) }}</dd> + <!-- Core Count --> + <dt>{{ $t('pageInventory.table.coreCount') }}:</dt> + <dd>{{ dataFormatter(item.processorSummaryCoreCount) }}</dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; + +import StatusIcon from '@/components/Global/StatusIcon'; + +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon }, + mixins: [BVToastMixin, TableRowExpandMixin, DataFormatterMixin], + data() { + return { + isBusy: true, + fields: [ + { + key: 'expandRow', + label: '', + thStyle: { width: '4%' }, + tdClass: 'table-row-expand', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + { + key: 'hardwareType', + label: this.$t('pageInventory.table.hardwareType'), + formatter: this.dataFormatter, + tdClass: 'text-nowrap ', + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.dataFormatter, + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + { + key: 'locationIndicatorActive', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + ], + expandRowLabel: expandRowLabel, + }; + }, + computed: { + systems() { + return this.$store.getters['system/systems']; + }, + }, + created() { + this.$store.dispatch('system/getSystem').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-system-complete'); + this.isBusy = false; + }); + }, + methods: { + toggleIdentifyLedSwitch(state) { + this.$store + .dispatch('system/changeIdentifyLedState', state) + .catch(({ message }) => this.errorToast(message)); + }, + }, +}; +</script> +<style lang="scss" scoped> +.custom-switch { + margin: 0; +} +</style> diff --git a/src/views/_sila/HardwareStatus/Inventory/index.js b/src/views/_sila/HardwareStatus/Inventory/index.js new file mode 100644 index 00000000..c9fde8d2 --- /dev/null +++ b/src/views/_sila/HardwareStatus/Inventory/index.js @@ -0,0 +1,2 @@ +import Inventory from './Inventory.vue'; +export default Inventory; diff --git a/src/views/_sila/HardwareStatus/Sensors/Sensors.vue b/src/views/_sila/HardwareStatus/Sensors/Sensors.vue index 347efd6c..b98e023d 100644 --- a/src/views/_sila/HardwareStatus/Sensors/Sensors.vue +++ b/src/views/_sila/HardwareStatus/Sensors/Sensors.vue @@ -1,5 +1,5 @@ <template> - <b-container fluid="xl"> + <b-container> <page-title /> <b-row class="align-items-end"> <b-col sm="6" md="5" xl="4"> @@ -254,3 +254,8 @@ export default { }, }; </script> +<style lang="scss"> +.container > .page-title > .text-title { + margin-left: 0 !important; +} +</style> diff --git a/src/views/_sila/InformationAndFAQ/InformationAndFAQ.vue b/src/views/_sila/InformationAndFAQ/InformationAndFAQ.vue new file mode 100644 index 00000000..ede7b3d8 --- /dev/null +++ b/src/views/_sila/InformationAndFAQ/InformationAndFAQ.vue @@ -0,0 +1,9 @@ +<template> + <span>This Page (Information And FAQ) In develop</span> +</template> + +<script> +export default { + name: 'InformationAndFAQ', +}; +</script> diff --git a/src/views/_sila/InformationAndFAQ/index.js b/src/views/_sila/InformationAndFAQ/index.js new file mode 100644 index 00000000..6bbeaba6 --- /dev/null +++ b/src/views/_sila/InformationAndFAQ/index.js @@ -0,0 +1 @@ +export { default } from './InformationAndFAQ.vue'; diff --git a/src/views/_sila/Login/Login.vue b/src/views/_sila/Login/Login.vue index 3c330362..1b8715c9 100644 --- a/src/views/_sila/Login/Login.vue +++ b/src/views/_sila/Login/Login.vue @@ -5,15 +5,30 @@ {{ $t('pageLogin.alert.message') }} </p> </alert> - <b-form-group label-for="language" :label="$t('pageLogin.language')"> + <!-- <b-form-group label-for="language" :label="$t('pageLogin.language')"> <b-form-select id="language" v-model="$i18n.locale" :options="languages" data-test-id="login-select-language" ></b-form-select> - </b-form-group> - <b-form-group label-for="username" :label="$t('pageLogin.username')"> + </b-form-group> --> + <b-form-group + class="bold-24px" + label-for="auth" + :label="$t('pageLogin.auth')" + ></b-form-group> + + <b-form-group + label-for="auth" + class="auth-description" + :label="$t('pageLogin.authDescription')" + ></b-form-group> + + <b-form-group> + <label class="regular-12px tretiatry" for="username">{{ + $t('pageLogin.username') + }}</label> <b-form-input id="username" v-model="userInfo.username" @@ -22,6 +37,7 @@ type="text" autofocus="autofocus" data-test-id="login-input-username" + class="form-control-with-button username-input" @input="$v.userInfo.username.$touch()" > </b-form-input> @@ -31,9 +47,12 @@ </template> </b-form-invalid-feedback> </b-form-group> - <div class="login-form__section mb-3"> - <label for="password">{{ $t('pageLogin.password') }}</label> + + <b-form-group> <input-password-toggle> + <label class="regular-12px tretiatry" for="password">{{ + $t('pageLogin.password') + }}</label> <b-form-input id="password" v-model="userInfo.password" @@ -41,19 +60,20 @@ :state="getValidationState($v.userInfo.password)" type="password" data-test-id="login-input-password" - class="form-control-with-button" + class="form-control-with-button password-input" @input="$v.userInfo.password.$touch()" > </b-form-input> + <b-form-invalid-feedback id="password-required" role="alert"> + <template v-if="!$v.userInfo.password.required"> + {{ $t('global.form.fieldRequired') }} + </template> + </b-form-invalid-feedback> </input-password-toggle> - <b-form-invalid-feedback id="password-required" role="alert"> - <template v-if="!$v.userInfo.password.required"> - {{ $t('global.form.fieldRequired') }} - </template> - </b-form-invalid-feedback> - </div> + </b-form-group> + <b-button - class="mt-3" + class="login-button" type="submit" variant="primary" data-test-id="login-button-submit" @@ -83,6 +103,10 @@ export default { disableSubmitButton: false, languages: [ { + value: 'ru-RU', + text: 'Русский', + }, + { value: 'en-US', text: 'English', }, @@ -90,10 +114,6 @@ export default { value: 'es', text: 'Español', }, - { - value: 'ru-RU', - text: 'Русский', - }, ], }; }, @@ -144,3 +164,35 @@ export default { }, }; </script> +<style scoped> +.regular-12px { + margin-left: 10px; +} +.username-input, +.password-input { + height: 56px; + margin: -30px 0 0 0; + padding-top: 30px; + font-family: 'Inter', sans-serif; + font-size: 14px; + font-weight: 500; + line-height: 20px; + letter-spacing: 0em; +} + +.text-input { + margin: 0 0 0 50px; + padding: 0 0 0 5px; +} + +.login-button { + height: 36px; + width: 380px; +} + +@media (max-width: 576px) { + .login-button { + width: 100%; + } +} +</style> diff --git a/src/views/_sila/Login/index.js b/src/views/_sila/Login/index.js index 8fe0250d..3eac6cc0 100644 --- a/src/views/_sila/Login/index.js +++ b/src/views/_sila/Login/index.js @@ -1,2 +1 @@ -import Login from './Login.vue'; -export default Login; +export { default } from './Login.vue'; diff --git a/src/views/_sila/Logs/Dumps/DumpsForm.vue b/src/views/_sila/Logs/Dumps/DumpsForm.vue index acd755c7..07f4a060 100644 --- a/src/views/_sila/Logs/Dumps/DumpsForm.vue +++ b/src/views/_sila/Logs/Dumps/DumpsForm.vue @@ -35,9 +35,9 @@ <script> import { required } from 'vuelidate/lib/validators'; import ModalConfirmation from './DumpsModalConfirmation'; -import Alert from '@/components/_sila/Global/Alert'; -import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; -import VuelidateMixin from '@/components/_sila/Mixins/VuelidateMixin.js'; +import Alert from '@/components/Global/Alert'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; export default { components: { Alert, ModalConfirmation }, diff --git a/src/views/_sila/Logs/Dumps/DumpsModalConfirmation.vue b/src/views/_sila/Logs/Dumps/DumpsModalConfirmation.vue index 49ef960e..f8e20cfd 100644 --- a/src/views/_sila/Logs/Dumps/DumpsModalConfirmation.vue +++ b/src/views/_sila/Logs/Dumps/DumpsModalConfirmation.vue @@ -38,8 +38,8 @@ </template> <script> -import StatusIcon from '@/components/_sila/Global/StatusIcon'; -import VuelidateMixin from '@/components/_sila/Mixins/VuelidateMixin.js'; +import StatusIcon from '@/components/Global/StatusIcon'; +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; export default { components: { StatusIcon }, diff --git a/src/views/_sila/Logs/EventLogs/EventLogs.vue b/src/views/_sila/Logs/EventLogs/EventLogs.vue index adaa2e9e..dda42c6c 100644 --- a/src/views/_sila/Logs/EventLogs/EventLogs.vue +++ b/src/views/_sila/Logs/EventLogs/EventLogs.vue @@ -1,63 +1,93 @@ <template> - <b-container fluid="xl"> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + class="logs-section" + > <page-title /> + <div class="logs-head-container"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="allLogs.length" + ></table-cell-count> + <search + :placeholder="$t('pageEventLogs.table.searchLogs')" + data-test-id="eventLogs-input-searchLogs" + style="margin: 13px; width: 35%" + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </div> <b-row class="align-items-start"> - <b-col sm="8" xl="6" class="d-sm-flex align-items-end mb-4"> - <search - :placeholder="$t('pageEventLogs.table.searchLogs')" - data-test-id="eventLogs-input-searchLogs" - @change-search="onChangeSearchInput" - @clear-search="onClearSearchInput" - /> - <div class="ml-sm-4"> - <table-cell-count - :filtered-items-count="filteredRows" - :total-number-of-cells="allLogs.length" - ></table-cell-count> - </div> - </b-col> - <b-col sm="8" md="7" xl="6"> + <b-col md="12" xl="6"> <table-date-filter @change="onChangeDateTimeFilter" /> </b-col> - </b-row> - <b-row> - <b-col class="text-right"> + <b-col + md="12" + xl="6" + class="d-flex align-items-end justify-content-between mt-3" + > <table-filter :filters="tableFilters" @filter-change="onFilterChange" /> - <b-button - variant="link" - :disabled="allLogs.length === 0" - @click="deleteAllLogs" + <popover-with-slot + id="popover-delete" + :button-label="$t('pageEventLogs.clear')" + :popup-label="$t('pageEventLogs.clearLogs_popup')" + placement="left" + :action="deleteAllLogs" > - <icon-delete /> {{ $t('global.action.deleteAll') }} - </b-button> + <b-button + id="popover-delete" + ref="button" + variant="link" + style="color: $red-brand-primary" + :disabled="allLogs.length === 0" + > + <icon-delete /> {{ $t('pageEventLogs.clearLogs') }} + </b-button> + </popover-with-slot> <b-button variant="primary" + size="md" :class="{ disabled: allLogs.length === 0 }" :download="exportFileNameByDate()" :href="href" > - <icon-export /> {{ $t('global.action.exportAll') }} + <icon-download /> {{ $t('global.action.exportAll') }} </b-button> </b-col> </b-row> <b-row> - <b-col> + <b-col id="logs-table-wrapper" class="m-0 p-0"> <table-toolbar ref="toolbar" :selected-items-count="selectedRows.length" :actions="batchActions" @clear-selected="clearSelectedRows($refs.table)" - @batch-action="onBatchAction" > <template #toolbar-buttons> - <b-button variant="primary" @click="resolveLogs"> + <b-button + size="md" + variant="secondary" + style="margin-right: 1rem" + @click="resolveLogs" + > + <img + class="sourse__img" + src="@/assets/images/_sila/event-logs/resolved-button.svg" + /> {{ $t('pageEventLogs.resolve') }} </b-button> - <b-button variant="primary" @click="unresolveLogs"> + <b-button + size="md" + variant="primary" + style="margin-right: 1rem" + @click="unresolveLogs" + > {{ $t('pageEventLogs.unresolve') }} </b-button> <table-toolbar-export :data="batchExportData" + style="margin-right: 1rem" :file-name="exportFileNameByDate()" /> </template> @@ -66,6 +96,7 @@ id="table-event-logs" ref="table" responsive="md" + class="logs-table" selectable no-select-on-click sort-icon-left @@ -73,7 +104,9 @@ no-sort-reset sort-desc show-empty + no-border-collapse sort-by="id" + :sticky-header="true" :fields="fields" :items="filteredLogs" :sort-compare="onSortCompare" @@ -106,126 +139,123 @@ <span class="sr-only">{{ $t('global.table.selectItem') }}</span> </b-form-checkbox> </template> - - <!-- Expand chevron icon --> - <template #cell(expandRow)="row"> - <b-button - variant="link" - :aria-label="expandRowLabel" - :title="expandRowLabel" - class="btn-icon-only" - @click="toggleRowDetails(row)" + <!-- Description column --> + <template #cell(description)="data"> + <b-row class="semi-bold-16px tretiatry"> + {{ filteredLogs[data.index].name }} + </b-row> + <b-row + class="regular-12px tretiatry pointer" + @click="toggleRowDetails(data)" > - <icon-chevron /> - </b-button> + {{ data.value }} + </b-row> </template> <template #row-details="{ item }"> - <b-container fluid> + <b-container + fluid + style="border-bottom: 1px solid rgba(26, 62, 91, 0.1)" + > <b-row> - <b-col> + <b-col class="mt-1"> <dl> <!-- Name --> - <dt>{{ $t('pageEventLogs.table.name') }}:</dt> - <dd>{{ dataFormatter(item.name) }}</dd> + <dt class="semi-bold-12px"> + {{ $t('pageEventLogs.table.name') }}: + </dt> + <dd class="regular-12px">{{ dataFormatter(item.name) }}</dd> </dl> <dl> - <!-- Type --> - <dt>{{ $t('pageEventLogs.table.type') }}:</dt> - <dd>{{ dataFormatter(item.type) }}</dd> + <!-- Description --> + <dt class="semi-bold-12px"> + {{ $t('pageEventLogs.table.description') }}: + </dt> + <dd class="regular-12px tretiatry"> + {{ dataFormatter(item.description) }} + </dd> </dl> - </b-col> - <b-col> <dl> - <!-- Modified date --> - <dt>{{ $t('pageEventLogs.table.modifiedDate') }}:</dt> - <dd v-if="item.modifiedDate"> - {{ item.modifiedDate | formatDate }} - {{ item.modifiedDate | formatTime }} + <!-- Type --> + <dt class="semi-bold-12px"> + {{ $t('pageEventLogs.table.type') }}: + </dt> + <dd class="regular-12px tretiatry"> + {{ dataFormatter(item.type) }} </dd> - <dd v-else>--</dd> </dl> </b-col> - <b-col class="text-nowrap"> - <b-button - class="btn btn-secondary float-right" - :href="item.additionalDataUri" - target="_blank" - > - <icon-download />{{ $t('pageEventLogs.additionalDataUri') }} - </b-button> - </b-col> </b-row> </b-container> </template> <!-- Severity column --> <template #cell(severity)="{ value }"> - <status-icon v-if="value" :status="statusIcon(value)" /> - {{ value }} + <span + :class="`indicators-${value}`" + class="bold-12px__caps" + style="text-overflow: ellipsis; white-space: nowrap" + > + {{ value }}</span + > </template> <!-- Date column --> <template #cell(date)="{ value }"> - <p class="mb-0">{{ value | formatDate }}</p> - <p class="mb-0">{{ value | formatTime }}</p> + <img + src="@/assets/images/_sila/event-logs/time.svg" + class="sourse__img" + /> + <span v-if="value" class="regular-12px quaternary"> + {{ value | formatDate }} + </span> + <span v-if="value" class="regular-12px quaternary"> + в {{ value | formatTimeShort }} + </span> </template> <!-- Status column --> - <template #cell(status)="row"> - <b-form-checkbox - v-model="row.item.status" - name="switch" - switch - @change="changelogStatus(row.item)" - > - <span v-if="row.item.status"> + <template #cell(status)="{ value }"> + <b-row v-if="value === true"> + <img + class="sourse__img" + src="@/assets/images/_sila/event-logs/resolved.svg" + /> + <span class="semi-bold-12px__caps resolved"> {{ $t('pageEventLogs.resolved') }} </span> - <span v-else> {{ $t('pageEventLogs.unresolved') }} </span> - </b-form-checkbox> - </template> - <template #cell(filterByStatus)="{ value }"> - {{ value }} - </template> - - <!-- Actions column --> - <template #cell(actions)="row"> - <table-row-action - v-for="(action, index) in row.item.actions" - :key="index" - :value="action.value" - :title="action.title" - :row-data="row.item" - :export-name="exportFileNameByDate('export')" - :data-test-id="`eventLogs-button-deleteRow-${row.index}`" - @click-table-action="onTableRowAction($event, row.item)" - > - <template #icon> - <icon-export v-if="action.value === 'export'" /> - <icon-trashcan v-if="action.value === 'delete'" /> - </template> - </table-row-action> + </b-row> + <b-row v-else> + <img + class="sourse__img" + src="@/assets/images/_sila/event-logs/not-resolved.svg" + /> + <span class="semi-bold-12px__caps not-resolved"> + {{ $t('pageEventLogs.unresolved') }}</span + > + </b-row> </template> </b-table> </b-col> </b-row> - <!-- Table pagination --> - <b-row> - <b-col sm="6"> + <b-row class="logs-pagination-container"> + <b-col sm="6" class="d-flex align-items-center justify-content-start"> <b-form-group class="table-pagination-select" :label="$t('global.table.itemsPerPage')" - label-for="pagination-items-per-page" + label-class="semi-bold-16px mb-0" + label-for="pagination" > <b-form-select - id="pagination-items-per-page" + id="pagination" v-model="perPage" + class="select-per-page semi-bold-16px mb-0" :options="itemsPerPageOptions" /> </b-form-group> </b-col> - <b-col sm="6"> + <b-col sm="6" class="d-flex align-items-center justify-content-end"> + <span class="semi-bold-16px"> Страницы </span> <b-pagination v-model="currentPage" first-number @@ -241,21 +271,17 @@ <script> import IconDelete from '@carbon/icons-vue/es/trash-can/20'; -import IconTrashcan from '@carbon/icons-vue/es/trash-can/20'; -import IconExport from '@carbon/icons-vue/es/document--export/20'; -import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; import IconDownload from '@carbon/icons-vue/es/download/20'; import { omit } from 'lodash'; import PageTitle from '@/components/_sila/Global/PageTitle'; -import StatusIcon from '@/components/_sila/Global/StatusIcon'; import Search from '@/components/_sila/Global/Search'; import TableCellCount from '@/components/_sila/Global/TableCellCount'; import TableDateFilter from '@/components/_sila/Global/TableDateFilter'; import TableFilter from '@/components/_sila/Global/TableFilter'; -import TableRowAction from '@/components/_sila/Global/TableRowAction'; import TableToolbar from '@/components/_sila/Global/TableToolbar'; import TableToolbarExport from '@/components/_sila/Global/TableToolbarExport'; +import PopoverWithSlot from '@/components/_sila/Global/SilaComponents/PopoverWithSlot'; import LoadingBarMixin from '@/components/_sila/Mixins/LoadingBarMixin'; import TableFilterMixin from '@/components/_sila/Mixins/TableFilterMixin'; @@ -282,19 +308,15 @@ import SearchFilterMixin, { export default { components: { IconDelete, - IconExport, - IconTrashcan, - IconChevron, IconDownload, PageTitle, Search, - StatusIcon, TableCellCount, TableFilter, - TableRowAction, TableToolbar, TableToolbarExport, TableDateFilter, + PopoverWithSlot, }, mixins: [ BVPaginationMixin, @@ -316,47 +338,52 @@ export default { data() { return { isBusy: true, + sortDesc: true, fields: [ { - key: 'expandRow', - label: '', - tdClass: 'table-row-expand', - }, - { key: 'checkbox', sortable: false, + thClass: ' logs-table__th', + tdClass: 'logs-table__td', }, { key: 'id', label: this.$t('pageEventLogs.table.id'), sortable: true, + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'semi-bold-12px logs-table__td', + }, + { + key: 'description', + label: this.$t('pageEventLogs.table.about_event'), + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'logs-table__td', }, { key: 'severity', label: this.$t('pageEventLogs.table.severity'), - sortable: true, - tdClass: 'text-nowrap', + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'logs-table__td', + }, + { + key: 'source', + label: this.$t('pageEventLogs.table.source'), + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'logs-table__td semi-bold-12px__caps tretiatry ', }, { key: 'date', label: this.$t('pageEventLogs.table.date'), sortable: true, - tdClass: 'text-nowrap', - }, - { - key: 'description', - label: this.$t('pageEventLogs.table.description'), - tdClass: 'text-break', + thClass: `bold-12px__caps logs-table__th`, + tdClass: 'logs-table__td', }, { key: 'status', + sortable: true, label: this.$t('pageEventLogs.table.status'), - }, - { - key: 'actions', - sortable: false, - label: '', - tdClass: 'text-right text-nowrap', + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'logs-table__td', }, ], tableFilters: [ @@ -431,7 +458,7 @@ export default { return this.getFilteredTableData( this.filteredLogsByDate, this.activeFilters - ); + ).filter((el) => el.description); }, }, created() { @@ -454,21 +481,10 @@ export default { .catch(({ message }) => this.errorToast(message)); }, deleteAllLogs() { - this.$bvModal - .msgBoxConfirm(this.$t('pageEventLogs.modal.deleteAllMessage'), { - title: this.$t('pageEventLogs.modal.deleteAllTitle'), - okTitle: this.$t('global.action.delete'), - okVariant: 'danger', - cancelTitle: this.$t('global.action.cancel'), - }) - .then((deleteConfirmed) => { - if (deleteConfirmed) { - this.$store - .dispatch('eventLog/deleteAllEventLogs', this.allLogs) - .then((message) => this.successToast(message)) - .catch(({ message }) => this.errorToast(message)); - } - }); + this.$store + .dispatch('eventLog/deleteAllEventLogs', this.allLogs) + .then((message) => this.successToast(message)) + .catch(({ message }) => this.errorToast(message)); }, deleteLogs(uris) { this.$store @@ -602,3 +618,20 @@ export default { }, }; </script> +<style lang="scss" scoped> +.semi-bold-14px { + margin: auto 0; +} +.row { + margin: 0px; +} + +.not-resolved { + color: $text-quaternary; +} + +.form-group, +.b-pagination { + margin: 0px !important; +} +</style> diff --git a/src/views/_sila/Logs/PostCodeLogs/PostCodeLogs.vue b/src/views/_sila/Logs/PostCodeLogs/PostCodeLogs.vue index e05ef639..14359dfb 100644 --- a/src/views/_sila/Logs/PostCodeLogs/PostCodeLogs.vue +++ b/src/views/_sila/Logs/PostCodeLogs/PostCodeLogs.vue @@ -1,26 +1,27 @@ <template> - <b-container fluid="xl"> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + class="logs-section" + > <page-title /> - <b-row class="align-items-start"> - <b-col sm="8" xl="6" class="d-sm-flex align-items-end mb-4"> - <search - :placeholder="$t('pagePostCodeLogs.table.searchLogs')" - @change-search="onChangeSearchInput" - @clear-search="onClearSearchInput" - /> - <div class="ml-sm-4"> - <table-cell-count - :filtered-items-count="filteredRows" - :total-number-of-cells="allLogs.length" - ></table-cell-count> - </div> - </b-col> - <b-col sm="8" md="7" xl="6"> + <div class="logs-head-container"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="allLogs.length" + ></table-cell-count> + <search + :placeholder="$t('pagePostCodeLogs.table.searchLogs')" + style="margin: 13px; width: 35%" + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </div> + <b-row class="align-items-start m-0 p-0"> + <b-col sm="7" md="6" xl="5"> <table-date-filter @change="onChangeDateTimeFilter" /> </b-col> - </b-row> - <b-row> - <b-col xl="12" class="text-right"> + <b-col class="d-flex justify-content-end mt-3"> <b-button variant="primary" :disabled="allLogs.length === 0" @@ -31,8 +32,8 @@ </b-button> </b-col> </b-row> - <b-row> - <b-col> + <b-row class="m-0 p-0"> + <b-col class="p-0"> <table-toolbar ref="toolbar" :selected-items-count="selectedRows.length" @@ -40,107 +41,116 @@ > <template #toolbar-buttons> <table-toolbar-export + style="margin-right: 1rem" :data="batchExportData" :file-name="exportFileNameByDate()" /> </template> </table-toolbar> - <b-table - id="table-post-code-logs" - ref="table" - responsive="md" - selectable - no-select-on-click - sort-icon-left - hover - no-sort-reset - sort-desc - show-empty - sort-by="id" - :fields="fields" - :items="filteredLogs" - :empty-text="$t('global.table.emptyMessage')" - :empty-filtered-text="$t('global.table.emptySearchMessage')" - :per-page="perPage" - :current-page="currentPage" - :filter="searchFilter" - :busy="isBusy" - @filtered="onFiltered" - @row-selected="onRowSelected($event, filteredLogs.length)" - > - <!-- Checkbox column --> - <template #head(checkbox)> - <b-form-checkbox - v-model="tableHeaderCheckboxModel" - data-test-id="postCode-checkbox-selectAll" - :indeterminate="tableHeaderCheckboxIndeterminate" - @change="onChangeHeaderCheckbox($refs.table)" - > - <span class="sr-only">{{ $t('global.table.selectAll') }}</span> - </b-form-checkbox> - </template> - <template #cell(checkbox)="row"> - <b-form-checkbox - v-model="row.rowSelected" - :data-test-id="`postCode-checkbox-selectRow-${row.index}`" - @change="toggleSelectRow($refs.table, row.index)" - > - <span class="sr-only">{{ $t('global.table.selectItem') }}</span> - </b-form-checkbox> - </template> - <!-- Date column --> - <template #cell(date)="{ value }"> - <p class="mb-0">{{ value | formatDate }}</p> - <p class="mb-0">{{ value | formatTime }}</p> - </template> + <div id="logs-table-wrapper"> + <b-table + id="logs-table" + ref="table" + responsive="md" + class="logs-table" + selectable + no-select-on-click + sort-icon-left + hover + no-sort-reset + sort-desc + show-empty + sort-by="id" + :no-border-collapse="true" + :sticky-header="true" + :fields="fields" + :items="filteredLogs" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + :per-page="perPage" + :current-page="currentPage" + :filter="searchFilter" + :busy="isBusy" + @filtered="onFiltered" + @row-selected="onRowSelected($event, filteredLogs.length)" + > + <!-- Checkbox column --> + <template #head(checkbox)> + <b-form-checkbox + v-model="tableHeaderCheckboxModel" + data-test-id="postCode-checkbox-selectAll" + :indeterminate="tableHeaderCheckboxIndeterminate" + @change="onChangeHeaderCheckbox($refs.table)" + > + <span class="sr-only">{{ $t('global.table.selectAll') }}</span> + </b-form-checkbox> + </template> + <template #cell(checkbox)="row"> + <b-form-checkbox + v-model="row.rowSelected" + :data-test-id="`postCode-checkbox-selectRow-${row.index}`" + @change="toggleSelectRow($refs.table, row.index)" + > + <span class="sr-only">{{ $t('global.table.selectItem') }}</span> + </b-form-checkbox> + </template> + <!-- Date column --> + <template #cell(date)="{ value }"> + <p class="mb-0">{{ value | formatDate }}</p> + <p class="mb-0">{{ value | formatTime }}</p> + </template> - <!-- Actions column --> - <template #cell(actions)="row"> - <table-row-action - v-for="(action, index) in row.item.actions" - :key="index" - :value="action.value" - :title="action.title" - :row-data="row.item" - :btn-icon-only="true" - :export-name="exportFileNameByDate(action.value)" - :download-location="row.item.uri" - :download-in-new-tab="true" - :show-button="false" - > - <template #icon> - <icon-export v-if="action.value === 'export'" /> - <icon-download v-if="action.value === 'download'" /> - </template> - </table-row-action> - </template> - </b-table> + <!-- Actions column --> + <template #cell(actions)="row"> + <table-row-action + v-for="(action, index) in row.item.actions" + :key="index" + :value="action.value" + :title="action.title" + :row-data="row.item" + :btn-icon-only="true" + :export-name="exportFileNameByDate(action.value)" + :download-location="row.item.uri" + :download-in-new-tab="true" + :show-button="false" + > + <template #icon> + <icon-export v-if="action.value === 'export'" /> + <icon-download v-if="action.value === 'download'" /> + </template> + </table-row-action> + </template> + </b-table> + </div> </b-col> </b-row> <!-- Table pagination --> - <b-row> - <b-col sm="6"> + <b-row class="logs-pagination-container m-0"> + <b-col sm="6" class="d-flex align-items-center justify-content-start"> <b-form-group class="table-pagination-select" :label="$t('global.table.itemsPerPage')" + label-class="semi-bold-16px mb-0" label-for="pagination-items-per-page" > <b-form-select id="pagination-items-per-page" v-model="perPage" + class="select-per-page semi-bold-16px mb-0" :options="itemsPerPageOptions" /> </b-form-group> </b-col> - <b-col sm="6"> + <b-col sm="6" class="d-flex align-items-center justify-content-end"> + <span class="semi-bold-16px"> Страницы </span> <b-pagination v-model="currentPage" first-number last-number :per-page="perPage" :total-rows="getTotalRowCount(filteredRows)" - aria-controls="table-post-code-logs" + aria-controls="table-event-logs" /> </b-col> </b-row> @@ -151,33 +161,33 @@ import IconDownload from '@carbon/icons-vue/es/download/20'; import IconExport from '@carbon/icons-vue/es/document--export/20'; import { omit } from 'lodash'; -import PageTitle from '@/components/_sila/Global/PageTitle'; -import Search from '@/components/_sila/Global/Search'; -import TableCellCount from '@/components/_sila/Global/TableCellCount'; -import TableDateFilter from '@/components/_sila/Global/TableDateFilter'; -import TableRowAction from '@/components/_sila/Global/TableRowAction'; -import TableToolbar from '@/components/_sila/Global/TableToolbar'; -import TableToolbarExport from '@/components/_sila/Global/TableToolbarExport'; -import LoadingBarMixin from '@/components/_sila/Mixins/LoadingBarMixin'; -import TableFilterMixin from '@/components/_sila/Mixins/TableFilterMixin'; +import PageTitle from '@/components/Global/PageTitle'; +import Search from '@/components/Global/Search'; +import TableCellCount from '@/components/Global/TableCellCount'; +import TableDateFilter from '@/components/Global/TableDateFilter'; +import TableRowAction from '@/components/Global/TableRowAction'; +import TableToolbar from '@/components/Global/TableToolbar'; +import TableToolbarExport from '@/components/Global/TableToolbarExport'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import TableFilterMixin from '@/components/Mixins/TableFilterMixin'; import BVPaginationMixin, { currentPage, perPage, itemsPerPageOptions, -} from '@/components/_sila/Mixins/BVPaginationMixin'; +} from '@/components/Mixins/BVPaginationMixin'; import BVTableSelectableMixin, { selectedRows, tableHeaderCheckboxModel, tableHeaderCheckboxIndeterminate, -} from '@/components/_sila/Mixins/BVTableSelectableMixin'; -import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; -import TableSortMixin from '@/components/_sila/Mixins/TableSortMixin'; +} from '@/components/Mixins/BVTableSelectableMixin'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; import TableRowExpandMixin, { expandRowLabel, -} from '@/components/_sila/Mixins/TableRowExpandMixin'; +} from '@/components/Mixins/TableRowExpandMixin'; import SearchFilterMixin, { searchFilter, -} from '@/components/_sila/Mixins/SearchFilterMixin'; +} from '@/components/Mixins/SearchFilterMixin'; export default { components: { @@ -210,32 +220,44 @@ export default { data() { return { isBusy: true, + // stickyHeader: 'calc(100vh - 307px)', fields: [ { key: 'checkbox', sortable: false, + thClass: ' logs-table__th', + tdClass: 'logs-table__td', }, { key: 'date', label: this.$t('pagePostCodeLogs.table.created'), sortable: true, + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'semi-bold-12px logs-table__td', }, { key: 'timeStampOffset', label: this.$t('pagePostCodeLogs.table.timeStampOffset'), + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'logs-table__td', }, { key: 'bootCount', label: this.$t('pagePostCodeLogs.table.bootCount'), + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'logs-table__td', }, { key: 'postCode', label: this.$t('pagePostCodeLogs.table.postCode'), + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'logs-table__td semi-bold-12px__caps', }, { key: 'actions', label: '', - tdClass: 'text-right text-nowrap', + thClass: 'bold-12px__caps logs-table__th', + tdClass: 'text-right logs-table__td', }, ], expandRowLabel, @@ -345,3 +367,17 @@ export default { }, }; </script> +<style lang="scss" scoped> +.form-group, +.b-pagination { + margin: 0px !important; +} +.items-per-page { + padding: 0 0 0 5px; + width: 55px; + height: 24px; + background-color: transparent; + border: 1px solid $on-surface-tretiatry; + border-radius: 4px; +} +</style> diff --git a/src/views/_sila/MemoryModules/DynamicInfo/MemoryDynamicPage.vue b/src/views/_sila/MemoryModules/DynamicInfo/MemoryDynamicPage.vue new file mode 100644 index 00000000..489784e2 --- /dev/null +++ b/src/views/_sila/MemoryModules/DynamicInfo/MemoryDynamicPage.vue @@ -0,0 +1,224 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.analyticalPanel')" /> + <div class="notification__container"> + <span class="semi-bold-12px">{{ + $t('global.ariaLabel.notificationTime') + }}</span> + <div class="form-control notification"> + <b-form-input + v-model="notificationInput" + type="number" + class="notification__input" + > + </b-form-input> + <button class="notification_button"> + <img class="notification__icon" src="@/assets/images/refresh.svg" /> + </button> + </div> + </div> + <date-switch :switch-time-scale="switchTimeScale" :time-scale="timeScale" /> + <div class="limit-container"> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/non-normal.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.nonNormalMode') + }}</span> + <b-form-input + v-model="temperatureNonNormalInput" + type="number" + :min="0" + :max="temperatureCritical" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/critical.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.criticalMode') + }}</span> + <b-form-input + v-model="temperatureCritical" + type="number" + :min="temperatureNonNormalInput" + :max="temperatureWarningInput" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.temperatureWarning') + }}</span> + <b-form-input + v-model="temperatureWarningInput" + type="number" + :min="temperatureCritical" + :max="100" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button + class="save-button" + variant="primary" + @click="updateTemperatureLimits" + > + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Temperature Table --> + <temperature-table + :time-scale="timeScale" + :warning="temperatureWarning" + :non-normal="temperatureNonNormal" + :critical-start="temperatureCriticalStart" + /> + <accessory-table :records="accessoryData.temperatureTable" /> + </b-container> +</template> +<script> +import PageTitle from '@/components/Global/PageTitle'; +import DateSwitch from '@/components/Global/SilaComponents/DateSwitch'; +import TemperatureTable from './TemperatureTable'; +import AccessoryTable from '@/components/Global/SilaComponents/Tables/AccessoryTableWithLabel'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +import TableFilterMixin from '@/components/Mixins/TableFilterMixin'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; + +import { AccessoryData } from './helpers'; + +export default { + components: { + PageTitle, + DateSwitch, + TemperatureTable, + AccessoryTable, + }, + mixins: [TableFilterMixin, LoadingBarMixin], + data() { + return { + isBusy: true, + timeScale: 'hour', + temperatureWarning: 72, + temperatureWarningInput: 72, + temperatureNonNormal: 44, + temperatureNonNormalInput: 44, + temperatureCriticalStart: 55, + temperatureCritical: 55, + notificationInput: 42, + accessoryData: AccessoryData, + iconChevronUp: iconChevronUp, + activeFilters: [], + }; + }, + computed: { + allSensors() { + let sensors = this.$store.getters['sensors/memorySensors']; + return sensors; + }, + }, + created() { + this.startLoader(); + this.$store.dispatch('sensors/getMemorySensors').finally(() => { + this.endLoader(); + this.accessoryData.temperatureTable.items = this.getFilteredTableData( + this.allSensors, + this.activeFilters + ); + this.isBusy = false; + }); + }, + methods: { + switchTimeScale(period) { + this.timeScale = period; + }, + updateTemperatureLimits() { + this.temperatureWarning = +this.temperatureWarningInput; + this.temperatureNonNormal = +this.temperatureNonNormalInput; + this.temperatureCriticalStart = +this.temperatureCritical; + }, + }, +}; +</script> +<style lang="scss" scoped> +.notification__container { + position: absolute; + top: calc(#{$header-height}); + right: 0px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; +} + +.notification { + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; + border: none; + box-shadow: none; + border-radius: 8px; + margin: 12px 32px 12px 8px; + width: 236px; + height: 40px; + z-index: 1001; +} + +.notification__icon { + width: 20px; + height: 20px; +} + +.notification__input { + border: none; + background: none; + box-shadow: none; +} + +.notification_button { + border: none; + background: none; +} + +.semi-bold-12px { + z-index: 1001; +} + +// temperature limit comtainer +.limit-container { + height: 85px; + width: 100%; + padding: 0 32px 10px 32px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: flex-end; + gap: 24px; +} + +.trmperature-limt { + height: 60px; + width: 100%; + max-width: 270px; + display: flex; + align-items: baseline; + flex-flow: row wrap; + gap: 8px; +} + +.form-control { + height: 36px; +} +.save-button { + width: 151px; + height: 36px; +} +</style> diff --git a/src/views/_sila/MemoryModules/DynamicInfo/TemperatureTable.vue b/src/views/_sila/MemoryModules/DynamicInfo/TemperatureTable.vue new file mode 100644 index 00000000..f9c149c3 --- /dev/null +++ b/src/views/_sila/MemoryModules/DynamicInfo/TemperatureTable.vue @@ -0,0 +1,126 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 70, + }, + nonNormal: { + type: Number, + default: 70, + }, + criticalStart: { + type: Number, + default: 70, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, 'hour'), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'С°'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значение предупреждения, С°', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + plotBands: [ + { + color: '#F0AC0C1A', + dashStyle: 'solid', + from: this.nonNormal, + to: this.criticalStart, + }, + { + color: '#FF41411A', + dashStyle: 'solid', + from: this.criticalStart, + to: this.warning, + }, + ], + }, + series: Series['temperature'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/MemoryModules/DynamicInfo/helpers.js b/src/views/_sila/MemoryModules/DynamicInfo/helpers.js new file mode 100644 index 00000000..3cbca867 --- /dev/null +++ b/src/views/_sila/MemoryModules/DynamicInfo/helpers.js @@ -0,0 +1,458 @@ +export const colors = [ + '#CB32F1', + '#F18638', + '#139BB9', + '#E1AB17', + '#175AE1', + '#13B937', +]; + +export const Series = { + temperature: [ + { + name: 'Sean', + data: [ + 15, + 15, + 45, + 15, + 65, + 15, + 75, + 15, + 15, + 15, + 55, + 15, + 25, + 15, + 45, + 15, + 55, + 15, + 75, + 15, + 35, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 65, + 15, + 45, + 15, + 25, + 15, + 45, + 15, + 65, + 15, + 35, + 35, + 35, + 15, + 55, + 15, + 15, + 35, + 15, + 25, + 15, + 35, + 15, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 62, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 22, + 12, + 42, + 12, + 52, + 12, + 72, + 12, + 52, + 12, + 62, + 12, + 72, + 12, + 82, + 12, + 12, + 42, + 12, + 12, + 52, + 12, + 42, + 12, + 12, + 62, + 12, + 42, + 12, + 22, + 12, + 42, + 12, + 62, + 12, + 12, + 32, + 12, + 62, + 12, + 52, + 12, + 32, + 12, + 22, + 12, + 32, + 12, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 13, + 13, + 33, + 13, + 23, + 13, + 33, + 13, + 63, + 13, + 63, + 13, + 13, + 13, + 43, + 13, + 13, + 13, + 53, + 13, + 23, + 13, + 43, + 13, + 53, + 13, + 63, + 13, + 53, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 53, + 13, + 43, + 13, + 13, + 63, + 13, + 43, + 13, + 23, + 13, + 43, + 13, + 63, + 13, + 13, + 13, + 53, + 13, + 53, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 24, + 14, + 44, + 14, + 64, + 14, + 64, + 14, + 34, + 14, + 54, + 14, + 14, + 34, + 14, + 24, + 14, + 34, + 14, + 64, + 14, + 64, + 14, + 14, + 14, + 44, + 14, + 14, + 14, + 54, + 14, + 14, + 14, + 44, + 14, + 54, + 14, + 14, + 14, + 54, + 14, + 64, + 14, + 14, + 14, + 84, + 14, + 94, + 14, + 54, + 4, + 54, + 14, + 44, + 14, + 44, + 64, + 14, + 44, + 14, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 16, + 46, + 16, + 26, + 16, + 46, + 16, + 66, + 16, + 16, + 16, + 16, + 16, + 56, + 16, + 16, + 36, + 16, + 26, + 16, + 36, + 16, + 66, + 16, + 76, + 16, + 16, + 16, + 46, + 16, + 16, + 16, + 56, + 16, + 26, + 16, + 46, + 16, + 56, + 16, + 76, + 16, + 56, + 16, + 66, + 16, + 76, + 16, + 16, + 16, + 96, + 16, + 16, + 16, + 56, + 16, + 46, + 16, + 46, + 16, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 49, + 19, + 19, + 69, + 19, + 49, + 19, + 29, + 19, + 49, + 19, + 69, + 19, + 39, + 39, + 39, + 19, + 59, + 19, + 19, + 39, + 19, + 29, + 19, + 39, + 19, + 69, + 19, + 69, + 19, + 19, + 19, + 49, + 19, + 19, + 19, + 59, + 19, + 29, + 19, + 49, + 19, + 59, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + color: '#139BB9', + }, + ], +}; + +export const setTime = (count) => { + const arr = [...new Array(count)].map(() => ''); + for (let i = 0; i < arr.length; i++) { + arr[i] = `15:${String(i).padStart(2, '0')}`; + } + return arr; +}; + +export const setCategories = (count, desc) => { + const arr = [...new Array(count)].map((i, k) => `${k} ${desc}`); + return arr; +}; + +export const AccessoryData = { + temperatureTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentValue', + label: 'Текущее, С°', + }, + { + key: 'middleTemperature', + label: 'Среднее, С°', + }, + { + key: 'lowerCaution', + label: 'Минимальное, С°', + }, + { + key: 'minDate', + label: 'Дата минимального', + }, + { + key: 'upperCaution', + label: 'Максимальное, С°', + }, + { + key: 'maxDate', + label: 'Дата максимального', + }, + ], + items: [], + }, +}; diff --git a/src/views/_sila/MemoryModules/DynamicInfo/index.js b/src/views/_sila/MemoryModules/DynamicInfo/index.js new file mode 100644 index 00000000..b840772c --- /dev/null +++ b/src/views/_sila/MemoryModules/DynamicInfo/index.js @@ -0,0 +1,2 @@ +import MemoryDynamicPage from './MemoryDynamicPage.vue'; +export default MemoryDynamicPage; diff --git a/src/views/_sila/MemoryModules/Specification/MemoryStaticPage.vue b/src/views/_sila/MemoryModules/Specification/MemoryStaticPage.vue new file mode 100644 index 00000000..5e544868 --- /dev/null +++ b/src/views/_sila/MemoryModules/Specification/MemoryStaticPage.vue @@ -0,0 +1,228 @@ +<template> + <b-container + id="page-memory-specification" + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.specification')" /> + <page-section class="bootstrap-table__section info_section"> + <span class="bold-16px">{{ $t('global.table.info') }}</span> + <b-table + responsive="md" + show-empty + no-border-collapse + :items="items" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + </b-table> + <span class="bold-16px">{{ $t('global.table.memorySlots') }}</span> + </page-section> + <data-tabs + :slots="memorySlots" + :switch-tab="switchMemorySlot" + :current-tab="currentMemorySlot" + :slot-width="120" + :slider-width="97" + /> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + show-empty + no-border-collapse + :items="items_slots" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(value)="{ index, value }"> + <b-row v-if="index === 0"> + <b-col cols="11"> + <span> + {{ 'Работоспособность' }} + </span> + </b-col> + <b-col cols="1"> + <img src="@/assets/images/status/on.svg" class="icon-chevron" /> + </b-col> + </b-row> + <b-row v-else> + <b-col> + <span>{{ value }}</span> + </b-col> + </b-row> + </template> + </b-table> + </page-section> + <page-section class="bootstrap-table__section"> + <span class="bold-16px">{{ $t('global.table.memoryModules') }}</span> + <b-table + responsive="md" + show-empty + no-border-collapse + :items="items_modules" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(value)="{ index, value }"> + <b-row v-if="index === 0"> + <b-col cols="11"> + <span> + {{ 'Работоспособность' }} + </span> + </b-col> + <b-col cols="1"> + <img src="@/assets/images/status/on.svg" class="icon-chevron" /> + </b-col> + </b-row> + <b-row v-else> + <b-col> + <span>{{ value }}</span> + </b-col> + </b-row> + </template> + </b-table> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import DataTabs from '@/components/Global/SilaComponents/DataTabs'; + +export default { + components: { PageTitle, PageSection, DataTabs }, + data() { + return { + currentMemorySlot: 1, + memorySlots: [ + { id: 1, name: 'CPU_1-DIMM_A1' }, + { id: 2, name: 'CPU_1-DIMM_A2' }, + { id: 3, name: 'CPU_1-DIMM_A3' }, + { id: 4, name: 'CPU_1-DIMM_B1' }, + { id: 5, name: 'CPU_1-DIMM_B2' }, + { id: 6, name: 'CPU_1-DIMM_B3' }, + { id: 7, name: 'CPU_1-DIMM_C1' }, + { id: 8, name: 'CPU_1-DIMM_C2' }, + { id: 9, name: 'CPU_1-DIMM_C3' }, + { id: 10, name: 'CPU_1-DIMM_D1' }, + { id: 11, name: 'CPU_1-DIMM_D2' }, + { id: 12, name: 'CPU_1-DIMM_D3' }, + { id: 13, name: 'CPU_1-DIMM_E1' }, + { id: 14, name: 'CPU_1-DIMM_E2' }, + { id: 15, name: 'CPU_1-DIMM_E3' }, + ], + fields: [ + { + key: 'parametr', + label: 'Статус', + formatter: this.dataFormatter, + thStyle: { width: '50%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + items: [ + { + parametr: 'Установленный объем памяти', + value: '8096 мб', + }, + { + parametr: 'Максимальный объем', + value: '4048 мб', + }, + { + parametr: 'Общее количество слотов', + value: '10', + }, + { + parametr: 'Используемое количество слотов', + value: '10', + }, + ], + items_slots: [ + { + parametr: 'Статус', + value: true, + }, + { + parametr: 'Имя слота', + value: 'Cлот 2', + }, + { + parametr: 'Технология', + value: 'Название технологии', + }, + { + parametr: 'Тип памяти', + value: 'Оперативная', + }, + { + parametr: 'Объем', + value: '2024', + }, + { + parametr: 'Состояние', + value: 'Отлично', + }, + { + parametr: 'Класс', + value: '1', + }, + { + parametr: 'Скорость', + value: '2000 МТ/сек', + }, + ], + items_modules: [ + { + parametr: 'Статус', + value: true, + }, + { + parametr: 'Имя коннектора', + value: 'DIMM A2', + }, + { + parametr: 'Тип памяти', + value: 'DDR-4', + }, + { + parametr: 'Объем', + value: '32 GB', + }, + { + parametr: 'Состояние', + value: 'Presence Detected', + }, + { + parametr: 'Ранг', + value: 'Dual Rank', + }, + { + parametr: 'Скорость', + value: '2400 MHz', + }, + ], + }; + }, + methods: { + switchMemorySlot(period) { + this.currentMemorySlot = period; + }, + }, +}; +</script> +<style lang="scss" scoped> +.info_section { + margin-bottom: 0px; +} + +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} +</style> diff --git a/src/views/_sila/MemoryModules/Specification/index.js b/src/views/_sila/MemoryModules/Specification/index.js new file mode 100644 index 00000000..4916f58a --- /dev/null +++ b/src/views/_sila/MemoryModules/Specification/index.js @@ -0,0 +1,2 @@ +import MemoryStaticPage from './MemoryStaticPage.vue'; +export default MemoryStaticPage; diff --git a/src/views/_sila/Motherboard/DynamicInfo/MotherboardDynamicPage.vue b/src/views/_sila/Motherboard/DynamicInfo/MotherboardDynamicPage.vue new file mode 100644 index 00000000..c2c88ab6 --- /dev/null +++ b/src/views/_sila/Motherboard/DynamicInfo/MotherboardDynamicPage.vue @@ -0,0 +1,159 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title /> + <date-switch :switch-time-scale="switchTimeScale" :time-scale="timeScale" /> + <div class="limit-container"> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.temperatureWarning') + }}</span> + <b-form-input + v-model="temperatureWarningInput" + type="number" + :min="0" + :max="100" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button + class="save-button" + variant="primary" + @click="updateTemperature" + > + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Temperature Table --> + <temperature-table + :time-scale="timeScale" + :warning="temperatureWarning" + :non-normal="temperatureNonNormal" + :critical-start="temperatureCriticalStart" + /> + <accessory-table :records="accessoryData.temperatureTable" /> + </b-container> +</template> +<script> +import PageTitle from '@/components/Global/PageTitle'; +import DateSwitch from '@/components/Global/SilaComponents/DateSwitch'; +import TemperatureTable from './TemperatureTable'; +import AccessoryTable from '@/components/Global/SilaComponents/Tables/AccessoryTableWithLabel'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +import TableFilterMixin from '@/components/Mixins/TableFilterMixin'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; + +import { AccessoryData } from './helpers'; + +export default { + components: { + PageTitle, + DateSwitch, + TemperatureTable, + AccessoryTable, + }, + mixins: [TableFilterMixin, LoadingBarMixin], + data() { + return { + isBusy: true, + timeScale: 'hour', + temperatureWarning: 72, + temperatureWarningInput: 72, + notificationInput: 42, + accessoryData: AccessoryData, + iconChevronUp: iconChevronUp, + activeFilters: [], + }; + }, + + computed: { + allSensors() { + let sensors = this.$store.getters['sensors/tempSensors']; + return sensors; + }, + }, + created() { + this.startLoader(); + this.$store.dispatch('sensors/getTempSensors').finally(() => { + this.endLoader(); + this.accessoryData.temperatureTable.items = this.getFilteredTableData( + this.allSensors, + this.activeFilters + ); + this.isBusy = false; + }); + }, + methods: { + switchTimeScale(period) { + this.timeScale = period; + }, + updateTemperature() { + this.temperatureWarning = +this.temperatureWarningInput; + }, + }, +}; +</script> +<style lang="scss" scoped> +//nav items style +.nav-item, +.nav-link { + padding: 0; +} +.nav-item { + list-style-type: none; +} + +a { + color: $text-primary !important; + &:hover { + color: $text-primary !important; + } +} + +.notification-button { + border: none; + background: none; +} + +.semi-bold-12px { + z-index: 1001; +} + +// temperature limit comtainer +.limit-container { + height: 85px; + width: 100%; + padding: 0 32px 10px 32px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: flex-end; + gap: 24px; +} + +.trmperature-limt { + height: 60px; + width: 100%; + max-width: 270px; + display: flex; + align-items: baseline; + flex-flow: row wrap; + gap: 8px; +} + +.form-control { + height: 36px; + &.non-normal { + width: 125px; + } +} +.save-button { + width: 151px; + height: 36px; +} +</style> diff --git a/src/views/_sila/Motherboard/DynamicInfo/TemperatureTable.vue b/src/views/_sila/Motherboard/DynamicInfo/TemperatureTable.vue new file mode 100644 index 00000000..1bbf7e08 --- /dev/null +++ b/src/views/_sila/Motherboard/DynamicInfo/TemperatureTable.vue @@ -0,0 +1,112 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 70, + }, + nonNormal: { + type: Number, + default: 70, + }, + criticalStart: { + type: Number, + default: 70, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, 'hour'), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'С°'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значение предупреждения, С°', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + }, + series: Series['temperature'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/Motherboard/DynamicInfo/helpers.js b/src/views/_sila/Motherboard/DynamicInfo/helpers.js new file mode 100644 index 00000000..890482ee --- /dev/null +++ b/src/views/_sila/Motherboard/DynamicInfo/helpers.js @@ -0,0 +1,458 @@ +export const colors = [ + '#CB32F1', + '#F18638', + '#139BB9', + '#E1AB17', + '#175AE1', + '#13B937', +]; + +export const Series = { + temperature: [ + { + name: 'Sean', + data: [ + 7, + 7, + 7, + 17, + 7, + 27, + 7, + 7, + 47, + 7, + 7, + 7, + 37, + 7, + 7, + 7, + 27, + 7, + 7, + 7, + 27, + 7, + 7, + 7, + 47, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 27, + 7, + 7, + 7, + 7, + 7, + 27, + 7, + 7, + 7, + 7, + 7, + 7, + 37, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 20, + 10, + 10, + 30, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 50, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 20, + 10, + 10, + 10, + 10, + 10, + 30, + 10, + 10, + 10, + 10, + 10, + 10, + 19, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 13, + 13, + 13, + 13, + 23, + 13, + 33, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 23, + 13, + 43, + 13, + 53, + 13, + 13, + 13, + 53, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 53, + 13, + 43, + 13, + 13, + 63, + 13, + 43, + 13, + 23, + 13, + 43, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 24, + 14, + 44, + 14, + 14, + 14, + 14, + 14, + 34, + 14, + 14, + 14, + 14, + 34, + 14, + 24, + 14, + 34, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 44, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 44, + 14, + 54, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 84, + 14, + 94, + 14, + 14, + 14, + 54, + 14, + 44, + 14, + 44, + 14, + 14, + 44, + 14, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 16, + 16, + 16, + 26, + 16, + 46, + 16, + 66, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 26, + 16, + 36, + 16, + 66, + 16, + 76, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 26, + 16, + 16, + 16, + 56, + 16, + 16, + 16, + 56, + 16, + 66, + 16, + 76, + 16, + 16, + 16, + 96, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 29, + 19, + 19, + 19, + 19, + 19, + 39, + 39, + 39, + 19, + 59, + 19, + 19, + 39, + 19, + 29, + 19, + 39, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 29, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + color: '#139BB9', + }, + ], +}; + +export const setTime = (count) => { + const arr = [...new Array(count)].map(() => ''); + for (let i = 0; i < arr.length; i++) { + arr[i] = `15:${String(i).padStart(2, '0')}`; + } + return arr; +}; + +export const setCategories = (count, desc) => { + const arr = [...new Array(count)].map((i, k) => `${k} ${desc}`); + return arr; +}; + +export const AccessoryData = { + temperatureTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentValue', + label: 'Текущее, С°', + }, + { + key: 'middleTemperature', + label: 'Среднее, С°', + }, + { + key: 'lowerCaution', + label: 'Минимальное, С°', + }, + { + key: 'minDate', + label: 'Дата минимального', + }, + { + key: 'upperCaution', + label: 'Максимальное, С°', + }, + { + key: 'maxDate', + label: 'Дата максимального', + }, + ], + items: [], + }, +}; diff --git a/src/views/_sila/Motherboard/DynamicInfo/index.js b/src/views/_sila/Motherboard/DynamicInfo/index.js new file mode 100644 index 00000000..bd155997 --- /dev/null +++ b/src/views/_sila/Motherboard/DynamicInfo/index.js @@ -0,0 +1,2 @@ +import MotherboardDynamicPage from './MotherboardDynamicPage.vue'; +export default MotherboardDynamicPage; diff --git a/src/views/_sila/Motherboard/Specification/MotherboardSpecificationPage.vue b/src/views/_sila/Motherboard/Specification/MotherboardSpecificationPage.vue new file mode 100644 index 00000000..56586dcf --- /dev/null +++ b/src/views/_sila/Motherboard/Specification/MotherboardSpecificationPage.vue @@ -0,0 +1,83 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.specification')" /> + <page-section class="bootstrap-table__section"> + <span class="bold-16px">{{ $t('global.table.info') }}</span> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + </b-table> + <span class="bold-16px">{{ $t('appNavigation.motherboardParam') }}</span> + <popover + id="popover-reactive-1" + description="SystemDescription.onBootRom" + popup="SystemDescription.onBootRom_popup" + button="global.action.refresh" + /> + <popover + id="popover-reactive-2" + description="SystemDescription.offBootRom" + popup="SystemDescription.offBootRom_popup" + button="global.action.refresh" + /> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; + +import Popover from '@/components/Global/Popover'; + +export default { + components: { PageTitle, PageSection, Popover }, + data() { + return { + fields: [ + { + key: 'parametr', + label: 'Параметр', + formatter: this.dataFormatter, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + items: [ + { + parametr: 'Модель', + value: 'R2132', + }, + { + parametr: 'Производитель', + value: 'Asus', + }, + { + parametr: 'Версия', + value: '4.2', + }, + { + parametr: 'Серийный номер', + value: '231232132133', + }, + { + parametr: 'Версия BIOS', + value: '2', + }, + ], + }; + }, +}; +</script> diff --git a/src/views/_sila/Motherboard/Specification/index.js b/src/views/_sila/Motherboard/Specification/index.js new file mode 100644 index 00000000..5458039a --- /dev/null +++ b/src/views/_sila/Motherboard/Specification/index.js @@ -0,0 +1,2 @@ +import MotherboardSpecificationPage from './MotherboardSpecificationPage.vue'; +export default MotherboardSpecificationPage; diff --git a/src/views/_sila/Operations/ConsoleSettings/ConsoleSettings.vue b/src/views/_sila/Operations/ConsoleSettings/ConsoleSettings.vue new file mode 100644 index 00000000..ecc2150f --- /dev/null +++ b/src/views/_sila/Operations/ConsoleSettings/ConsoleSettings.vue @@ -0,0 +1,154 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title /> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="systems" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(value)="data"> + <b-row v-if="typeof data.value === 'boolean'"> + <b-col> + <span v-if="systems[data.index].value"> + {{ $t('global.status.enabled') }} + </span> + <span v-else> + {{ $t('global.status.disabled') }} + </span> + </b-col> + <b-col> + <b-form-checkbox v-model="systems[data.index].value" switch> + </b-form-checkbox> + </b-col> + </b-row> + <b-row + v-else-if="data.index === 1 || data.index === 6 || data.index === 8" + > + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + <b-col> + <img :is="iconChevron" class="icon-chevron" /> + </b-col> + </b-row> + <b-row v-else-if="data.index === 3"> + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + <b-col> + <img src="@/assets/images/icon-edit.svg" class="icon-chevron" /> + </b-col> + </b-row> + <b-row v-else> + <span>{{ data.value }}</span></b-row + > + </template> + </b-table> + <div class="save-button"> + <b-button variant="primary" class="console-settings__save-button"> + {{ $t('global.action.saveChanges') }} + </b-button> + </div> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import iconChevron from '@carbon/icons-vue/es/chevron--down/16'; + +export default { + components: { + PageTitle, + PageSection, + }, + mixins: [BVToastMixin, TableRowExpandMixin], + data() { + return { + text: '', + isBusy: true, + fields: [ + { + key: 'attributes', + label: 'Атрибуты', + formatter: this.dataFormatter, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + thStyle: { width: '30%' }, + }, + ], + iconChevron, + expandRowLabel: expandRowLabel, + systems: [ + { attributes: 'Состояние', value: true }, + { attributes: 'Максимальное количество сеансов', value: '6' }, + { attributes: 'Активные сеансы', value: '0' }, + { attributes: 'Порт удаленного доступа', value: '5900' }, + { attributes: 'Статус шифрования видео', value: true }, + { attributes: 'Видео с локального сервера', value: true }, + { + attributes: + 'Действие по умолчанию при истечении времени ожидания запроса на общий доступ к сеансу', + value: 'Полный доступ', + }, + { + attributes: 'Автоматическая блокировка системы', + value: false, + }, + { + attributes: 'Состояние подключения клавиатуры/мыши', + value: 'Автоматическое', + }, + ], + // iconChevronUp: iconChevronUp, + }; + }, +}; +</script> +<style lang="scss" scoped> +.row { + margin: 0px; + height: 15px; + flex-wrap: nowrap; + align-items: center; +} +.col { + padding: 0; +} + +.icon-chevron { + margin: 0 0 0 85%; + cursor: pointer; +} + +.save-button { + display: flex; + justify-content: flex-end; +} + +.console-settings__save-button { + width: 241px; + height: 36px; + margin-right: 0.5rem; +} +</style> diff --git a/src/views/_sila/Operations/ConsoleSettings/index.js b/src/views/_sila/Operations/ConsoleSettings/index.js new file mode 100644 index 00000000..860ee595 --- /dev/null +++ b/src/views/_sila/Operations/ConsoleSettings/index.js @@ -0,0 +1,2 @@ +import ConsoleSettings from './ConsoleSettings.vue'; +export default ConsoleSettings; diff --git a/src/views/_sila/Operations/FactoryReset/FactoryReset.vue b/src/views/_sila/Operations/FactoryReset/FactoryReset.vue index 4e315619..d1ab187e 100644 --- a/src/views/_sila/Operations/FactoryReset/FactoryReset.vue +++ b/src/views/_sila/Operations/FactoryReset/FactoryReset.vue @@ -1,12 +1,19 @@ <template> - <b-container fluid="xl"> + <b-container fluid class="m-0 p-0"> <page-title :description="$t('pageFactoryReset.description')" /> <!-- Reset Form --> - <b-form id="factory-reset" @submit.prevent="onResetSubmit"> + <b-form + id="factory-reset" + class="bootstrap-table__section" + @submit.prevent="onResetSubmit" + > <b-row> <b-col md="8"> - <b-form-group :label="$t('pageFactoryReset.form.resetOptionsLabel')"> + <b-form-group + :label="$t('pageFactoryReset.form.resetOptionsLabel')" + label-class="semi-bold-16px" + > <b-form-radio-group id="factory-reset-options" v-model="resetOption" diff --git a/src/views/_sila/Operations/Firmware/Firmware.vue b/src/views/_sila/Operations/Firmware/Firmware.vue index 0497376d..c4006b7f 100644 --- a/src/views/_sila/Operations/Firmware/Firmware.vue +++ b/src/views/_sila/Operations/Firmware/Firmware.vue @@ -1,5 +1,5 @@ <template> - <b-container fluid="xl"> + <b-container fluid class="m-0 p-0"> <page-title /> <alerts-server-power v-if="isServerPowerOffRequired" @@ -7,7 +7,7 @@ /> <!-- Firmware cards --> - <b-row> + <b-row class="bootstrap-table__section"> <b-col xl="10"> <!-- BMC Firmware --> <bmc-cards :is-page-disabled="isPageDisabled" /> @@ -19,6 +19,7 @@ <!-- Update firmware--> <page-section + class="bootstrap-table__section" :section-title="$t('pageFirmware.sectionTitleUpdateFirmware')" > <b-row> @@ -39,12 +40,10 @@ import AlertsServerPower from './FirmwareAlertServerPower'; import BmcCards from './FirmwareCardsBmc'; import FormUpdate from './FirmwareFormUpdate'; import HostCards from './FirmwareCardsHost'; -import PageSection from '@/components/_sila/Global/PageSection'; -import PageTitle from '@/components/_sila/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import PageTitle from '@/components/Global/PageTitle'; -import LoadingBarMixin, { - loading, -} from '@/components/_sila/Mixins/LoadingBarMixin'; +import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; export default { name: 'FirmwareSingleImage', diff --git a/src/views/_sila/Operations/Firmware/FirmwareAlertServerPower.vue b/src/views/_sila/Operations/Firmware/FirmwareAlertServerPower.vue index 471ccfac..24aa1d69 100644 --- a/src/views/_sila/Operations/Firmware/FirmwareAlertServerPower.vue +++ b/src/views/_sila/Operations/Firmware/FirmwareAlertServerPower.vue @@ -31,7 +31,7 @@ </template> <script> -import Alert from '@/components/_sila/Global/Alert'; +import Alert from '@/components/Global/Alert'; export default { components: { Alert }, diff --git a/src/views/_sila/Operations/Firmware/FirmwareCardsBmc.vue b/src/views/_sila/Operations/Firmware/FirmwareCardsBmc.vue index 23c263d9..d79a8769 100644 --- a/src/views/_sila/Operations/Firmware/FirmwareCardsBmc.vue +++ b/src/views/_sila/Operations/Firmware/FirmwareCardsBmc.vue @@ -53,11 +53,9 @@ <script> import IconSwitch from '@carbon/icons-vue/es/arrows--horizontal/20'; -import PageSection from '@/components/_sila/Global/PageSection'; -import LoadingBarMixin, { - loading, -} from '@/components/_sila/Mixins/LoadingBarMixin'; -import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; +import PageSection from '@/components/Global/PageSection'; +import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; import ModalSwitchToRunning from './FirmwareModalSwitchToRunning'; diff --git a/src/views/_sila/Operations/Firmware/FirmwareCardsHost.vue b/src/views/_sila/Operations/Firmware/FirmwareCardsHost.vue index 03a25ee5..b4a8e90d 100644 --- a/src/views/_sila/Operations/Firmware/FirmwareCardsHost.vue +++ b/src/views/_sila/Operations/Firmware/FirmwareCardsHost.vue @@ -37,7 +37,7 @@ </template> <script> -import PageSection from '@/components/_sila/Global/PageSection'; +import PageSection from '@/components/Global/PageSection'; export default { components: { PageSection }, diff --git a/src/views/_sila/Operations/Firmware/FirmwareFormUpdate.vue b/src/views/_sila/Operations/Firmware/FirmwareFormUpdate.vue index 23fe90f2..a5e5ba97 100644 --- a/src/views/_sila/Operations/Firmware/FirmwareFormUpdate.vue +++ b/src/views/_sila/Operations/Firmware/FirmwareFormUpdate.vue @@ -59,6 +59,7 @@ <b-btn data-test-id="firmware-button-startUpdate" type="submit" + size="md" variant="primary" :disabled="isPageDisabled" > @@ -75,13 +76,11 @@ <script> import { requiredIf } from 'vuelidate/lib/validators'; -import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; -import LoadingBarMixin, { - loading, -} from '@/components/_sila/Mixins/LoadingBarMixin'; -import VuelidateMixin from '@/components/_sila/Mixins/VuelidateMixin.js'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; -import FormFile from '@/components/_sila/Global/FormFile'; +import FormFile from '@/components/Global/FormFile'; import ModalUpdateFirmware from './FirmwareModalUpdateFirmware'; export default { diff --git a/src/views/_sila/Operations/KeyClear/KeyClear.vue b/src/views/_sila/Operations/KeyClear/KeyClear.vue index 8955f6cd..fd6468fa 100644 --- a/src/views/_sila/Operations/KeyClear/KeyClear.vue +++ b/src/views/_sila/Operations/KeyClear/KeyClear.vue @@ -1,9 +1,9 @@ <template> - <b-container fluid="xl"> + <b-container fluid class="m-0 p-0"> <page-title :description="$t('pageKeyClear.description')" /> - <b-row> + <b-row class="bootstrap-table__section"> <b-col md="8" xl="6"> - <alert variant="info" class="mb-4"> + <alert variant="info" class="mb-0"> <div class="font-weight-bold"> {{ $t('pageKeyClear.alert.title') }} </div> @@ -14,10 +14,17 @@ </b-col> </b-row> <!-- Reset Form --> - <b-form id="key-clear" @submit.prevent="onKeyClearSubmit(keyOption)"> + <b-form + id="key-clear" + class="bootstrap-table__section" + @submit.prevent="onKeyClearSubmit(keyOption)" + > <b-row> <b-col md="8"> - <b-form-group :label="$t('pageKeyClear.form.keyClearOptionsLabel')"> + <b-form-group + :label="$t('pageKeyClear.form.keyClearOptionsLabel')" + label-class="semi-bold-16px" + > <b-form-radio-group id="key-clear-options" v-model="keyOption" diff --git a/src/views/_sila/Operations/Kvm/Kvm.vue b/src/views/_sila/Operations/Kvm/Kvm.vue index ede24608..a3103453 100644 --- a/src/views/_sila/Operations/Kvm/Kvm.vue +++ b/src/views/_sila/Operations/Kvm/Kvm.vue @@ -1,5 +1,5 @@ <template> - <b-container fluid="xl"> + <b-container fluid> <page-title /> <div class="terminal-container"> <kvm-console :is-full-window="false" /> @@ -8,7 +8,7 @@ </template> <script> -import PageTitle from '@/components/_sila/Global/PageTitle'; +import PageTitle from '@/components/Global/PageTitle'; import KvmConsole from './KvmConsole'; export default { diff --git a/src/views/_sila/Operations/Kvm/KvmConsole.vue b/src/views/_sila/Operations/Kvm/KvmConsole.vue index 50cbff7f..d06e2824 100644 --- a/src/views/_sila/Operations/Kvm/KvmConsole.vue +++ b/src/views/_sila/Operations/Kvm/KvmConsole.vue @@ -1,8 +1,12 @@ <template> <div :class="marginClass"> <div ref="toolbar" class="kvm-toolbar"> - <b-row class="d-flex"> - <b-col class="d-flex flex-column justify-content-end" cols="4"> + <b-row class="d-flex flex-column flex-sm-row"> + <b-col + class="d-flex flex-column justify-content-end console-title" + style="min-width: 105px" + cols="3" + > <dl class="mb-2" sm="2" md="2"> <dt class="d-inline font-weight-bold mr-1"> {{ $t('pageKvm.status') }}: @@ -14,9 +18,10 @@ </dl> </b-col> - <b-col class="d-flex justify-content-end pr-1"> + <b-col class="d-flex justify-content-end pr-1 flex-column flex-sm-row"> <b-button v-if="isConnected" + class="console-title console-button" variant="link" type="button" @click="sendCtrlAltDel" @@ -26,6 +31,7 @@ </b-button> <b-button v-if="!isFullWindow" + class="console-title console-button" variant="link" type="button" @click="openConsoleWindow()" @@ -42,7 +48,7 @@ <script> import RFB from '@novnc/novnc/core/rfb'; -import StatusIcon from '@/components/_sila/Global/StatusIcon'; +import StatusIcon from '@/components/Global/StatusIcon'; import IconLaunch from '@carbon/icons-vue/es/launch/20'; import IconArrowDown from '@carbon/icons-vue/es/arrow--down/16'; import { throttle } from 'lodash'; @@ -167,4 +173,16 @@ export default { .margin-left-full-window { margin-left: 5px; } + +@media (max-width: 1200px) { + .console-title { + font-size: 0.7rem; + } +} + +@media (max-width: 576px) { + .console-button { + justify-content: flex-end; + } +} </style> diff --git a/src/views/_sila/Operations/RebootBmc/RebootBmc.vue b/src/views/_sila/Operations/RebootBmc/RebootBmc.vue index fa16f55e..71b9b123 100644 --- a/src/views/_sila/Operations/RebootBmc/RebootBmc.vue +++ b/src/views/_sila/Operations/RebootBmc/RebootBmc.vue @@ -1,7 +1,7 @@ <template> - <b-container fluid="xl"> + <b-container fluid class="m-0 p-0"> <page-title /> - <b-row> + <b-row class="bootstrap-table__section"> <b-col md="8" lg="8" xl="6"> <page-section> <b-row> @@ -19,14 +19,22 @@ </b-col> </b-row> {{ $t('pageRebootBmc.rebootInformation') }} - <b-button - variant="primary" - class="d-block mt-5" - data-test-id="rebootBmc-button-reboot" - @click="onClick" + <popover-with-slot + id="popover-reboot" + :button-label="$t('global.action.reload')" + :popup-label="$t('BMC.ReloadBmc_popup')" + placement="left" + :action="rebootBmc" > - {{ $t('pageRebootBmc.rebootBmc') }} - </b-button> + <b-button + id="popover-reboot" + variant="primary" + class="d-block mt-5" + data-test-id="rebootBmc-button-reboot" + > + {{ $t('pageRebootBmc.rebootBmc') }} + </b-button> + </popover-with-slot> </page-section> </b-col> </b-row> @@ -38,10 +46,11 @@ import PageTitle from '@/components/_sila/Global/PageTitle'; import PageSection from '@/components/_sila/Global/PageSection'; import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; import LoadingBarMixin from '@/components/_sila/Mixins/LoadingBarMixin'; +import PopoverWithSlot from '@/components/_sila/Global/SilaComponents/PopoverWithSlot'; export default { name: 'RebootBmc', - components: { PageTitle, PageSection }, + components: { PageTitle, PageSection, PopoverWithSlot }, mixins: [BVToastMixin, LoadingBarMixin], beforeRouteLeave(to, from, next) { this.hideLoader(); @@ -59,17 +68,6 @@ export default { .finally(() => this.endLoader()); }, methods: { - onClick() { - this.$bvModal - .msgBoxConfirm(this.$t('pageRebootBmc.modal.confirmMessage'), { - title: this.$t('pageRebootBmc.modal.confirmTitle'), - okTitle: this.$t('global.action.confirm'), - cancelTitle: this.$t('global.action.cancel'), - }) - .then((confirmed) => { - if (confirmed) this.rebootBmc(); - }); - }, rebootBmc() { this.$store .dispatch('controls/rebootBmc') diff --git a/src/views/_sila/Operations/SerialOverLan/SerialOverLanConsole.vue b/src/views/_sila/Operations/SerialOverLan/SerialOverLanConsole.vue index bf974b51..2a9a2f4a 100644 --- a/src/views/_sila/Operations/SerialOverLan/SerialOverLanConsole.vue +++ b/src/views/_sila/Operations/SerialOverLan/SerialOverLanConsole.vue @@ -9,7 +9,7 @@ class="mt-4" > <p class="col-form-label"> - {{ $t('pageSerialOverLan.alert.disconnectedAlertMessage') }} + {{ $t('pageSerialOverLan.disconnectedAlertMessage') }} </p> </alert> </b-col> diff --git a/src/views/_sila/Operations/ServerPowerOperations/BootSettings.vue b/src/views/_sila/Operations/ServerPowerOperations/BootSettings.vue index 8d74e381..7f56c36a 100644 --- a/src/views/_sila/Operations/ServerPowerOperations/BootSettings.vue +++ b/src/views/_sila/Operations/ServerPowerOperations/BootSettings.vue @@ -1,16 +1,14 @@ <template> - <div class="form-background p-3"> + <page-section class="m-0"> <b-form novalidate @submit.prevent="handleSubmit"> - <b-form-group - :label=" + <b-form-group label-for="boot-option" class="mb-3 regular-12px"> + <label class="semi-bold-12px">{{ $t('pageServerPowerOperations.bootSettings.bootSettingsOverride') - " - label-for="boot-option" - class="mb-3" - > + }}</label> <b-form-select id="boot-option" v-model="form.bootOption" + class="boot-select regular-14px" :disabled="bootSourceOptions.length === 0" :options="bootSourceOptions" @change="onChangeSelect" @@ -19,43 +17,47 @@ </b-form-group> <b-form-checkbox v-model="form.oneTimeBoot" - class="mb-4" + class="mb-4 regular-12px cb" :disabled="form.bootOption === 'None'" @change="$v.form.oneTimeBoot.$touch()" > {{ $t('pageServerPowerOperations.bootSettings.enableOneTimeBoot') }} </b-form-checkbox> - <b-form-group - :label="$t('pageServerPowerOperations.bootSettings.tpmRequiredPolicy')" - > - <b-form-text id="tpm-required-policy-help-block"> + <b-form-group> + <label class="semi-bold-12px">{{ + $t('pageServerPowerOperations.bootSettings.tpmRequiredPolicy') + }}</label> + <label id="tpm-required-policy-help-block" class="regular-12px"> {{ $t('pageServerPowerOperations.bootSettings.tpmRequiredPolicyHelper') }} - </b-form-text> + </label> <b-form-checkbox id="tpm-required-policy" v-model="form.tpmPolicyOn" + class="regular-12px cb" aria-describedby="tpm-required-policy-help-block" @change="$v.form.tpmPolicyOn.$touch()" > {{ $t('global.status.enabled') }} </b-form-checkbox> </b-form-group> - <b-button variant="primary" type="submit" class="mb-3"> + <b-button variant="primary" size="md" type="submit" class="mb-3"> {{ $t('global.action.save') }} </b-button> </b-form> - </div> + </page-section> </template> <script> import { mapState } from 'vuex'; import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; import LoadingBarMixin from '@/components/_sila/Mixins/LoadingBarMixin'; +import PageSection from '@/components/_sila/Global/PageSection'; export default { name: 'BootSettings', + components: { PageSection }, mixins: [BVToastMixin, LoadingBarMixin], data() { return { @@ -130,3 +132,12 @@ export default { }, }; </script> +<style lang="scss" scoped> +.boot-select { + height: 40px; + max-width: 270px; + border: none; + border-radius: 8px; + background-image: url('../../../../assets/images/_sila/icon-chevron.svg'); +} +</style> diff --git a/src/views/_sila/Operations/ServerPowerOperations/ServerPowerOperations.vue b/src/views/_sila/Operations/ServerPowerOperations/ServerPowerOperations.vue index e848215f..1ba90b83 100644 --- a/src/views/_sila/Operations/ServerPowerOperations/ServerPowerOperations.vue +++ b/src/views/_sila/Operations/ServerPowerOperations/ServerPowerOperations.vue @@ -1,28 +1,40 @@ <template> - <b-container fluid="xl"> + <b-container fluid="xxl pt-0 m-0"> <page-title /> - <b-row class="mb-4"> + <page-section + :section-title="$t('pageServerPowerOperations.serverStatus')" + class="m-4" + > <b-col md="8" xl="6"> - <page-section - :section-title="$t('pageServerPowerOperations.currentStatus')" - > + <page-section class="pt-2 mb-0"> <b-row> <b-col> <dl> - <dt>{{ $t('pageServerPowerOperations.serverStatus') }}</dt> <dd v-if="serverStatus === 'on'" + style="margin-top: 10px" + class="regular-12px" data-test-id="powerServerOps-text-hostStatus" > - {{ $t('global.status.on') }} + <img + style="margin-right: 5px" + src="@/assets/images/_sila/status/on.svg" + /> + {{ $t('global.status.on_full') }} </dd> <dd v-else-if="serverStatus === 'off'" + style="margin-top: 10px" + class="regular-12px" data-test-id="powerServerOps-text-hostStatus" > - {{ $t('global.status.off') }} + <img + style="margin-right: 5px" + src="@/assets/images/_sila/status/off.svg" + /> + {{ $t('global.status.off_full') }} </dd> - <dd v-else> + <dd v-else class="regular-12px"> {{ $t('global.status.notAvailable') }} </dd> </dl> @@ -31,11 +43,12 @@ <b-row> <b-col> <dl> - <dt> + <dt class="semi-bold-12px"> {{ $t('pageServerPowerOperations.lastPowerOperation') }} </dt> <dd v-if="lastPowerOperationTime" + class="regular-12px" data-test-id="powerServerOps-text-lastPowerOp" > {{ lastPowerOperationTime | formatDate }} @@ -47,19 +60,13 @@ </b-row> </page-section> </b-col> - </b-row> - <b-row> - <b-col v-if="hasBootSourceOptions" sm="8" md="6" xl="4"> - <page-section - :section-title="$t('pageServerPowerOperations.serverBootSettings')" - > - <boot-settings /> - </page-section> - </b-col> + </page-section> + <page-section + :section-title="$t('SystemDescription.title.Control')" + class="ml-4 mb-0" + > <b-col sm="8" md="6" xl="7"> - <page-section - :section-title="$t('pageServerPowerOperations.operations')" - > + <page-section> <alert :show="oneTimeBootEnabled" variant="warning"> {{ $t('pageServerPowerOperations.oneTimeBootWarning') }} </alert> @@ -69,20 +76,30 @@ </alert> </template> <template v-else-if="serverStatus === 'off'"> - <b-button - variant="primary" - data-test-id="serverPowerOperations-button-powerOn" - @click="powerOn" + <popover-with-slot + id="popover-powerOn" + :button-label="$t('pageServerPowerOperations.powerOn')" + :popup-label="$t('pageServerPowerOperations.powerOnServer')" + placement="right" + :action="powerOn" > - {{ $t('pageServerPowerOperations.powerOn') }} - </b-button> + <b-button + id="popover-powerOn" + ref="button" + size="md" + variant="primary" + > + {{ $t('pageServerPowerOperations.powerOn') }} + </b-button> + </popover-with-slot> </template> <template v-else> <!-- Reboot server options --> - <b-form novalidate class="mb-5" @submit.prevent="rebootServer"> - <b-form-group - :label="$t('pageServerPowerOperations.rebootServer')" - > + <b-form novalidate class="mb-2"> + <b-form-group class="regular-12px cb"> + <label class="semi-bold-12px">{{ + $t('pageServerPowerOperations.rebootServer') + }}</label> <b-form-radio v-model="form.rebootOption" name="reboot-option" @@ -100,19 +117,29 @@ {{ $t('pageServerPowerOperations.immediateReboot') }} </b-form-radio> </b-form-group> - <b-button - variant="primary" - type="submit" - data-test-id="serverPowerOperations-button-reboot" + <popover-with-slot + id="popover-reboot" + :button-label="$t('pageServerPowerOperations.reboot')" + :popup-label="$t('pageServerPowerOperations.rebootServer')" + placement="right" + :action="rebootServer" > - {{ $t('pageServerPowerOperations.reboot') }} - </b-button> + <b-button + id="popover-reboot" + ref="button" + size="md" + variant="primary" + > + {{ $t('pageServerPowerOperations.reboot') }} + </b-button> + </popover-with-slot> </b-form> <!-- Shutdown server options --> - <b-form novalidate @submit.prevent="shutdownServer"> - <b-form-group - :label="$t('pageServerPowerOperations.shutdownServer')" - > + <b-form> + <b-form-group class="regular-12px cb"> + <label class="semi-bold-12px">{{ + $t('pageServerPowerOperations.shutdownServer') + }}</label> <b-form-radio v-model="form.shutdownOption" name="shutdown-option" @@ -130,18 +157,35 @@ {{ $t('pageServerPowerOperations.immediateShutdown') }} </b-form-radio> </b-form-group> - <b-button - variant="primary" - type="submit" - data-test-id="serverPowerOperations-button-shutDown" + <popover-with-slot + id="popover-shutDown" + :button-label="$t('pageServerPowerOperations.shutDown')" + :popup-label="$t('pageServerPowerOperations.shutdownServer')" + placement="right" + :action="shutdownServer" > - {{ $t('pageServerPowerOperations.shutDown') }} - </b-button> + <b-button + id="popover-shutDown" + ref="button" + size="md" + variant="secondary" + > + {{ $t('pageServerPowerOperations.shutDown') }} + </b-button> + </popover-with-slot> </b-form> </template> </page-section> </b-col> - </b-row> + </page-section> + <page-section + :section-title="$t('pageServerPowerOperations.serverBootSettings')" + class="m-4" + > + <b-col v-if="hasBootSourceOptions" sm="8" md="6" xl="4"> + <boot-settings /> + </b-col> + </page-section> </b-container> </template> @@ -152,10 +196,17 @@ import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; import BootSettings from './BootSettings'; import LoadingBarMixin from '@/components/_sila/Mixins/LoadingBarMixin'; import Alert from '@/components/_sila/Global/Alert'; +import PopoverWithSlot from '@/components/_sila/Global/SilaComponents/PopoverWithSlot'; export default { name: 'ServerPowerOperations', - components: { PageTitle, PageSection, BootSettings, Alert }, + components: { + PageTitle, + PageSection, + BootSettings, + Alert, + PopoverWithSlot, + }, mixins: [BVToastMixin, LoadingBarMixin], beforeRouteLeave(to, from, next) { this.hideLoader(); @@ -207,52 +258,18 @@ export default { this.$store.dispatch('controls/serverPowerOn'); }, rebootServer() { - const modalMessage = this.$t( - 'pageServerPowerOperations.modal.confirmRebootMessage' - ); - const modalOptions = { - title: this.$t('pageServerPowerOperations.modal.confirmRebootTitle'), - okTitle: this.$t('global.action.confirm'), - cancelTitle: this.$t('global.action.cancel'), - }; - if (this.form.rebootOption === 'orderly') { - this.$bvModal - .msgBoxConfirm(modalMessage, modalOptions) - .then((confirmed) => { - if (confirmed) this.$store.dispatch('controls/serverSoftReboot'); - }); + this.$store.dispatch('controls/serverSoftReboot'); } else if (this.form.rebootOption === 'immediate') { - this.$bvModal - .msgBoxConfirm(modalMessage, modalOptions) - .then((confirmed) => { - if (confirmed) this.$store.dispatch('controls/serverHardReboot'); - }); + this.$store.dispatch('controls/serverHardReboot'); } }, shutdownServer() { - const modalMessage = this.$t( - 'pageServerPowerOperations.modal.confirmShutdownMessage' - ); - const modalOptions = { - title: this.$t('pageServerPowerOperations.modal.confirmShutdownTitle'), - okTitle: this.$t('global.action.confirm'), - cancelTitle: this.$t('global.action.cancel'), - }; - if (this.form.shutdownOption === 'orderly') { - this.$bvModal - .msgBoxConfirm(modalMessage, modalOptions) - .then((confirmed) => { - if (confirmed) this.$store.dispatch('controls/serverSoftPowerOff'); - }); + this.$store.dispatch('controls/serverSoftPowerOff'); } if (this.form.shutdownOption === 'immediate') { - this.$bvModal - .msgBoxConfirm(modalMessage, modalOptions) - .then((confirmed) => { - if (confirmed) this.$store.dispatch('controls/serverHardPowerOff'); - }); + this.$store.dispatch('controls/serverHardPowerOff'); } }, }, diff --git a/src/views/_sila/Operations/VirtualMedia/ModalConfigureConnection.vue b/src/views/_sila/Operations/VirtualMedia/ModalConfigureConnection.vue index 9886eff5..9ba90d57 100644 --- a/src/views/_sila/Operations/VirtualMedia/ModalConfigureConnection.vue +++ b/src/views/_sila/Operations/VirtualMedia/ModalConfigureConnection.vue @@ -9,14 +9,17 @@ <template #modal-title> {{ $t('pageVirtualMedia.modal.title') }} </template> - <b-form> + <b-form style="width: 100%"> <b-form-group - :label="$t('pageVirtualMedia.modal.serverUri')" + :label=" + $t('pageVirtualMedia.modal.serverUri') + ' (http, ftp, smb, nfs)' + " label-for="serverUri" > <b-form-input id="serverUri" v-model="form.serverUri" + placeholder="https://" type="text" :state="getValidationState($v.form.serverUri)" data-test-id="configureConnection-input-serverUri" @@ -43,12 +46,14 @@ :label="$t('pageVirtualMedia.modal.password')" label-for="password" > - <b-form-input - id="password" - v-model="form.password" - type="password" - data-test-id="configureConnection-input-password" - /> + <input-password-toggle> + <b-form-input + id="password" + v-model="form.password" + type="password" + data-test-id="configureConnection-input-password" + /> + </input-password-toggle> </b-form-group> <b-form-group> <b-form-checkbox @@ -72,8 +77,12 @@ <script> import { required } from 'vuelidate/lib/validators'; import VuelidateMixin from '@/components/_sila/Mixins/VuelidateMixin.js'; +import InputPasswordToggle from '@/components/_sila/Global/InputPasswordToggle'; export default { + components: { + InputPasswordToggle, + }, mixins: [VuelidateMixin], props: { connection: { diff --git a/src/views/_sila/Operations/VirtualMedia/VirtualMedia.vue b/src/views/_sila/Operations/VirtualMedia/VirtualMedia.vue index 0f75bbd7..e508e06f 100644 --- a/src/views/_sila/Operations/VirtualMedia/VirtualMedia.vue +++ b/src/views/_sila/Operations/VirtualMedia/VirtualMedia.vue @@ -1,14 +1,18 @@ <template> - <b-container fluid="xl"> + <b-container fluid class="m-0 p-0"> <page-title /> - <b-row class="mb-4"> + <b-row class="bootstrap-table__section"> <b-col md="12"> <page-section :section-title="$t('pageVirtualMedia.virtualMediaSubTitleFirst')" > <b-row> <b-col v-for="(dev, $index) in proxyDevices" :key="$index" md="6"> - <b-form-group :label="dev.id" label-class="bold"> + <b-form-group + :label="dev.id" + label-class="regular-14px" + :style="{ 'margin-bottom': dev.isActive ? '0' : '1rem' }" + > <form-file v-if="!dev.isActive" :id="concatId(dev.id)" @@ -21,8 +25,18 @@ </template> </form-file> </b-form-group> + + <div + v-if="dev.isActive && dev.file && dev.file.name" + class="clear-selected-file px-3" + :style="{ 'margin-bottom': '1rem' }" + > + {{ dev.file.name }} + </div> + <b-button v-if="!dev.isActive" + size="md" variant="primary" :disabled="!dev.file" @click="startVM(dev)" @@ -42,7 +56,7 @@ </page-section> </b-col> </b-row> - <b-row v-if="loadImageFromExternalServer" class="mb-4"> + <b-row v-if="loadImageFromExternalServer" class="bootstrap-table__section"> <b-col md="12"> <page-section :section-title="$t('pageVirtualMedia.virtualMediaSubTitleSecond')" @@ -56,34 +70,35 @@ <b-form-group :label="device.id" :label-for="device.id" - label-class="bold" + label-class="regular-14px" > <b-button variant="primary" + size="lg" :disabled="device.isActive" @click="configureConnection(device)" > {{ $t('pageVirtualMedia.configureConnection') }} </b-button> - - <b-button - v-if="!device.isActive" - variant="primary" - class="float-right" - :disabled="!device.serverUri" - @click="startLegacy(device)" - > - {{ $t('pageVirtualMedia.start') }} - </b-button> - <b-button - v-if="device.isActive" - variant="primary" - class="float-right" - @click="stopLegacy(device)" - > - {{ $t('pageVirtualMedia.stop') }} - </b-button> </b-form-group> + + <b-button + v-if="!device.isActive" + variant="primary" + size="md" + :disabled="!device.serverUri" + @click="startLegacy(device)" + > + {{ $t('pageVirtualMedia.start') }} + </b-button> + <b-button + v-if="device.isActive" + size="md" + variant="primary" + @click="stopLegacy(device)" + > + {{ $t('pageVirtualMedia.stop') }} + </b-button> </b-col> </b-row> </page-section> @@ -107,7 +122,12 @@ import FormFile from '@/components/_sila/Global/FormFile'; export default { name: 'VirtualMedia', - components: { PageTitle, PageSection, ModalConfigureConnection, FormFile }, + components: { + PageTitle, + PageSection, + ModalConfigureConnection, + FormFile, + }, mixins: [BVToastMixin, LoadingBarMixin], data() { return { diff --git a/src/views/_sila/Overview/Inventory/index.js b/src/views/_sila/Overview/Inventory/index.js index c9fde8d2..e69de29b 100644 --- a/src/views/_sila/Overview/Inventory/index.js +++ b/src/views/_sila/Overview/Inventory/index.js @@ -1,2 +0,0 @@ -import Inventory from './Inventory.vue'; -export default Inventory; diff --git a/src/views/_sila/Overview/Network/index.js b/src/views/_sila/Overview/Network/index.js index 97bf0397..e69de29b 100644 --- a/src/views/_sila/Overview/Network/index.js +++ b/src/views/_sila/Overview/Network/index.js @@ -1,2 +0,0 @@ -import Network from './Network.vue'; -export default Network; diff --git a/src/views/_sila/Overview/Overview.vue b/src/views/_sila/Overview/Overview.vue index 9f97fb3e..9960f373 100644 --- a/src/views/_sila/Overview/Overview.vue +++ b/src/views/_sila/Overview/Overview.vue @@ -26,7 +26,7 @@ </template> <script> -import LoadingBarMixin from '@/components/_sila/Mixins/LoadingBarMixin'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; import OverviewDumps from './OverviewDumps.vue'; import OverviewEvents from './OverviewEvents.vue'; import OverviewFirmware from './OverviewFirmware.vue'; @@ -35,8 +35,8 @@ import OverviewNetwork from './OverviewNetwork'; import OverviewPower from './OverviewPower'; import OverviewQuickLinks from './OverviewQuickLinks'; import OverviewServer from './OverviewServer'; -import PageSection from '@/components/_sila/Global/PageSection'; -import PageTitle from '@/components/_sila/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import PageTitle from '@/components/Global/PageTitle'; export default { name: 'Overview', diff --git a/src/views/_sila/Overview/OverviewDumps.vue b/src/views/_sila/Overview/OverviewDumps.vue index 27f5067d..a2ae4e4e 100644 --- a/src/views/_sila/Overview/OverviewDumps.vue +++ b/src/views/_sila/Overview/OverviewDumps.vue @@ -20,7 +20,7 @@ <script> import OverviewCard from './OverviewCard'; -import DataFormatterMixin from '@/components/_sila/Mixins/DataFormatterMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; export default { name: 'Dumps', diff --git a/src/views/_sila/Overview/OverviewEvents.vue b/src/views/_sila/Overview/OverviewEvents.vue index c54bc5b9..b73c0b48 100644 --- a/src/views/_sila/Overview/OverviewEvents.vue +++ b/src/views/_sila/Overview/OverviewEvents.vue @@ -32,8 +32,8 @@ <script> import OverviewCard from './OverviewCard'; -import StatusIcon from '@/components/_sila/Global/StatusIcon'; -import DataFormatterMixin from '@/components/_sila/Mixins/DataFormatterMixin'; +import StatusIcon from '@/components/Global/StatusIcon'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; export default { name: 'Events', diff --git a/src/views/_sila/Overview/OverviewFirmware.vue b/src/views/_sila/Overview/OverviewFirmware.vue index 9167c75c..f1f9ce53 100644 --- a/src/views/_sila/Overview/OverviewFirmware.vue +++ b/src/views/_sila/Overview/OverviewFirmware.vue @@ -18,7 +18,7 @@ <script> import OverviewCard from './OverviewCard'; -import DataFormatterMixin from '@/components/_sila/Mixins/DataFormatterMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; export default { name: 'Firmware', diff --git a/src/views/_sila/Overview/OverviewNetwork.vue b/src/views/_sila/Overview/OverviewNetwork.vue index 7010b991..b81e5c73 100644 --- a/src/views/_sila/Overview/OverviewNetwork.vue +++ b/src/views/_sila/Overview/OverviewNetwork.vue @@ -49,7 +49,7 @@ <script> import OverviewCard from './OverviewCard'; -import DataFormatterMixin from '@/components/_sila/Mixins/DataFormatterMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; export default { name: 'Network', diff --git a/src/views/_sila/Overview/OverviewPower.vue b/src/views/_sila/Overview/OverviewPower.vue index ffda495f..0d84c76c 100644 --- a/src/views/_sila/Overview/OverviewPower.vue +++ b/src/views/_sila/Overview/OverviewPower.vue @@ -24,7 +24,7 @@ <script> import OverviewCard from './OverviewCard'; -import DataFormatterMixin from '@/components/_sila/Mixins/DataFormatterMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; import { mapGetters } from 'vuex'; export default { diff --git a/src/views/_sila/Overview/OverviewQuickLinks.vue b/src/views/_sila/Overview/OverviewQuickLinks.vue index 6f74fd91..bc579b03 100644 --- a/src/views/_sila/Overview/OverviewQuickLinks.vue +++ b/src/views/_sila/Overview/OverviewQuickLinks.vue @@ -27,7 +27,7 @@ <script> import ArrowRight16 from '@carbon/icons-vue/es/arrow--right/16'; -import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; export default { name: 'QuickLinks', diff --git a/src/views/_sila/Overview/OverviewServer.vue b/src/views/_sila/Overview/OverviewServer.vue index 7dded5ba..d066d391 100644 --- a/src/views/_sila/Overview/OverviewServer.vue +++ b/src/views/_sila/Overview/OverviewServer.vue @@ -1,7 +1,7 @@ <template> <overview-card :title="$t('pageOverview.serverInformation')" - :to="`/hardware-inventory`" + :to="`/hardware-status/inventory`" > <b-row class="mt-3"> <b-col lg="6"> @@ -18,7 +18,7 @@ <script> import OverviewCard from './OverviewCard'; -import DataFormatterMixin from '@/components/_sila/Mixins/DataFormatterMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; import { mapState } from 'vuex'; export default { diff --git a/src/views/_sila/PowerSupplies/DynamicInfo/PowerDynamicPage.vue b/src/views/_sila/PowerSupplies/DynamicInfo/PowerDynamicPage.vue new file mode 100644 index 00000000..223570bf --- /dev/null +++ b/src/views/_sila/PowerSupplies/DynamicInfo/PowerDynamicPage.vue @@ -0,0 +1,592 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.analyticalPanel')" /> + <div class="notification-time__container"> + <span class="semi-bold-12px">{{ + $t('global.ariaLabel.notificationTime') + }}</span> + <div class="form-control notification-time"> + <b-form-input + v-model="notificationInput" + type="number" + class="notification-time__input" + > + </b-form-input> + <button class="notification-button"> + <img + class="notification-time__icon" + src="@/assets/images/refresh.svg" + /> + </button> + </div> + </div> + <date-switch :switch-time-scale="switchTimeScale" :time-scale="timeScale" /> + <!-- Temperature Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_1 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/temperature-icon.svg" /> + <span class="bold-16px">{{ $t('subHeader.temperature') }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_1" class="nav-item__nav"> + <!-- Temperature Limit Inputs --> + <div class="limit-container"> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/non-normal.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.nonNormalMode') + }}</span> + <b-form-input + v-model="temperatureNonNormalInput" + type="number" + :min="0" + :max="temperatureCritical" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/critical.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.criticalMode') + }}</span> + <b-form-input + v-model="temperatureCritical" + type="number" + :min="temperatureNonNormalInput" + :max="temperatureWarningInput" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.temperatureWarning') + }}</span> + <b-form-input + v-model="temperatureWarningInput" + type="number" + :min="temperatureCritical" + :max="100" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button + class="save-button" + variant="primary" + @click="updateTemperature" + > + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Temperature Tables --> + <temperature-table + :time-scale="timeScale" + :warning="temperatureWarning" + :non-normal="temperatureNonNormal" + :critical="temperatureCritical" + /> + <accessory-table :records="accessoryData.temperatureTable" /> + </b-collapse> + </div> + <!-- using Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_2 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/usage-icon.svg" /> + <span class="bold-16px">{{ $t('pagePower.usingPercent') }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_2" class="nav-item__nav"> + <!-- using Limit Inputs --> + <div class="limit-container"> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.warningPercent') + }}</span> + <b-form-input + v-model="usingNonNormalInput" + type="number" + :min="0" + :max="usingCritical" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/non-normal.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.nonNormalModePercent') + }}</span> + <b-form-input + v-model="usingCritical" + type="number" + :min="usingNonNormalInput" + :max="usingWarningInput" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/critical.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.criticalModePercent') + }}</span> + <b-form-input + v-model="usingWarningInput" + type="number" + :min="usingCritical" + :max="100" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button class="save-button" variant="primary" @click="updateUsage"> + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- using Tables --> + <using-table + :time-scale="timeScale" + :warning="usingWarning" + :non-normal="usingNonNormal" + :critical-start="usingCritical" + /> + <accessory-table :records="accessoryData.usingTable" /> + </b-collapse> + </div> + <!-- Input Voltage Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_3 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/voltage-icon.svg" /> + <span class="bold-16px">{{ + $t('tablesDescription.inputVoltage') + }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_3" class="nav-item__nav"> + <div class="limit-container"> + <div class="frequency-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.voltageWarning') + }}</span> + <b-form-input + v-model="powerWarningInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="frequency-limt"> + <img src="@/assets/images/labels/shutdown.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.voltageShutdown') + }}</span> + <b-form-input + v-model="powerShutdownInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button class="save-button" variant="primary" @click="updatePower"> + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Input Voltage Tables --> + <voltage-table :time-scale="timeScale" :warning="frequencyWarning" /> + <accessory-table :records="accessoryData.voltageTable" /> + </b-collapse> + </div> + <!-- Input Power Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_4 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/power-icon.svg" /> + <span class="bold-16px">{{ $t('tablesDescription.inputPower') }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_4" class="nav-item__nav"> + <div class="limit-container"> + <div class="frequency-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.powerWarning') + }}</span> + <b-form-input + v-model="powerWarningInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="frequency-limt"> + <img src="@/assets/images/labels/shutdown.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.powerShutdown') + }}</span> + <b-form-input + v-model="powerShutdownInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button class="save-button" variant="primary" @click="updatePower"> + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Input Power Table --> + <power-table + :time-scale="timeScale" + :warning="powerWarning" + :shutdown="powerShutdown" + /> + <accessory-table :records="accessoryData.powerTable" /> + </b-collapse> + </div> + <!-- Output Voltage Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_5 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/voltage-icon.svg" /> + <span class="bold-16px">{{ + $t('tablesDescription.outputVoltage') + }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_5" class="nav-item__nav"> + <div class="limit-container"> + <div class="frequency-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.voltageWarning') + }}</span> + <b-form-input + v-model="powerWarningInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="frequency-limt"> + <img src="@/assets/images/labels/shutdown.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.voltageShutdown') + }}</span> + <b-form-input + v-model="powerShutdownInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button class="save-button" variant="primary" @click="updatePower"> + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Output Voltage Tables --> + <voltage-table :time-scale="timeScale" :warning="frequencyWarning" /> + <accessory-table :records="accessoryData.voltageTable" /> + </b-collapse> + </div> + <!-- Output Power Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_6 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/power-icon.svg" /> + <span class="bold-16px">{{ $t('tablesDescription.outputPower') }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_6" class="nav-item__nav"> + <div class="limit-container"> + <div class="frequency-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.powerWarning') + }}</span> + <b-form-input + v-model="powerWarningInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="frequency-limt"> + <img src="@/assets/images/labels/shutdown.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.powerShutdown') + }}</span> + <b-form-input + v-model="powerShutdownInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button class="save-button" variant="primary" @click="updatePower"> + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Output Power Table --> + <power-table + :time-scale="timeScale" + :warning="powerWarning" + :shutdown="powerShutdown" + /> + <accessory-table :records="accessoryData.powerTable" /> + </b-collapse> + </div> + <!-- Amperage Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_7 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/amperage-icon.svg" /> + <span class="bold-16px">{{ $t('tablesDescription.amperage') }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_7" class="nav-item__nav"> + <div class="limit-container"> + <div class="frequency-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.voltageWarning') + }}</span> + <b-form-input + v-model="powerWarningInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="frequency-limt"> + <img src="@/assets/images/labels/shutdown.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.voltageShutdown') + }}</span> + <b-form-input + v-model="powerShutdownInput" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button class="save-button" variant="primary" @click="updatePower"> + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Amperage Table --> + <voltage-table :time-scale="timeScale" :warning="frequencyWarning" /> + <accessory-table :records="accessoryData.voltageTable" /> + </b-collapse> + </div> + </b-container> +</template> +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import DateSwitch from '@/components/Global/SilaComponents/DateSwitch'; + +import TemperatureTable from './TemperatureTable'; +import UsingTable from './UsingTable'; +import PowerTable from './PowerTable'; +import VoltageTable from './VoltageTable'; +import AccessoryTable from '@/components/Global/SilaComponents/Tables/AccessoryTablePower'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +import { AccessoryData } from './helpers'; + +export default { + components: { + PageTitle, + PageSection, + DateSwitch, + TemperatureTable, + UsingTable, + PowerTable, + VoltageTable, + AccessoryTable, + }, + data() { + return { + timeScale: 'hour', + temperatureNonNormalInput: 44, + temperatureNonNormal: 44, + temperatureCriticalInput: 55, + temperatureCritical: 55, + temperatureWarningInput: 72, + temperatureWarning: 72, + usingNonNormalInput: 50, + usingNonNormal: 50, + usingCriticalInput: 55, + usingCritical: 55, + usingWarningInput: 72, + usingWarning: 72, + // frequencyWarning: 660, + // frequencyWarningInput: 660, + powerWarning: 66, + powerWarningInput: 66, + powerShutdown: 88, + powerShutdownInput: 88, + notificationInput: 42, + accessoryData: AccessoryData, + iconChevronUp: iconChevronUp, + }; + }, + methods: { + switchTimeScale(period) { + this.timeScale = period; + }, + updateTemperature() { + this.temperatureNonNormal = +this.temperatureNonNormalInput; + this.temperatureCritical = +this.temperatureCriticalInput; + this.temperatureWarning = +this.temperatureWarningInput; + }, + updateUsage() { + this.usingNonNormal = +this.usingNonNormalInput; + this.usingCritical = +this.usingCriticalInput; + this.usingWarning = +this.usingWarningInput; + }, + updateFrequency() { + this.frequencyWarning = +this.frequencyWarningInput; + }, + updatePower() { + this.powerWarning = +this.powerWarningInput; + this.powerShutdown = +this.powerShutdownInput; + }, + }, +}; +</script> +<style lang="scss" scoped> +//nav items style +.nav-item, +.nav-link { + padding: 0; +} +.nav-item { + list-style-type: none; +} + +a { + color: $text-primary !important; + &:hover { + color: $text-primary !important; + } +} + +.notification-time__container { + position: absolute; + top: calc(#{$header-height}); + right: 0px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; +} + +.notification-time { + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; + border: none; + box-shadow: none; + border-radius: 8px; + margin: 12px 32px 12px 8px; + width: 236px; + height: 40px; + z-index: 1001; +} + +.notification-time__icon { + width: 20px; + height: 20px; +} + +.notification-time__input { + border: none; + background: none; + box-shadow: none; +} + +.notification-button { + border: none; + background: none; +} + +.semi-bold-12px { + z-index: 1001; +} +// temperature limit comtainer +.limit-container { + height: 85px; + width: 100%; + padding: 0 32px 10px 32px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: flex-end; + gap: 24px; +} + +.trmperature-limt { + height: 60px; + width: 100%; + max-width: 270px; + display: flex; + align-items: baseline; + flex-flow: row wrap; + gap: 8px; +} + +.form-control { + height: 36px; + &.non-normal { + width: 125px; + } +} +.save-button { + width: 151px; + height: 36px; +} + +.frequency-limt { + height: 60px; + width: 100%; + max-width: 288px; + display: flex; + align-items: baseline; + flex-flow: row wrap; + gap: 8px; +} +</style> diff --git a/src/views/_sila/PowerSupplies/DynamicInfo/PowerTable.vue b/src/views/_sila/PowerSupplies/DynamicInfo/PowerTable.vue new file mode 100644 index 00000000..4ccb8aac --- /dev/null +++ b/src/views/_sila/PowerSupplies/DynamicInfo/PowerTable.vue @@ -0,0 +1,126 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 66, + }, + shutdown: { + type: Number, + default: 88, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, this.timeScale), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'Вт'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значения предупреждение', + align: 'right', + style: { + fontFamily: 'Inter', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + { + color: '#1A3E5B', + dashStyle: 'solid', + value: this.shutdown, + width: 2, + label: { + text: 'Пороговое значения отказ', + align: 'right', + style: { + fontFamily: 'Inter', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + }, + series: Series['power'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/PowerSupplies/DynamicInfo/TemperatureTable.vue b/src/views/_sila/PowerSupplies/DynamicInfo/TemperatureTable.vue new file mode 100644 index 00000000..9ae92c06 --- /dev/null +++ b/src/views/_sila/PowerSupplies/DynamicInfo/TemperatureTable.vue @@ -0,0 +1,126 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 70, + }, + nonNormal: { + type: Number, + default: 70, + }, + critical: { + type: Number, + default: 70, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, 'hour'), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'С°'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значение предупреждения, С°', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + plotBands: [ + { + color: '#F0AC0C1A', + dashStyle: 'solid', + from: this.nonNormal, + to: this.criticalStart, + }, + { + color: '#FF41411A', + dashStyle: 'solid', + from: this.criticalStart, + to: this.warning, + }, + ], + }, + series: Series['temperature'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/PowerSupplies/DynamicInfo/UsingTable.vue b/src/views/_sila/PowerSupplies/DynamicInfo/UsingTable.vue new file mode 100644 index 00000000..322a7f7b --- /dev/null +++ b/src/views/_sila/PowerSupplies/DynamicInfo/UsingTable.vue @@ -0,0 +1,126 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 70, + }, + nonNormal: { + type: Number, + default: 70, + }, + criticalStart: { + type: Number, + default: 70, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, 'hour'), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, '%'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значение предупреждения, %', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + plotBands: [ + { + color: '#F0AC0C1A', + dashStyle: 'solid', + from: this.nonNormal, + to: this.criticalStart, + }, + { + color: '#FF41411A', + dashStyle: 'solid', + from: this.criticalStart, + to: this.warning, + }, + ], + }, + series: Series['temperature'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/PowerSupplies/DynamicInfo/VoltageTable.vue b/src/views/_sila/PowerSupplies/DynamicInfo/VoltageTable.vue new file mode 100644 index 00000000..b7b2a973 --- /dev/null +++ b/src/views/_sila/PowerSupplies/DynamicInfo/VoltageTable.vue @@ -0,0 +1,126 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 66, + }, + shutdown: { + type: Number, + default: 88, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, this.timeScale), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'В'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значения предупреждение', + align: 'right', + style: { + fontFamily: 'Inter', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + { + color: '#1A3E5B', + dashStyle: 'solid', + value: this.shutdown, + width: 2, + label: { + text: 'Пороговое значения отказ', + align: 'right', + style: { + fontFamily: 'Inter', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + }, + series: Series['voltage'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/PowerSupplies/DynamicInfo/helpers.js b/src/views/_sila/PowerSupplies/DynamicInfo/helpers.js new file mode 100644 index 00000000..1418411a --- /dev/null +++ b/src/views/_sila/PowerSupplies/DynamicInfo/helpers.js @@ -0,0 +1,1526 @@ +export const colors = [ + '#CB32F1', + '#F18638', + '#139BB9', + '#E1AB17', + '#175AE1', + '#13B937', +]; + +export const Series = { + temperature: [ + { + name: 'Sean', + data: [ + 10, + 10, + 10, + 30, + 10, + 10, + 10, + 37, + 10, + 10, + 10, + 10, + 10, + 25, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 35, + 10, + 10, + 10, + 10, + 10, + 45, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 50, + 10, + 10, + 10, + 10, + 10, + 10, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 11, + 11, + 11, + 30, + 11, + 11, + 11, + 11, + 57, + 11, + 11, + 11, + 11, + 25, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 61, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 31, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 21, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 51, + 11, + 11, + 11, + 11, + 11, + 11, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 15, + 15, + 15, + 35, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 25, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 45, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 35, + 15, + 15, + 55, + 15, + 15, + 15, + 15, + 15, + 15, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 21, + 21, + 21, + 51, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 40, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 35, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 53, + 21, + 21, + 30, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 19, + 19, + 19, + 39, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 29, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 39, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 39, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 59, + 19, + 19, + 19, + 19, + 19, + 19, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 16, + 16, + 16, + 56, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 26, + 16, + 16, + 16, + 16, + 16, + 16, + 26, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 76, + 16, + 16, + 16, + 16, + 46, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 46, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + ], + color: '#139BB9', + }, + ], + //////////////////////////////voltage//////////////// + voltage: [ + { + name: 'Sean', + data: [ + 16, + 16, + 16, + 46, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 26, + 16, + 16, + 16, + 16, + 16, + 16, + 26, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 56, + 16, + 26, + 16, + 16, + 36, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 46, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 36, + 16, + 16, + 16, + 16, + 16, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 11, + 11, + 11, + 44, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 21, + 11, + 11, + 11, + 11, + 11, + 11, + 21, + 11, + 31, + 11, + 11, + 11, + 11, + 11, + 11, + 55, + 11, + 21, + 11, + 11, + 31, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 31, + 11, + 11, + 41, + 11, + 11, + 11, + 21, + 11, + 11, + 11, + 35, + 11, + 11, + 11, + 11, + 11, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 31, + 31, + 31, + 51, + 31, + 31, + 31, + 31, + 31, + 90, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 50, + 31, + 31, + 31, + 31, + 31, + 31, + 35, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 40, + 31, + 31, + 31, + 31, + 53, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 21, + 21, + 21, + 51, + 21, + 21, + 21, + 21, + 21, + 40, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 50, + 21, + 21, + 21, + 21, + 21, + 21, + 35, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 60, + 21, + 21, + 21, + 10, + 53, + 21, + 21, + 30, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 39, + 19, + 19, + 39, + 19, + 19, + 19, + 29, + 19, + 19, + 59, + 19, + 19, + 69, + 19, + 19, + 19, + 19, + 29, + 19, + 19, + 49, + 19, + 19, + 59, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 17, + 17, + 17, + 15, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 27, + 17, + 17, + 17, + 17, + 17, + 17, + 21, + 17, + 17, + 17, + 59, + 17, + 17, + 17, + 17, + 17, + 59, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 57, + 17, + 17, + 17, + 17, + 17, + 77, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + ], + color: '#139BB9', + }, + ], + //////////////////////////////power//////////////// + power: [ + { + name: 'Sean', + data: [ + 15, + 15, + 45, + 15, + 65, + 15, + 75, + 15, + 15, + 15, + 55, + 15, + 25, + 15, + 45, + 15, + 55, + 15, + 75, + 15, + 35, + 15, + 55, + 15, + 75, + 15, + 85, + 15, + 15, + 45, + 15, + 15, + 45, + 15, + 45, + 15, + 15, + 65, + 15, + 45, + 15, + 25, + 15, + 45, + 15, + 65, + 15, + 35, + 35, + 35, + 15, + 55, + 15, + 15, + 35, + 15, + 25, + 15, + 35, + 15, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 62, + 12, + 72, + 12, + 12, + 12, + 42, + 12, + 12, + 12, + 52, + 12, + 22, + 12, + 42, + 12, + 52, + 12, + 72, + 12, + 52, + 12, + 62, + 12, + 72, + 12, + 82, + 12, + 12, + 42, + 12, + 12, + 52, + 12, + 42, + 12, + 12, + 62, + 12, + 42, + 12, + 22, + 12, + 42, + 12, + 62, + 12, + 12, + 32, + 12, + 62, + 12, + 52, + 12, + 32, + 12, + 22, + 12, + 32, + 12, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 14, + 14, + 34, + 14, + 24, + 14, + 34, + 14, + 64, + 14, + 74, + 14, + 14, + 14, + 44, + 14, + 14, + 14, + 54, + 14, + 24, + 14, + 44, + 14, + 54, + 14, + 74, + 14, + 54, + 14, + 64, + 14, + 74, + 14, + 84, + 14, + 14, + 44, + 14, + 14, + 54, + 14, + 44, + 14, + 14, + 64, + 14, + 44, + 14, + 24, + 14, + 44, + 14, + 64, + 14, + 14, + 14, + 54, + 14, + 54, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 24, + 14, + 44, + 14, + 64, + 14, + 64, + 14, + 34, + 14, + 54, + 14, + 14, + 34, + 14, + 24, + 14, + 34, + 14, + 64, + 14, + 74, + 14, + 14, + 14, + 44, + 14, + 14, + 14, + 54, + 14, + 14, + 14, + 44, + 14, + 54, + 14, + 74, + 14, + 54, + 14, + 64, + 14, + 74, + 14, + 84, + 14, + 94, + 14, + 54, + 4, + 54, + 14, + 44, + 14, + 44, + 64, + 14, + 44, + 14, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 16, + 46, + 16, + 26, + 16, + 46, + 16, + 66, + 16, + 16, + 16, + 36, + 16, + 56, + 16, + 16, + 36, + 16, + 26, + 16, + 36, + 16, + 66, + 16, + 76, + 16, + 16, + 16, + 46, + 16, + 16, + 16, + 56, + 16, + 26, + 16, + 46, + 16, + 56, + 16, + 76, + 16, + 56, + 16, + 66, + 16, + 76, + 16, + 86, + 16, + 96, + 16, + 16, + 16, + 56, + 16, + 46, + 16, + 46, + 16, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 49, + 19, + 19, + 69, + 19, + 49, + 19, + 29, + 19, + 49, + 19, + 69, + 19, + 39, + 39, + 39, + 19, + 59, + 19, + 19, + 39, + 19, + 29, + 19, + 39, + 19, + 69, + 19, + 79, + 19, + 19, + 19, + 49, + 19, + 19, + 19, + 59, + 19, + 29, + 19, + 49, + 19, + 59, + 19, + 79, + 19, + 59, + 19, + 69, + 19, + 79, + 19, + 89, + 19, + 99, + 19, + 19, + 69, + 59, + 19, + ], + color: '#139BB9', + }, + ], +}; + +export const setTime = (count) => { + const arr = [...new Array(count)].map(() => ''); + for (let i = 0; i < arr.length; i++) { + arr[i] = `15:${String(i).padStart(2, '0')}`; + } + return arr; +}; + +export const setCategories = (count, desc) => { + const arr = [...new Array(count)].map((i, k) => `${k} ${desc}`); + return arr; +}; + +export const AccessoryData = { + temperatureTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentTemperature', + label: 'Текущее, С°', + }, + { + key: 'middleTemperature', + label: 'Среднее, С°', + }, + { + key: 'minTemperature', + label: 'Минимальное, С°', + }, + { + key: 'minDate', + label: 'Дата минимального', + }, + { + key: 'maxTemperature', + label: 'Максимальное, С°', + }, + { + key: 'maxDate', + label: 'Дата максимального', + }, + ], + items: [ + { + name: 'Источник 1', + currentTemperature: 19, + middleTemperature: 40, + minTemperature: 31, + minDate: { time: '15:15', date: '11.11.2021' }, + maxTemperature: 88, + maxDate: { time: '10:26', date: '15.11.2021' }, + }, + { + name: 'Источник 2', + currentTemperature: 29, + middleTemperature: 40, + minTemperature: 20, + minDate: { time: '15:45', date: '11.11.2021' }, + maxTemperature: 76, + maxDate: { time: '16:59', date: '16.11.2021' }, + }, + { + name: 'Источник 3', + currentTemperature: 48, + middleTemperature: 46, + minTemperature: 31, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 69, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + { + name: 'Источник 4', + currentTemperature: 48, + middleTemperature: 45, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 75, + maxDate: { time: '11:26', date: '16.11.2021' }, + }, + { + name: 'Источник 5', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 30, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '17.11.2021' }, + }, + { + name: 'Источник 6', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + ], + }, + usingTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentTemperature', + label: 'Текущее, %', + }, + { + key: 'middleTemperature', + label: 'Среднее, %', + }, + { + key: 'minTemperature', + label: 'Минимальное, %', + }, + { + key: 'minDate', + label: 'Дата минимального', + }, + { + key: 'maxTemperature', + label: 'Максимальное, %', + }, + { + key: 'maxDate', + label: 'Дата максимального', + }, + ], + items: [ + { + name: 'Источник 1', + currentTemperature: 19, + middleTemperature: 40, + minTemperature: 31, + minDate: { time: '15:15', date: '11.11.2021' }, + maxTemperature: 88, + maxDate: { time: '10:26', date: '15.11.2021' }, + }, + { + name: 'Источник 2', + currentTemperature: 29, + middleTemperature: 40, + minTemperature: 20, + minDate: { time: '15:45', date: '11.11.2021' }, + maxTemperature: 76, + maxDate: { time: '16:59', date: '16.11.2021' }, + }, + { + name: 'Источник 3', + currentTemperature: 48, + middleTemperature: 46, + minTemperature: 31, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 69, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + { + name: 'Источник 4', + currentTemperature: 48, + middleTemperature: 45, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 75, + maxDate: { time: '11:26', date: '16.11.2021' }, + }, + { + name: 'Источник 5', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 30, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '17.11.2021' }, + }, + { + name: 'Источник 6', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + ], + }, + voltageTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentTemperature', + label: 'Текущее, В', + }, + { + key: 'middleTemperature', + label: 'Среднее, В', + }, + { + key: 'minTemperature', + label: 'Минимальное, В', + }, + { + key: 'minDate', + label: 'Дата минимального', + }, + { + key: 'maxTemperature', + label: 'Максимальное, В', + }, + { + key: 'maxDate', + label: 'Дата максимального', + }, + ], + items: [ + { + name: 'Источник 1', + currentTemperature: 19, + middleTemperature: 40, + minTemperature: 31, + minDate: { time: '15:15', date: '11.11.2021' }, + maxTemperature: 88, + maxDate: { time: '10:26', date: '15.11.2021' }, + }, + { + name: 'Источник 2', + currentTemperature: 29, + middleTemperature: 40, + minTemperature: 20, + minDate: { time: '15:45', date: '11.11.2021' }, + maxTemperature: 76, + maxDate: { time: '16:59', date: '16.11.2021' }, + }, + { + name: 'Источник 3', + currentTemperature: 48, + middleTemperature: 46, + minTemperature: 31, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 69, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + { + name: 'Источник 4', + currentTemperature: 48, + middleTemperature: 45, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 75, + maxDate: { time: '11:26', date: '16.11.2021' }, + }, + { + name: 'Источник 5', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 30, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '17.11.2021' }, + }, + { + name: 'Источник 6', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + ], + }, + powerTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentPower', + label: 'Текущее, Вт', + label2: '', + }, + ], + items: [ + { + name: 'Источник 1', + currentPower: 91, + }, + { + name: 'Источник 2', + currentPower: 77, + }, + { + name: 'Источник 3', + currentPower: 76, + }, + { + name: 'Источник 4', + currentPower: 74, + }, + { + name: 'Источник 5', + currentPower: 73, + }, + { + name: 'Источник 6', + currentPower: 71, + }, + ], + }, +}; diff --git a/src/views/_sila/PowerSupplies/DynamicInfo/index.js b/src/views/_sila/PowerSupplies/DynamicInfo/index.js new file mode 100644 index 00000000..c45d5c89 --- /dev/null +++ b/src/views/_sila/PowerSupplies/DynamicInfo/index.js @@ -0,0 +1,2 @@ +import PowerDynamicPage from './PowerDynamicPage.vue'; +export default PowerDynamicPage; diff --git a/src/views/_sila/PowerSupplies/Specification/PowerStaticPage.vue b/src/views/_sila/PowerSupplies/Specification/PowerStaticPage.vue new file mode 100644 index 00000000..10bf1457 --- /dev/null +++ b/src/views/_sila/PowerSupplies/Specification/PowerStaticPage.vue @@ -0,0 +1,137 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.specification')" /> + <page-section class="bootstrap-table__section info_section"> + <span class="bold-16px">{{ $t('pageInventory.powerSources') }}</span> + </page-section> + <data-tabs + :slots="sourceSlots" + :switch-tab="switchSourceSlot" + :current-tab="currentSourceSlot" + :slot-width="100" + :slider-width="68" + /> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items_slots" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(value)="{ index, value }"> + <b-row v-if="index === 0"> + <b-col cols="11"> + <span> + {{ 'Работоспособность' }} + </span> + </b-col> + <b-col cols="1"> + <img src="@/assets/images/status/on.svg" class="icon-chevron" /> + </b-col> + </b-row> + <b-row v-else> + <b-col> + <span>{{ value }}</span> + </b-col> + </b-row> + </template> + </b-table> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import DataTabs from '@/components/Global/SilaComponents/DataTabs'; + +export default { + components: { PageTitle, PageSection, DataTabs }, + data() { + return { + currentSourceSlot: 1, + sourceSlots: [ + { id: 1, name: 'Источник 1' }, + { id: 2, name: 'Источник 2' }, + { id: 3, name: 'Источник 3' }, + { id: 4, name: 'Источник 4' }, + { id: 5, name: 'Источник 5' }, + { id: 6, name: 'Источник 6' }, + { id: 7, name: 'Источник 7' }, + { id: 8, name: 'Источник 8' }, + { id: 9, name: 'Источник 9' }, + { id: 10, name: 'Источник 10' }, + { id: 11, name: 'Источник 11' }, + { id: 12, name: 'Источник 12' }, + { id: 13, name: 'Источник 13' }, + { id: 14, name: 'Источник 14' }, + { id: 15, name: 'Источник 15' }, + ], + fields: [ + { + key: 'parametr', + label: 'Параметр', + formatter: this.dataFormatter, + thStyle: { width: '50%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + items_slots: [ + { + parametr: 'Статус', + value: true, + }, + { + parametr: 'Название', + value: 'Источник 1', + }, + { + parametr: 'Версия прошивки', + value: '1.1.2257', + }, + { + parametr: 'Серийный номер', + value: '4789564478551', + }, + { + parametr: 'Номинальное напряжение', + value: '220 В', + }, + { + parametr: 'Номинальная мощность', + value: '400 Вт', + }, + { + parametr: 'Поддержка горячей замены', + value: 'Есть', + }, + ], + }; + }, + methods: { + switchSourceSlot(period) { + this.currentSourceSlot = period; + }, + }, +}; +</script> +<style lang="scss" scoped> +.info_section { + margin-bottom: 0px; +} + +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} +</style> diff --git a/src/views/_sila/PowerSupplies/Specification/index.js b/src/views/_sila/PowerSupplies/Specification/index.js new file mode 100644 index 00000000..14c4ef64 --- /dev/null +++ b/src/views/_sila/PowerSupplies/Specification/index.js @@ -0,0 +1,2 @@ +import PowerStaticPage from './PowerStaticPage.vue'; +export default PowerStaticPage; diff --git a/src/views/_sila/Processors/DynamicInfo/FrequencyTable.vue b/src/views/_sila/Processors/DynamicInfo/FrequencyTable.vue new file mode 100644 index 00000000..c749905d --- /dev/null +++ b/src/views/_sila/Processors/DynamicInfo/FrequencyTable.vue @@ -0,0 +1,107 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 70, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, 'hour'), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(1001, 'Hz'), + min: 0, + max: 1000, + title: null, + minTickInterval: 250, + minorGridLineColor: '#1A3E5B1A', + labels: { + padding: 4, + }, + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значение предупреждения', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + }, + series: Series['frequency'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/Processors/DynamicInfo/PowerTable.vue b/src/views/_sila/Processors/DynamicInfo/PowerTable.vue new file mode 100644 index 00000000..4ccb8aac --- /dev/null +++ b/src/views/_sila/Processors/DynamicInfo/PowerTable.vue @@ -0,0 +1,126 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 66, + }, + shutdown: { + type: Number, + default: 88, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, this.timeScale), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'Вт'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значения предупреждение', + align: 'right', + style: { + fontFamily: 'Inter', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + { + color: '#1A3E5B', + dashStyle: 'solid', + value: this.shutdown, + width: 2, + label: { + text: 'Пороговое значения отказ', + align: 'right', + style: { + fontFamily: 'Inter', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + }, + series: Series['power'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/Processors/DynamicInfo/ProcessorsDynamicPage.vue b/src/views/_sila/Processors/DynamicInfo/ProcessorsDynamicPage.vue new file mode 100644 index 00000000..7e0b16a4 --- /dev/null +++ b/src/views/_sila/Processors/DynamicInfo/ProcessorsDynamicPage.vue @@ -0,0 +1,361 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.analyticalPanel')" /> + <div class="notification-time__container"> + <span class="semi-bold-12px">{{ + $t('global.ariaLabel.notificationTime') + }}</span> + <div class="form-control notification-time"> + <b-form-input + v-model="notificationInput" + type="number" + class="notification-time__input" + > + </b-form-input> + <button class="notification-button"> + <img + class="notification-time__icon" + src="@/assets/images/refresh.svg" + /> + </button> + </div> + </div> + <date-switch :switch-time-scale="switchTimeScale" :time-scale="timeScale" /> + <!-- Temperature Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_1 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/temperature-icon.svg" /> + <span class="bold-16px">{{ $t('subHeader.temperature') }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_1" class="nav-item__nav"> + <!-- Temperature Limit Inputs --> + <div class="limit-container"> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/non-normal.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.nonNormalMode') + }}</span> + <b-form-input + v-model="temperatureNonNormalInput" + type="number" + :min="0" + :max="temperatureCriticalInput" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/critical.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.criticalMode') + }}</span> + <b-form-input + v-model="temperatureCriticalInput" + type="number" + :min="temperatureNonNormalInput" + :max="temperatureWarningInput" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.temperatureWarning') + }}</span> + <b-form-input + v-model="temperatureWarningInput" + type="number" + :min="temperatureCriticalInput" + :max="100" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button + class="save-button" + variant="primary" + @click="updateTemperature" + > + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Temperature Tables --> + <temperature-table + :time-scale="timeScale" + :warning="temperatureWarning" + :non-normal="temperatureNonNormal" + :critical-start="temperatureCritical" + /> + <accessory-table :records="accessoryData.temperatureTable" /> + </b-collapse> + </div> + <!-- Frequency Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_2 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/frequency-icon.svg" /> + <span class="bold-16px">{{ $t('subHeader.frequency') }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_2" class="nav-item__nav"> + <div class="limit-container"> + <div class="frequency-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.frequencyWarning') + }}</span> + <b-form-input + v-model="frequencyWarningInput" + :min="0" + :max="1000" + type="number" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button + class="save-button" + variant="primary" + @click="updateFrequency" + > + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Frequency Table --> + <frequency-table :time-scale="timeScale" :warning="frequencyWarning" /> + <accessory-table :records="accessoryData.frequencyTable" /> + <!-- <frequency-table /> --> + </b-collapse> + </div> + <!-- Power Consumption Section --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_3 + variant="link" + class="collapse-button semi-bold-16px" + > + <img src="@/assets/images/power-icon.svg" /> + <span class="bold-16px">{{ $t('subHeader.powerConsumption') }}</span> + <component :is="iconChevronUp" class="icon-expand" /> + </b-button> + <b-collapse id="toggle-collapse_3" class="nav-item__nav"> + <div class="limit-container"> + <div class="frequency-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.powerWarning') + }}</span> + <b-form-input + v-model="powerWarningInput" + type="number" + :min="0" + :max="100" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="frequency-limt"> + <img src="@/assets/images/labels/shutdown.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.powerShutdown') + }}</span> + <b-form-input + v-model="powerShutdownInput" + type="number" + :min="0" + :max="100" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button class="save-button" variant="primary" @click="updatePower"> + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Power Consumption Table --> + <power-table + :time-scale="timeScale" + :warning="powerWarning" + :shutdown="powerShutdown" + /> + <!-- <power-table /> --> + <accessory-table :records="accessoryData.powerTable" /> + </b-collapse> + </div> + <!-- <page-section class="bootstrap-table__section"> </page-section> --> + </b-container> +</template> +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import DateSwitch from '@/components/Global/SilaComponents/DateSwitch'; +import FrequencyTable from './FrequencyTable'; +import PowerTable from './PowerTable'; +import TemperatureTable from './TemperatureTable'; +import AccessoryTable from '@/components/Global/SilaComponents/Tables/AccessoryTablePower'; + +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +import { AccessoryData } from './helpers'; + +export default { + components: { + PageTitle, + PageSection, + DateSwitch, + FrequencyTable, + TemperatureTable, + PowerTable, + AccessoryTable, + }, + data() { + return { + timeScale: 'hour', + temperatureWarning: 72, + temperatureWarningInput: 72, + temperatureNonNormal: 44, + temperatureNonNormalInput: 44, + temperatureCritical: 55, + temperatureCriticalInput: 55, + frequencyWarning: 660, + frequencyWarningInput: 660, + powerWarning: 66, + powerWarningInput: 66, + powerShutdown: 88, + powerShutdownInput: 88, + notificationInput: 42, + accessoryData: AccessoryData, + iconChevronUp: iconChevronUp, + }; + }, + methods: { + switchTimeScale(period) { + this.timeScale = period; + }, + updateTemperature() { + this.temperatureWarning = +this.temperatureWarningInput; + this.temperatureNonNormal = +this.temperatureNonNormalInput; + this.temperatureCritical = +this.temperatureCriticalInput; + }, + updateFrequency() { + this.frequencyWarning = +this.frequencyWarningInput; + }, + updatePower() { + this.powerWarning = +this.powerWarningInput; + this.powerShutdown = +this.powerShutdownInput; + }, + }, +}; +</script> +<style lang="scss" scoped> +//nav items style +.nav-item, +.nav-link { + padding: 0; +} +.nav-item { + list-style-type: none; +} + +a { + color: $text-primary !important; + &:hover { + color: $text-primary !important; + } +} + +.notification-time__container { + position: absolute; + top: calc(#{$header-height}); + right: 0px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; +} + +.notification-time { + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; + border: none; + box-shadow: none; + border-radius: 8px; + margin: 12px 32px 12px 8px; + width: 236px; + height: 40px; + z-index: 1001; +} + +.notification-time__icon { + width: 20px; + height: 20px; +} + +.notification-time__input { + border: none; + background: none; + box-shadow: none; +} + +.notification-button { + border: none; + background: none; +} + +.semi-bold-12px { + z-index: 1001; +} +// temperature limit comtainer +.limit-container { + height: 85px; + width: 100%; + padding: 0 32px 10px 32px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: flex-end; + gap: 24px; +} + +.trmperature-limt { + height: 60px; + width: 100%; + max-width: 270px; + display: flex; + align-items: baseline; + flex-flow: row wrap; + gap: 8px; +} + +.form-control { + height: 36px; +} +.save-button { + width: 151px; + height: 36px; +} + +.frequency-limt { + height: 60px; + width: 100%; + max-width: 288px; + display: flex; + align-items: baseline; + flex-flow: row wrap; + gap: 8px; +} +</style> diff --git a/src/views/_sila/Processors/DynamicInfo/TemperatureTable.vue b/src/views/_sila/Processors/DynamicInfo/TemperatureTable.vue new file mode 100644 index 00000000..f9c149c3 --- /dev/null +++ b/src/views/_sila/Processors/DynamicInfo/TemperatureTable.vue @@ -0,0 +1,126 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 70, + }, + nonNormal: { + type: Number, + default: 70, + }, + criticalStart: { + type: Number, + default: 70, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, 'hour'), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'С°'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значение предупреждения, С°', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + plotBands: [ + { + color: '#F0AC0C1A', + dashStyle: 'solid', + from: this.nonNormal, + to: this.criticalStart, + }, + { + color: '#FF41411A', + dashStyle: 'solid', + from: this.criticalStart, + to: this.warning, + }, + ], + }, + series: Series['temperature'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/Processors/DynamicInfo/helpers.js b/src/views/_sila/Processors/DynamicInfo/helpers.js new file mode 100644 index 00000000..82e23544 --- /dev/null +++ b/src/views/_sila/Processors/DynamicInfo/helpers.js @@ -0,0 +1,1398 @@ +export const colors = [ + '#CB32F1', + '#F18638', + '#139BB9', + '#E1AB17', + '#175AE1', + '#13B937', +]; + +export const Series = { + temperature: [ + { + name: 'Sean', + data: [ + 10, + 10, + 10, + 30, + 10, + 10, + 10, + 37, + 10, + 10, + 10, + 10, + 10, + 25, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 35, + 10, + 10, + 10, + 10, + 10, + 45, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 50, + 10, + 10, + 10, + 10, + 10, + 10, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 11, + 11, + 11, + 30, + 11, + 11, + 11, + 11, + 57, + 11, + 11, + 11, + 11, + 25, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 61, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 31, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 21, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 51, + 11, + 11, + 11, + 11, + 11, + 11, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 15, + 15, + 15, + 35, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 25, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 45, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 35, + 15, + 15, + 55, + 15, + 15, + 15, + 15, + 15, + 15, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 21, + 21, + 21, + 51, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 40, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 35, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 53, + 21, + 21, + 30, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 19, + 19, + 19, + 39, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 29, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 39, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 39, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 59, + 19, + 19, + 19, + 19, + 19, + 19, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 16, + 16, + 16, + 56, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 26, + 16, + 16, + 16, + 16, + 16, + 16, + 26, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 76, + 16, + 16, + 16, + 16, + 46, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 46, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + ], + color: '#139BB9', + }, + ], + //////////////////////////////frequency//////////////// + frequency: [ + { + name: 'Sean', + data: [ + 100, + 100, + 450, + 100, + 100, + 100, + 100, + 137, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 125, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 135, + 100, + 100, + 100, + 100, + 100, + 145, + 100, + 100, + 360, + 100, + 100, + 450, + 100, + 100, + 100, + 100, + 100, + 100, + 150, + 100, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 120, + 120, + 120, + 140, + 120, + 157, + 120, + 120, + 120, + 210, + 120, + 125, + 120, + 120, + 120, + 350, + 120, + 120, + 120, + 120, + 120, + 590, + 120, + 120, + 120, + 120, + 450, + 120, + 120, + 120, + 120, + 120, + 125, + 120, + 120, + 120, + 120, + 120, + 162, + 120, + 120, + 120, + 120, + 120, + 220, + 120, + 120, + 120, + 120, + 120, + 360, + 120, + 210, + 120, + 200, + 120, + 120, + 120, + 120, + 120, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 110, + 110, + 110, + 450, + 110, + 110, + 110, + 157, + 110, + 110, + 110, + 110, + 110, + 165, + 110, + 110, + 110, + 110, + 110, + 310, + 110, + 110, + 110, + 590, + 110, + 110, + 175, + 110, + 110, + 110, + 110, + 110, + 110, + 110, + 110, + 110, + 110, + 110, + 110, + 110, + 152, + 110, + 310, + 110, + 110, + 210, + 110, + 110, + 110, + 110, + 110, + 110, + 360, + 110, + 110, + 110, + 210, + 110, + 110, + 110, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 221, + 221, + 221, + 251, + 221, + 221, + 221, + 221, + 221, + 590, + 221, + 221, + 421, + 221, + 221, + 221, + 221, + 221, + 221, + 221, + 221, + 221, + 221, + 221, + 221, + 221, + 450, + 221, + 221, + 221, + 221, + 421, + 221, + 235, + 221, + 221, + 221, + 221, + 221, + 421, + 221, + 360, + 221, + 221, + 221, + 210, + 253, + 221, + 221, + 230, + 221, + 221, + 221, + 590, + 221, + 221, + 221, + 221, + 221, + 221, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 590, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 590, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + 159, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 176, + 176, + 176, + 156, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 276, + 176, + 176, + 176, + 176, + 176, + 176, + 210, + 176, + 176, + 176, + 590, + 176, + 176, + 176, + 176, + 176, + 590, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 570, + 176, + 176, + 176, + 176, + 176, + 770, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + 176, + ], + color: '#139BB9', + }, + ], + //////////////////////////////power//////////////// + power: [ + { + name: 'Sean', + data: [ + 15, + 15, + 45, + 15, + 65, + 15, + 75, + 15, + 15, + 15, + 55, + 15, + 25, + 15, + 45, + 15, + 55, + 15, + 75, + 15, + 35, + 15, + 55, + 15, + 75, + 15, + 85, + 15, + 15, + 45, + 15, + 15, + 45, + 15, + 45, + 15, + 15, + 65, + 15, + 45, + 15, + 25, + 15, + 45, + 15, + 65, + 15, + 35, + 35, + 35, + 15, + 55, + 15, + 15, + 35, + 15, + 25, + 15, + 35, + 15, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 62, + 12, + 72, + 12, + 12, + 12, + 42, + 12, + 12, + 12, + 52, + 12, + 22, + 12, + 42, + 12, + 52, + 12, + 72, + 12, + 52, + 12, + 62, + 12, + 72, + 12, + 82, + 12, + 12, + 42, + 12, + 12, + 52, + 12, + 42, + 12, + 12, + 62, + 12, + 42, + 12, + 22, + 12, + 42, + 12, + 62, + 12, + 12, + 32, + 12, + 62, + 12, + 52, + 12, + 32, + 12, + 22, + 12, + 32, + 12, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 14, + 14, + 34, + 14, + 24, + 14, + 34, + 14, + 64, + 14, + 74, + 14, + 14, + 14, + 44, + 14, + 14, + 14, + 54, + 14, + 24, + 14, + 44, + 14, + 54, + 14, + 74, + 14, + 54, + 14, + 64, + 14, + 74, + 14, + 84, + 14, + 14, + 44, + 14, + 14, + 54, + 14, + 44, + 14, + 14, + 64, + 14, + 44, + 14, + 24, + 14, + 44, + 14, + 64, + 14, + 14, + 14, + 54, + 14, + 54, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 24, + 14, + 44, + 14, + 64, + 14, + 64, + 14, + 34, + 14, + 54, + 14, + 14, + 34, + 14, + 24, + 14, + 34, + 14, + 64, + 14, + 74, + 14, + 14, + 14, + 44, + 14, + 14, + 14, + 54, + 14, + 14, + 14, + 44, + 14, + 54, + 14, + 74, + 14, + 54, + 14, + 64, + 14, + 74, + 14, + 84, + 14, + 94, + 14, + 54, + 4, + 54, + 14, + 44, + 14, + 44, + 64, + 14, + 44, + 14, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 16, + 46, + 16, + 26, + 16, + 46, + 16, + 66, + 16, + 16, + 16, + 36, + 16, + 56, + 16, + 16, + 36, + 16, + 26, + 16, + 36, + 16, + 66, + 16, + 76, + 16, + 16, + 16, + 46, + 16, + 16, + 16, + 56, + 16, + 26, + 16, + 46, + 16, + 56, + 16, + 76, + 16, + 56, + 16, + 66, + 16, + 76, + 16, + 86, + 16, + 96, + 16, + 16, + 16, + 56, + 16, + 46, + 16, + 46, + 16, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 49, + 19, + 19, + 69, + 19, + 49, + 19, + 29, + 19, + 49, + 19, + 69, + 19, + 39, + 39, + 39, + 19, + 59, + 19, + 19, + 39, + 19, + 29, + 19, + 39, + 19, + 69, + 19, + 79, + 19, + 19, + 19, + 49, + 19, + 19, + 19, + 59, + 19, + 29, + 19, + 49, + 19, + 59, + 19, + 79, + 19, + 59, + 19, + 69, + 19, + 79, + 19, + 89, + 19, + 99, + 19, + 19, + 69, + 59, + 19, + ], + color: '#139BB9', + }, + ], +}; + +export const setTime = (count) => { + const arr = [...new Array(count)].map(() => ''); + for (let i = 0; i < arr.length; i++) { + arr[i] = `15:${String(i).padStart(2, '0')}`; + } + return arr; +}; + +export const setCategories = (count, desc) => { + const arr = [...new Array(count)].map((i, k) => `${k} ${desc}`); + return arr; +}; + +export const AccessoryData = { + temperatureTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentTemperature', + label: 'Текущее, С°', + }, + { + key: 'middleTemperature', + label: 'Среднее, С°', + }, + { + key: 'minTemperature', + label: 'Минимальное, С°', + }, + { + key: 'minDate', + label: 'Дата минимального', + }, + { + key: 'maxTemperature', + label: 'Максимальное, С°', + }, + { + key: 'maxDate', + label: 'Дата максимального', + }, + ], + items: [ + { + name: 'Процессор 1', + currentTemperature: 19, + middleTemperature: 40, + minTemperature: 31, + minDate: { time: '15:15', date: '11.11.2021' }, + maxTemperature: 88, + maxDate: { time: '10:26', date: '15.11.2021' }, + }, + { + name: 'Процессор 2', + currentTemperature: 29, + middleTemperature: 40, + minTemperature: 20, + minDate: { time: '15:45', date: '11.11.2021' }, + maxTemperature: 76, + maxDate: { time: '16:59', date: '16.11.2021' }, + }, + { + name: 'Процессор 3', + currentTemperature: 48, + middleTemperature: 46, + minTemperature: 31, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 69, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + { + name: 'Процессор 4', + currentTemperature: 48, + middleTemperature: 45, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 75, + maxDate: { time: '11:26', date: '16.11.2021' }, + }, + { + name: 'Процессор 5', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 30, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '17.11.2021' }, + }, + { + name: 'Процессор 6', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + ], + }, + frequencyTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentFrequency', + label: 'Текущее, Hz', + }, + { + key: 'baseFrequency', + label: 'Базовое, Hz', + }, + ], + items: [ + { + name: 'Процессор 1', + currentFrequency: 600, + baseFrequency: 400, + }, + { + name: 'Процессор 2', + currentFrequency: 500, + baseFrequency: 470, + }, + { + name: 'Процессор 3', + currentFrequency: 500, + baseFrequency: 450, + }, + { + name: 'Процессор 4', + currentFrequency: 500, + baseFrequency: 470, + }, + { + name: 'Процессор 5', + currentFrequency: 600, + baseFrequency: 470, + }, + { + name: 'Процессор 6', + currentFrequency: 500, + baseFrequency: 400, + }, + ], + }, + powerTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentPower', + label: 'Текущее, Вт', + label2: '', + }, + ], + items: [ + { + name: 'Процессор 1', + currentPower: 91, + }, + { + name: 'Процессор 2', + currentPower: 77, + }, + { + name: 'Процессор 3', + currentPower: 76, + }, + { + name: 'Процессор 4', + currentPower: 74, + }, + { + name: 'Процессор 5', + currentPower: 73, + }, + { + name: 'Процессор 6', + currentPower: 71, + }, + ], + }, +}; diff --git a/src/views/_sila/Processors/DynamicInfo/index.js b/src/views/_sila/Processors/DynamicInfo/index.js new file mode 100644 index 00000000..121c0316 --- /dev/null +++ b/src/views/_sila/Processors/DynamicInfo/index.js @@ -0,0 +1,2 @@ +import ProcessorsDynamicPage from './ProcessorsDynamicPage.vue'; +export default ProcessorsDynamicPage; diff --git a/src/views/_sila/Processors/Specification/AcceleratorSpecificationTable.vue b/src/views/_sila/Processors/Specification/AcceleratorSpecificationTable.vue new file mode 100644 index 00000000..53b31d8b --- /dev/null +++ b/src/views/_sila/Processors/Specification/AcceleratorSpecificationTable.vue @@ -0,0 +1,146 @@ +<template> + <b-table + responsive="md" + show-empty + no-border-collapse + :items="accelerators" + :fields="fields" + > + <template #cell(expandRow)=""> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandAccelerators" + :title="expandRowLabel" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + <template #cell(status)="{ value }"> + <div v-if="value" class="fans-table-col-first__cell"> + <img class="status__img" src="@/assets/images/status/on.svg" /> + <span> + {{ $t('global.status.inWork') }} + </span> + </div> + <div v-else class="fans-table-col-first__cell"> + <img class="status__img" src="@/assets/images/_sila/status/off.svg" /> + <span> + {{ $t('global.status.outWorking') }} + </span> + </div> + </template> + </b-table> +</template> + +<script> +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; + +import { Accelerators } from './helpers'; +export default { + components: { IconChevron }, + mixins: [BVToastMixin, TableRowExpandMixin, DataFormatterMixin], + data() { + return { + isBusy: true, + isAddersСolon: false, + accelerators: Accelerators, + fields: [ + { + key: 'expandRow', + label: '', + thStyle: { width: '3%' }, + sortable: false, + }, + { + key: 'status', + label: 'Статус', + formatter: this.dataFormatter, + thStyle: { width: '15%' }, + }, + { + key: 'name', + label: 'Имя', + formatter: this.dataFormatter, + thStyle: { width: '10%' }, + }, + { + key: 'slot_number', + label: '№ Слота', + formatter: this.dataFormatter, + thStyle: { width: '8%' }, + }, + { + key: 'board_number', + label: '№ Платы', + formatter: this.dataFormatter, + thStyle: { width: '8%' }, + }, + { + key: 'serial_number', + label: 'Серийный номер', + formatter: this.dataFormatter, + thStyle: { width: '15%' }, + }, + { + key: 'frequency', + label: '№ Детали', + formatter: this.dataFormatter, + thStyle: { width: '8%' }, + }, + { + key: 'cores', + label: 'Версия прошивки', + formatter: this.dataFormatter, + thStyle: { width: '10%' }, + }, + ], + expandRowLabel: expandRowLabel, + }; + }, +}; +</script> +<style lang="scss" scoped> +.row { + margin: 0px; + flex-wrap: nowrap; +} +.fans-table-col-first__cell { + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: flex-start; +} + +.status__img { + margin-right: 7px; +} + +.bold-16px { + margin: 24px 0 0 2rem; +} + +.bold-12px__caps { + color: $text-secondary; +} + +.attrib-names { + border-bottom: 1px solid $faint-secondary-primary-10; + color: $text-secondary !important; + font-weight: 600; +} + +.custom-switch { + margin: 0; +} + +.btn-link { + width: 30px !important; + height: 20px !important; +} +</style> diff --git a/src/views/_sila/Processors/Specification/ProcessorsSpecificationPage.vue b/src/views/_sila/Processors/Specification/ProcessorsSpecificationPage.vue new file mode 100644 index 00000000..50362ac2 --- /dev/null +++ b/src/views/_sila/Processors/Specification/ProcessorsSpecificationPage.vue @@ -0,0 +1,117 @@ +<template> + <b-container + id="page-processors" + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.specification')" /> + <page-section class="bootstrap-table__section"> + <span class="bold-16px">{{ + $t('pageInventory.installedProcessors') + }}</span> + <!-- Processors Specification Table --> + <div class="capability-info"> + <b-row> + <b-col class="mt-0 mb-2 p-0 bold-12px__caps"> + {{ $t('pageInventory.table.processorCapabilityInfo') }} + </b-col> + </b-row> + <b-row> + <b-col class="mt-2 p-0" sm="3" xl="3"> + <dl class="light-12px"> + <dd class="attrib-names"> + {{ $t('global.table.attributes') }} + </dd> + <dd>{{ 'Многопоточность' }}</dd> + <dd>{{ 'Виртуализация' }}</dd> + <dd>{{ 'Турбо режим' }}</dd> + </dl> + </b-col> + <b-col class="mt-2 p-0" sm="3" xl="3"> + <dl class="light-12px"> + <dd class="attrib-names">{{ 'Состояние присутсвия' }}</dd> + <dd + v-for="item in processors[0].presence_status" + :key="item.presence_status" + > + {{ item }} + </dd> + </dl> + </b-col> + <b-col class="mt-2 p-0" sm="3" xl="3"> + <dl class="light-12px"> + <!-- Status state --> + <dd class="attrib-names">{{ 'Включен' }}</dd> + <dd> + <b-form-checkbox + v-model="processors[0].statuses.multithreading" + switch + > + </b-form-checkbox> + </dd> + <dd> + <b-form-checkbox + v-model="processors[0].statuses.virtualization" + switch + > + </b-form-checkbox> + </dd> + <dd> + <b-form-checkbox v-model="processors[0].statuses.turbo" switch> + </b-form-checkbox> + </dd> + </dl> + </b-col> + </b-row> + </div> + <processors-specification-table /> + <span class="bold-16px">{{ + $t('pageInventory.installedAccelerator') + }}</span> + <!-- Accelerators Specification Table --> + <accelerator-specification-table /> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import { processors } from './helpers'; + +import ProcessorsSpecificationTable from './ProcessorsSpecificationTable'; +import AcceleratorSpecificationTable from './AcceleratorSpecificationTable'; +export default { + components: { + PageTitle, + PageSection, + ProcessorsSpecificationTable, + AcceleratorSpecificationTable, + }, + data() { + return { + processors, + }; + }, +}; +</script> +<style lang="scss" scoped> +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} + +.capability-info { + padding-left: 1rem; +} + +.attrib-names { + border-bottom: 1px solid $faint-secondary-primary-10; + color: $text-secondary !important; + font-weight: 600; +} + +.custom-switch { + margin: 0; +} +</style> diff --git a/src/views/_sila/Processors/Specification/ProcessorsSpecificationTable.vue b/src/views/_sila/Processors/Specification/ProcessorsSpecificationTable.vue new file mode 100644 index 00000000..c872eefc --- /dev/null +++ b/src/views/_sila/Processors/Specification/ProcessorsSpecificationTable.vue @@ -0,0 +1,251 @@ +<template> + <b-table + sort-icon-left + no-sort-reset + :sort-desc="true" + responsive="md" + show-empty + no-border-collapse + :items="processors" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + > + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandProcessors" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + <template #cell(status)="{ value }"> + <div v-if="value" class="fans-table-col-first__cell"> + <img class="status__img" src="@/assets/images/status/on.svg" /> + <span> + {{ $t('global.status.inWork') }} + </span> + </div> + <div v-else class="fans-table-col-first__cell"> + <img class="status__img" src="@/assets/images/_sila/status/off.svg" /> + <span> + {{ $t('global.status.outWorking') }} + </span> + </div> + </template> + + <template #row-details="{ index }"> + <b-container fluid> + <!-- ProcessorCapabilityInfo --> + <b-row> + <b-col class="mt-3 mb-2 p-0 bold-12px__caps"> + {{ $t('pageInventory.table.processorCacheInfo') }} + </b-col> + </b-row> + <b-row> + <b-col class="mt-2 p-0" sm="3" xl="3"> + <dl class="light-12px"> + <!-- Attributes Names --> + <dd class="attrib-names">{{ $t('global.table.attributes') }}</dd> + <dd>{{ 'Уровень кеша' }}</dd> + <dd>{{ 'Максимальный объем' }}</dd> + <dd>{{ 'Установленный объем' }}</dd> + <dd>{{ 'Тип кэша' }}</dd> + <dd>{{ 'Локализация' }}</dd> + <dd>{{ 'Политика записи' }}</dd> + <dd>{{ 'Ассоциативность' }}</dd> + <dd>{{ 'Тип исправления ошибки' }}</dd> + </dl> + </b-col> + <b-col class="mt-2 p-0" sm="3" xl="3"> + <dl class="light-12px"> + <!-- Status КЭШ-1 --> + <dd class="attrib-names">{{ 'КЭШ-1' }}</dd> + <dd v-for="item in processors[index].cache_1" :key="item.cache_1"> + {{ dataFormatter(item) }} + </dd> + </dl> + </b-col> + <b-col class="mt-2 p-0" sm="3" xl="3"> + <dl class="light-12px"> + <!-- Status state --> + <dd class="attrib-names">{{ 'КЭШ-2' }}</dd> + <dd v-for="item in processors[index].cache_2" :key="item.cache_2"> + {{ dataFormatter(item) }} + </dd> + </dl> + </b-col> + <b-col class="mt-2 p-0" sm="3" xl="3"> + <dl class="light-12px"> + <dd class="attrib-names">{{ 'КЭШ-3' }}</dd> + <dd v-for="item in processors[index].cache_3" :key="item.cache_3"> + {{ dataFormatter(item) }} + </dd> + </dl> + </b-col> + </b-row> + <b-row> + <b-col class="mt-3 mb-2 p-0 bold-12px__caps"> + {{ $t('pageInventory.table.SupportedTechnologies') }} + </b-col> + </b-row> + <b-row> + <b-col class="mt-2 p-0" sm="6" xl="6"> + <dl class="light-12px"> + <dd class="attrib-names">{{ $t('global.table.attributes') }}</dd> + <dd></dd> + <dd>{{ 'processor' }}</dd> + <dd>{{ 'vendor_id' }}</dd> + <dd>{{ 'cpu family' }}</dd> + <dd>{{ 'model' }}</dd> + <dd>{{ 'model name' }}</dd> + <dd>{{ 'stepping' }}</dd> + <dd>{{ 'microcode' }}</dd> + <dd>{{ 'cpu MHz' }}</dd> + <dd>{{ 'cache size' }}</dd> + <dd>{{ 'physical id' }}</dd> + <dd>{{ 'siblings' }}</dd> + <dd>{{ 'core id' }}</dd> + <dd>{{ 'cpu cores' }}</dd> + <dd>{{ 'apicid' }}</dd> + <dd>{{ 'initial apicid' }}</dd> + <dd>{{ 'fpu' }}</dd> + <dd>{{ 'fpu_exception' }}</dd> + <dd>{{ 'cpuid level' }}</dd> + <dd>{{ 'wp' }}</dd> + <dd>{{ 'flags' }}</dd> + <dd>{{ 'bugs' }}</dd> + </dl> + </b-col> + <b-col class="mt-2 p-0" sm="6" xl="6"> + <dl class="light-12px"> + <dd class="attrib-names">{{ $t('global.table.value') }}</dd> + <dd + v-for="item in processors[index].description" + :key="item.description" + > + {{ dataFormatter(item) }} + </dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> +</template> + +<script> +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; + +import { processors } from './helpers'; +export default { + components: { IconChevron }, + mixins: [BVToastMixin, TableRowExpandMixin, DataFormatterMixin], + data() { + return { + isBusy: true, + isAddersСolon: false, + processors, + fields: [ + { + key: 'expandRow', + label: '', + thStyle: { width: '3%' }, + sortable: false, + }, + { + key: 'status', + label: 'Статус', + formatter: this.dataFormatter, + thStyle: { width: '17%' }, + }, + { + key: 'name', + label: 'Имя', + formatter: this.dataFormatter, + thStyle: { width: '15%' }, + }, + { + key: 'model', + label: 'Модель', + formatter: this.dataFormatter, + thStyle: { width: '12%' }, + }, + { + key: 'serialNumber', + label: 'Серийный номер', + formatter: this.dataFormatter, + thStyle: { width: '15%' }, + }, + { + key: 'version', + label: 'Версия', + formatter: this.dataFormatter, + thStyle: { width: '10%' }, + }, + { + key: 'frequency', + label: 'Частота', + formatter: this.dataFormatter, + thStyle: { width: '10%' }, + }, + { + key: 'cores', + label: 'Ядра', + formatter: this.dataFormatter, + thStyle: { width: '10%' }, + }, + ], + expandRowLabel: expandRowLabel, + }; + }, +}; +</script> +<style lang="scss" scoped> +.row { + margin: 0px; + flex-wrap: nowrap; +} +.fans-table-col-first__cell { + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: flex-start; +} + +.status__img { + margin-right: 7px; +} + +.bold-16px { + margin: 24px 0 0 2rem; +} + +.bold-12px__caps { + color: $text-secondary; +} + +.attrib-names { + border-bottom: 1px solid $faint-secondary-primary-10; + color: $text-secondary !important; + font-weight: 600; +} + +.custom-switch { + margin: 0; +} + +.btn-link { + width: 30px !important; + height: 20px !important; +} +</style> diff --git a/src/views/_sila/Processors/Specification/helpers.js b/src/views/_sila/Processors/Specification/helpers.js new file mode 100644 index 00000000..6227e4b8 --- /dev/null +++ b/src/views/_sila/Processors/Specification/helpers.js @@ -0,0 +1,254 @@ +export const processors = [ + { + expandRow: false, + status: true, + name: 'Процессор №1', + model: 'Core i5', + serialNumber: '789578456698', + version: 'v3.1.5', + frequency: '2.4', + cores: '4', + presence_status: { + multithreading: 'Нет', + virtualization: 'Да', + turbo: 'Да', + }, + statuses: { + multithreading: false, + virtualization: true, + turbo: true, + }, + cache_1: { + level: 'L1', + max_value: '1MB', + current_value: '1MB', + cache_type: 'Unified', + localization: 'Internal', + politics: 'Write Back', + associativity: '8-Way Set-Associativity', + fix_type: 'Parity', + }, + cache_2: { + level: 'L2', + max_value: '32MB', + current_value: '32MB', + cache_type: 'Unified', + localization: 'Internal', + politics: 'Write Back', + associativity: '8-Way Set-Associativity', + fix_type: 'Multiple-bit ECC', + }, + cache_3: { + level: 'L3', + max_value: '64MB', + current_value: '64MB', + cache_type: 'Unified', + localization: 'Internal', + politics: 'Write Back', + associativity: '8-Way Set-Associativity', + fix_type: 'Multiple-bit ECC', + }, + description: { + processor: '79', + vendor_id: 'GenuineIntel', + cpu_family: '6', + model: '85', + model_name: 'Intel(R) Xeon(R) Gold 6242R CPU @ 3.10GHz', + stepping: '7', + microcode: '0x5002f00', + cpu_MHz: '2955.939', + cache_size: '36608 KB', + physical_id: '1', + siblings: '40', + core_id: '29', + cpu_cores: '20', + apicid: '123', + initial_apicid: '123', + fpu: 'yes', + fpu_exception: 'yes', + cpuid_level: '22', + wp: 'yes', + flags: 'fpu, vme, de, pse, tsc, pat, pse36....', + bugs: 'spectre_v1 spectre_v2, spec_store_bypass', + }, + }, + { + expandRow: false, + status: true, + name: 'Процессор №2', + model: 'Core i3', + serialNumber: '425546788976', + version: 'v2.1.5', + frequency: '1.4', + cores: '2', + presence_status: { + multithreading: 'Нет', + virtualization: 'Да', + turbo: 'Да', + }, + statuses: { + multithreading: false, + virtualization: true, + turbo: true, + }, + cache_1: { + level: 'L1', + max_value: '1MB', + current_value: '1MB', + cache_type: 'Unified', + localization: 'Internal', + politics: 'Write Back', + associativity: '8-Way Set-Associativity', + fix_type: 'Parity', + }, + cache_2: { + level: 'L2', + max_value: '32MB', + current_value: '32MB', + cache_type: 'Unified', + localization: 'Internal', + politics: 'Write Back', + associativity: '8-Way Set-Associativity', + fix_type: 'Multiple-bit ECC', + }, + cache_3: { + level: 'L3', + max_value: '64MB', + current_value: '64MB', + cache_type: 'Unified', + localization: 'Internal', + politics: 'Write Back', + associativity: '8-Way Set-Associativity', + fix_type: 'Multiple-bit ECC', + }, + description: { + processor: '79', + vendor_id: 'GenuineIntel', + cpu_family: '6', + model: '85', + model_name: 'Intel(R) Xeon(R) Gold 6242R CPU @ 3.10GHz', + stepping: '7', + microcode: '0x5002f00', + cpu_MHz: '2955.939', + cache_size: '36608 KB', + physical_id: '1', + siblings: '40', + core_id: '29', + cpu_cores: '20', + apicid: '123', + initial_apicid: '123', + fpu: 'yes', + fpu_exception: 'yes', + cpuid_level: '22', + wp: 'yes', + flags: 'fpu, vme, de, pse, tsc, pat, pse36....', + bugs: 'spectre_v1 spectre_v2, spec_store_bypass', + }, + }, + { + expandRow: false, + status: false, + name: 'Процессор №3', + model: 'Core i7', + serialNumber: '454555556698', + version: 'v6.1.5', + frequency: '3.4', + cores: '6', + presence_status: { + multithreading: 'Нет', + virtualization: 'Да', + turbo: 'Да', + }, + statuses: { + multithreading: false, + virtualization: true, + turbo: true, + }, + cache_1: { + level: 'L1', + max_value: '1MB', + current_value: '1MB', + cache_type: 'Unified', + localization: 'Internal', + politics: 'Write Back', + associativity: '8-Way Set-Associativity', + fix_type: 'Parity', + }, + cache_2: { + level: 'L2', + max_value: '32MB', + current_value: '32MB', + cache_type: 'Unified', + localization: 'Internal', + politics: 'Write Back', + associativity: '8-Way Set-Associativity', + fix_type: 'Multiple-bit ECC', + }, + cache_3: { + level: 'L3', + max_value: '64MB', + current_value: '64MB', + cache_type: 'Unified', + localization: 'Internal', + politics: 'Write Back', + associativity: '8-Way Set-Associativity', + fix_type: 'Multiple-bit ECC', + }, + description: { + processor: '79', + vendor_id: 'GenuineIntel', + cpu_family: '6', + model: '85', + model_name: 'Intel(R) Xeon(R) Gold 6242R CPU @ 3.10GHz', + stepping: '7', + microcode: '0x5002f00', + cpu_MHz: '2955.939', + cache_size: '36608 KB', + physical_id: '1', + siblings: '40', + core_id: '29', + cpu_cores: '20', + apicid: '123', + initial_apicid: '123', + fpu: 'yes', + fpu_exception: 'yes', + cpuid_level: '22', + wp: 'yes', + flags: 'fpu, vme, de, pse, tsc, pat, pse36....', + bugs: 'spectre_v1 spectre_v2, spec_store_bypass', + }, + }, +]; + +export const Accelerators = [ + { + expandRow: false, + status: true, + name: 'Процессор №1', + slot_number: '789578456698', + board_number: '789578456698', + serial_number: '425546788976', + frequency: '2213', + cores: '4', + }, + { + expandRow: false, + status: true, + name: 'Процессор №2', + slot_number: '425546788976', + board_number: '425546788976', + serial_number: '425546788976', + frequency: '1332', + cores: '2', + }, + { + expandRow: false, + status: false, + name: 'Процессор №3', + slot_number: '454555556698', + board_number: '454555556698', + serial_number: '425546788976', + frequency: '3213', + cores: '6', + }, +]; diff --git a/src/views/_sila/Processors/Specification/index.js b/src/views/_sila/Processors/Specification/index.js new file mode 100644 index 00000000..93bbbf10 --- /dev/null +++ b/src/views/_sila/Processors/Specification/index.js @@ -0,0 +1,2 @@ +import ProcessorsSpecificationPage from './ProcessorsSpecificationPage.vue'; +export default ProcessorsSpecificationPage; diff --git a/src/views/_sila/SILA/AnalyticalPanel/AnalyticalPanelPage.vue b/src/views/_sila/SILA/AnalyticalPanel/AnalyticalPanelPage.vue new file mode 100644 index 00000000..533fbfa1 --- /dev/null +++ b/src/views/_sila/SILA/AnalyticalPanel/AnalyticalPanelPage.vue @@ -0,0 +1,272 @@ +<template> + <b-container fluid="xxl pt-0 m-0"> + <page-title :description="$t('appNavigation.analyticalPanel')" /> + <div class="tables-container"> + <div class="server-table"> + <span class="semi-bold-16px">{{ + $t('global.status.serverStatus') + }}</span> + <b-table + show-empty + responsive="md" + class="table-analytical" + no-border-collapse + :items="serverItems" + :fields="server_fields" + > + <template #cell(value)="{ value }"> + <b-col v-if="value"> + <img + src="@/assets/images/icon-ok.svg" + class="system-network-table__icon" + /> + </b-col> + <b-col v-else> + <img + src="@/assets/images/icon-no.svg" + class="system-network-table__icon" + /> + </b-col> + </template> + </b-table> + </div> + <div class="events-table"> + <span class="semi-bold-16px">{{ $t('global.action.events') }}</span> + <div id="events-table-wrapper"> + <b-table + show-empty + :sticky-header="stickyHeader" + responsive="md" + class="table-analytical" + no-border-collapse + :items="eventsItems" + :fields="events_fields" + > + <template #cell(date)="{ value }"> + <img + src="@/assets/images/_sila/event-logs/time.svg" + class="sourse__img" + /> + <span class="regular-12px quaternary"> {{ value }} </span> + </template> + <template #cell(type)="{ value }"> + <span + v-if="value === 'Критические'" + class="bold-12px__caps errors" + >{{ value }}</span + > + <span + v-else-if="value === 'Предупреждения'" + class="bold-12px__caps warning" + > + {{ value }} + </span> + <span v-else class="bold-12px__caps information">{{ + value + }}</span> + </template> + <template #cell(description)="data"> + <b-row class="semi-bold-16px tretiatry"> + {{ data.item.description.name }} + </b-row> + <b-row + class="regular-12px tretiatry pointer" + @click="toggleRowDetails(data)" + > + {{ data.item.description.description }}</b-row + > + </template> + <template #row-details="{ index }"> + <b-container fluid> + <b-col class="mt-1 mb-2 regular-12px tretiatry"> + <span class="row-details"> + {{ + `Краткое описание ошибки или сообщение которое состоит из текста указывающего на предмет ошибки.Краткое описание ошибки или сообщение которое состоит из текста указывающего на предмет ошибки. + Краткое описание ошибки или сообщение которое состоит из текста указывающего на предмет ошибки.Краткое описание ошибки или сообщение которое состоит из текста указывающего на предмет ошибки.` + }} + {{ $t(eventsItems[index].description.description) }} + </span> + </b-col> + </b-container> + </template> + </b-table> + </div> + </div> + </div> + <div class="indicators-container"> + <span class="bold-16px">{{ $t('global.status.indicators') }}</span> + <span class="indicators-units regular-12px tretiatry"> + {{ $t('global.units.unit') }}</span + > + <b-button id="popover-choice-1" variant="unstyled"> + <span class="regular-12px units-label"> + {{ + $t(`global.units.${unit === 'Ампераж' ? 'amper' : 'volt'}`) + }}</span + > + <img class="units__icon" src="@/assets/images/_sila/icon-chevron-red.svg" /> + </b-button> + </div> + <two-chioce-popover + :id="1" + fitst-option="Ампераж" + second-option="Вольтаж" + :chosen-option="unit" + :first-action="selectAmper" + :second-action="selectVolt" + /> + <date-switch :switch-time-scale="switchTimeScale" :time-scale="timeScale" /> + <accessory-table :records="accessoryData.power" /> + <power-table :time-scale="timeScale" /> + <div class="tables-container"> + <span class="semi-bold-16px">Управление</span> + </div> + <control-system /> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import TableRowExpandMixin from '@/components/Mixins/TableRowExpandMixin'; +import DateSwitch from '@/components/Global/SilaComponents/DateSwitch'; +import AccessoryTable from '@/components/Global/SilaComponents/Tables/AccessoryTableWithLabel'; +import ControlSystem from '@/components/Global/SilaComponents/InventoryControlSystem'; +import TwoChiocePopover from '@/components/Global/SilaComponents/TwoChiocePopover'; + +import PowerTable from './PowerTable'; +import { AccessoryData, ServerItems, EventsItems } from './helpers'; +export default { + components: { + PageTitle, + DateSwitch, + AccessoryTable, + PowerTable, + ControlSystem, + TwoChiocePopover, + }, + mixins: [TableRowExpandMixin], + data() { + return { + unit: 'Ампераж', + isActive: false, + timeScale: 'hour', + stickyHeader: '511px', + accessoryData: AccessoryData, + serverItems: ServerItems, + eventsItems: EventsItems, + server_fields: [ + { + key: 'param', + label: 'Раздел', + formatter: this.dataFormatter, + }, + { + key: 'value', + label: 'Статус', + formatter: this.dataFormatter, + thStyle: { width: '10%' }, + }, + ], + events_fields: [ + { + key: 'date', + label: 'Время', + formatter: this.dataFormatter, + }, + { + key: 'type', + label: 'Тип события', + formatter: this.dataFormatter, + }, + { + key: 'description', + label: 'О событии', + formatter: this.dataFormatter, + }, + ], + }; + }, + methods: { + switchTimeScale(period) { + this.timeScale = period; + }, + selectAmper() { + this.unit = 'Ампераж'; + }, + selectVolt() { + this.unit = 'Вольтаж'; + }, + }, +}; +</script> +<style lang="scss" scoped> +.tables-container { + display: flex; + flex-flow: row nowrap; + margin: 16px 32px; + gap: 24px; +} + +.server-table { + width: 35%; +} + +.events-table { + width: 65%; +} + +#events-table-wrapper div { + overflow-y: overlay; + overflow-x: hidden; + &::-webkit-scrollbar { + position: absolute; + width: 10px; + } + &::-webkit-scrollbar-thumb { + border: 4px solid transparent; + border-top: 43px solid transparent; + background: $faint-secondary-primary-20; + border-radius: 16px; + background-clip: content-box; + } +} + +.row-details { + display: block; + height: auto; + width: 30vw; +} + +.errors { + color: $indicators-errors !important; +} + +.warning { + color: $indicators-warning !important; +} + +.information { + color: $indicators-complementary !important; +} + +.indicators-container { + margin-left: 2rem; + display: flex; + align-items: center; + justify-content: flex-start; +} + +.indicators-units { + margin-left: auto; +} + +.units__icon { + margin: 0 32px 0 5px; + cursor: pointer; +} + +.units-label { + cursor: pointer; + color: $red-brand-primary; +} +</style> diff --git a/src/views/_sila/SILA/AnalyticalPanel/PowerTable.vue b/src/views/_sila/SILA/AnalyticalPanel/PowerTable.vue new file mode 100644 index 00000000..27bb4efa --- /dev/null +++ b/src/views/_sila/SILA/AnalyticalPanel/PowerTable.vue @@ -0,0 +1,82 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'areaspline', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(6, this.timeScale), + title: null, + labels: { + step: 1, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(2.5, 'A'), + min: 0, + max: 4, + title: null, + tickInterval: 1, + minorGridLineColor: '#1A3E5B1A', + }, + series: Series['power'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + areaspline: { + fillOpacity: 0, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/SILA/AnalyticalPanel/TemperatureTable.vue b/src/views/_sila/SILA/AnalyticalPanel/TemperatureTable.vue new file mode 100644 index 00000000..ae52062a --- /dev/null +++ b/src/views/_sila/SILA/AnalyticalPanel/TemperatureTable.vue @@ -0,0 +1,115 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 70, + }, + nonNormal: { + type: Number, + default: 70, + }, + criticalStart: { + type: Number, + default: 70, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'areaspline', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, 'hour'), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'С°'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значение предупреждения, С°', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + }, + series: Series['temperature'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + areaspline: { + fillOpacity: 0, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/SILA/AnalyticalPanel/helpers.js b/src/views/_sila/SILA/AnalyticalPanel/helpers.js new file mode 100644 index 00000000..d1ffd728 --- /dev/null +++ b/src/views/_sila/SILA/AnalyticalPanel/helpers.js @@ -0,0 +1,196 @@ +export const colors = [ + '#CB32F1', + '#F18638', + '#139BB9', + '#E1AB17', + '#175AE1', + '#13B937', +]; + +export const Series = { + power: [ + { + name: 'Sean', + data: [1, 1, 2.7, 0.5, 0.2, 1], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [0.4, 0.3, 1, 1.4, 2, 0.4], + color: '#F18638', + }, + { + name: 'Brendan', + data: [0.5, 2.5, 1, 0.4, 1, 3], + color: '#139BB9', + }, + ], +}; + +export const setTime = (count) => { + const arr = [...new Array(count)].map(() => ''); + for (let i = 0; i < arr.length; i++) { + arr[i] = `15:${i}0`; + } + return arr; +}; + +export const setCategories = (count, desc) => { + const arr = []; + for (let i = 0; i < count; i += 0.5) { + arr.push(`${i} ${desc}`); + } + return arr; +}; + +export const AccessoryData = { + power: { + fields: [ + { + key: 'name', + label: 'Наименование', + }, + { + key: 'power', + label: 'Текущее значение тока, А', + }, + ], + items: [ + { + name: 'Источникк питания 1', + power: '1,3 A', + }, + { + name: 'Источникк питания 2', + power: '1,8 A', + }, + { + name: 'Источникк питания 3', + power: '1,6 A', + }, + ], + }, +}; + +export const ServerItems = [ + { param: 'Сервер №1', value: true }, + { param: 'ВМС', value: true }, + { param: 'Аналитическая панель', value: true }, + { param: 'RAID-контроллеры', value: false }, + { param: 'Модули памяти', value: true }, + { param: 'Процессоры', value: true }, + { param: 'Источники питания', value: true }, + { param: 'Вентиляторы', value: true }, + { param: 'Физические накопители', value: true }, + { param: 'Виртуальные накопители', value: true }, + { param: 'Материнская плата', value: true }, + { param: 'Сетевые адаптеры', value: true }, + { param: 'PCI устройства', value: true }, +]; + +export const EventsItems = [ + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, + { + date: '23.11.2021 в 13.36', + type: 'Критические', + description: { + name: 'Имя источника событий', + description: 'Краткое описание ошибки или сообщени....', + }, + }, +]; diff --git a/src/views/_sila/SILA/AnalyticalPanel/index.js b/src/views/_sila/SILA/AnalyticalPanel/index.js new file mode 100644 index 00000000..7da2938e --- /dev/null +++ b/src/views/_sila/SILA/AnalyticalPanel/index.js @@ -0,0 +1,2 @@ +import AnalyticalPanelPage from './AnalyticalPanelPage.vue'; +export default AnalyticalPanelPage; diff --git a/src/views/_sila/SILA/NetworkAdapters/Dynamic/NetworkDynamicPage.vue b/src/views/_sila/SILA/NetworkAdapters/Dynamic/NetworkDynamicPage.vue new file mode 100644 index 00000000..3745a71f --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/Dynamic/NetworkDynamicPage.vue @@ -0,0 +1,135 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.analyticalPanel')" /> + <date-switch :switch-time-scale="switchTimeScale" :time-scale="timeScale" /> + <div class="limit-container"> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.temperatureWarning') + }}</span> + <b-form-input + v-model="temperatureWarningInput" + type="number" + :min="0" + :max="100" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button + class="save-button" + variant="primary" + @click="updateTemperature" + > + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Temperature Table --> + <temperature-table + :time-scale="timeScale" + :warning="temperatureWarning" + :non-normal="temperatureNonNormal" + :critical-start="temperatureCriticalStart" + /> + <accessory-table :records="accessoryData.temperatureTable" /> + </b-container> +</template> +<script> +import PageTitle from '@/components/Global/PageTitle'; +import DateSwitch from '@/components/Global/SilaComponents/DateSwitch'; +import TemperatureTable from './TemperatureTable'; +import AccessoryTable from '@/components/Global/SilaComponents/Tables/AccessoryTableWithLabel'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +import { AccessoryData } from './helpers'; + +export default { + components: { + PageTitle, + DateSwitch, + TemperatureTable, + AccessoryTable, + }, + data() { + return { + timeScale: 'hour', + temperatureWarning: 72, + temperatureWarningInput: 72, + notificationInput: 42, + accessoryData: AccessoryData, + iconChevronUp: iconChevronUp, + }; + }, + methods: { + switchTimeScale(period) { + this.timeScale = period; + }, + updateTemperature() { + this.temperatureWarning = +this.temperatureWarningInput; + }, + }, +}; +</script> +<style lang="scss" scoped> +//nav items style +.nav-item, +.nav-link { + padding: 0; +} +.nav-item { + list-style-type: none; +} + +a { + color: $text-primary !important; + &:hover { + color: $text-primary !important; + } +} + +.notification-button { + border: none; + background: none; +} + +.semi-bold-12px { + z-index: 1001; +} + +// temperature limit comtainer +.limit-container { + height: 85px; + width: 100%; + padding: 0 32px 10px 32px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: flex-end; + gap: 24px; +} + +.trmperature-limt { + height: 60px; + width: 100%; + max-width: 270px; + display: flex; + align-items: baseline; + flex-flow: row wrap; + gap: 8px; +} + +.form-control { + height: 36px; + &.non-normal { + width: 125px; + } +} +.save-button { + width: 151px; + height: 36px; +} +</style> diff --git a/src/views/_sila/SILA/NetworkAdapters/Dynamic/TemperatureTable.vue b/src/views/_sila/SILA/NetworkAdapters/Dynamic/TemperatureTable.vue new file mode 100644 index 00000000..1bbf7e08 --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/Dynamic/TemperatureTable.vue @@ -0,0 +1,112 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 70, + }, + nonNormal: { + type: Number, + default: 70, + }, + criticalStart: { + type: Number, + default: 70, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, 'hour'), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'С°'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значение предупреждения, С°', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + }, + series: Series['temperature'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/SILA/NetworkAdapters/Dynamic/helpers.js b/src/views/_sila/SILA/NetworkAdapters/Dynamic/helpers.js new file mode 100644 index 00000000..65046d01 --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/Dynamic/helpers.js @@ -0,0 +1,356 @@ +export const colors = ['#CB32F1', '#F18638', '#139BB9', '#E1AB17']; + +export const Series = { + temperature: [ + { + name: 'Sean', + data: [ + 7, + 7, + 7, + 17, + 7, + 27, + 7, + 7, + 47, + 7, + 7, + 7, + 37, + 7, + 7, + 7, + 27, + 7, + 7, + 7, + 27, + 7, + 7, + 7, + 47, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 27, + 7, + 7, + 7, + 7, + 7, + 27, + 7, + 7, + 7, + 7, + 7, + 7, + 37, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 20, + 10, + 10, + 30, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 50, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 20, + 10, + 10, + 10, + 10, + 10, + 30, + 10, + 10, + 10, + 10, + 10, + 10, + 19, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + ], + color: '#F18638', + }, + { + name: 'Brendan', + data: [ + 13, + 13, + 13, + 13, + 23, + 13, + 33, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 23, + 13, + 43, + 13, + 53, + 13, + 13, + 13, + 53, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 53, + 13, + 43, + 13, + 13, + 63, + 13, + 43, + 13, + 23, + 13, + 43, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + ], + color: '#139BB9', + }, + { + name: 'Matteo', + data: [ + 24, + 14, + 44, + 14, + 14, + 14, + 14, + 14, + 34, + 14, + 14, + 14, + 14, + 34, + 14, + 24, + 14, + 34, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 44, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 44, + 14, + 54, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 84, + 14, + 94, + 14, + 14, + 14, + 54, + 14, + 44, + 14, + 44, + 14, + 14, + 44, + 14, + ], + color: '#E1AB17', + }, + ], +}; + +export const setTime = (count) => { + const arr = [...new Array(count)].map(() => ''); + for (let i = 0; i < arr.length; i++) { + arr[i] = `15:${String(i).padStart(2, '0')}`; + } + return arr; +}; + +export const setCategories = (count, desc) => { + const arr = [...new Array(count)].map((i, k) => `${k} ${desc}`); + return arr; +}; + +export const AccessoryData = { + temperatureTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentTemperature', + label: 'Текущее, С°', + }, + { + key: 'middleTemperature', + label: 'Среднее, С°', + }, + { + key: 'minTemperature', + label: 'Минимальное, С°', + }, + { + key: 'minDate', + label: 'Дата минимального', + }, + { + key: 'maxTemperature', + label: 'Максимальное, С°', + }, + { + key: 'maxDate', + label: 'Дата максимального', + }, + ], + items: [ + { + name: 'Приемопередитчик 1', + currentTemperature: 19, + middleTemperature: 40, + minTemperature: 31, + minDate: { time: '15:15', date: '11.11.2021' }, + maxTemperature: 88, + maxDate: { time: '10:26', date: '15.11.2021' }, + }, + { + name: 'Приемопередитчик 2', + currentTemperature: 29, + middleTemperature: 40, + minTemperature: 20, + minDate: { time: '15:45', date: '11.11.2021' }, + maxTemperature: 76, + maxDate: { time: '16:59', date: '16.11.2021' }, + }, + { + name: 'Приемопередитчик 3', + currentTemperature: 48, + middleTemperature: 46, + minTemperature: 31, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 69, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + { + name: 'Приемопередитчик 4', + currentTemperature: 48, + middleTemperature: 45, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 75, + maxDate: { time: '11:26', date: '16.11.2021' }, + }, + ], + }, +}; diff --git a/src/views/_sila/SILA/NetworkAdapters/Dynamic/index.js b/src/views/_sila/SILA/NetworkAdapters/Dynamic/index.js new file mode 100644 index 00000000..081b5560 --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/Dynamic/index.js @@ -0,0 +1 @@ +export { default } from './NetworkDynamicPage.vue'; diff --git a/src/views/_sila/SILA/NetworkAdapters/EthernetAdapters/EthernetAdaptersPage.vue b/src/views/_sila/SILA/NetworkAdapters/EthernetAdapters/EthernetAdaptersPage.vue new file mode 100644 index 00000000..62a4bcab --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/EthernetAdapters/EthernetAdaptersPage.vue @@ -0,0 +1,399 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.ethernetAdapters')" /> + <data-tabs + :slots="adaptersSlots" + :switch-tab="switchAdapterSlot" + :current-tab="currentAdapterSlot" + :slot-width="85" + :slider-width="60" + style="margin-top: 16px" + /> + <page-section class="bootstrap-table__section"> + <div class="settings-container"> + <span class="bold-16px">{{ $t('appNavigation.settings') }}</span> + <apply-settings-popover + :appaly-on-reload="appalyOnReload" + :appaly-option1="appalyOption1" + :appaly-option2="appalyOption2" + :apply-type="applyType" + /> + <b-button variant="primary" class="apply-button"> + {{ $t('global.action.acceptChanges') }} + </b-button> + </div> + <span class="bold-12px__caps"> + {{ $t('pageNetwork.macAddress') }} + </span> + <b-table + responsive="md" + class="table-stripes" + no-border-collapse + :items="items" + :fields="fields" + > + <template #cell(value)="data"> + <b-row> + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + <b-col> + <img + src="@/assets/images/icon-edit.svg" + class="icon-chevron icon-edit" + /> + </b-col> + </b-row> + </template> + </b-table> + <span class="bold-12px__caps"> + {{ $t('adapters.settings') }} + </span> + <b-table + responsive="md" + class="table-stripes" + no-border-collapse + :items="items_2" + :fields="fields" + > + <template #cell(value)="data"> + <b-row v-if="data.index === 3" class="multiple-select-container"> + <b-col v-if="selected.length" class="chip-column"> + <b-row class="chip-container"> + <div + v-for="item in selected" + :key="item.value" + class="custom-chip" + > + <span> {{ item.text }} </span> + <img + class="clear-icon" + src="@/assets/images/_sila/popups/x-icon.svg" + @click="optionRemove(item.value)" + /> + </div> + </b-row> + </b-col> + <b-col v-else> + {{ 'Не выбрано ' }} + </b-col> + <b-col class="icon-container"> + <b-button id="popover-boot" variant="toogle-popover"> + <img :is="iconChevron" class="icon-chevron" /> + </b-button> + <b-popover + placement="left" + triggers="focus" + :show.sync="show" + custom-class="boot-popover" + target="popover-boot" + > + <b-form-group + v-slot="{ ariaDescribedby }" + class="checkbox-group" + > + <b-form-checkbox-group + v-model="selected" + :options="options" + :aria-describedby="ariaDescribedby" + class="regular-14px checkbox-container" + stacked + ></b-form-checkbox-group> + </b-form-group> + </b-popover> + </b-col> + </b-row> + <b-row v-else> + <b-col> + <span> + {{ data.value ? 'Возможно' : 'Не возможно' }} + </span> + </b-col> + <b-col> + <img :is="iconChevron" class="icon-chevron icon-options" /> + </b-col> + </b-row> + </template> + </b-table> + <span class="bold-12px__caps"> + {{ $t('adapters.propherties') }} + </span> + <b-table + responsive="md" + class="table-stripes" + no-border-collapse + :items="items_3" + :fields="fields" + > + </b-table> + <div class="settings-container"> + <span class="bold-16px">{{ $t('adapters.PXE') }}</span> + <apply-settings-popover + :id="2" + :appaly-on-reload="appalyOnReload" + :appaly-option1="appalyOption1" + :appaly-option2="appalyOption2" + :apply-type="applyType" + /> + </div> + <b-table + responsive="md" + class="table-stripes" + no-border-collapse + :items="items_4" + :fields="fields" + > + <template #cell(value)="data"> + <b-row class="flex-row"> + <b-col> + <span> + {{ data.value ? 'Включено' : 'Выключено' }} + </span> + </b-col> + <b-col> + <b-form-checkbox v-model="items_4[data.index].value" switch> + </b-form-checkbox> + </b-col> + </b-row> + </template> + </b-table> + <div class="settings-container"> + <span class="bold-16px">{{ $t('adapters.links') }}</span> + </div> + <b-table + responsive="md" + class="table-stripes" + no-border-collapse + :items="items_5" + :fields="fields" + > + </b-table> + <div class="settings-container"> + <span class="bold-16px">{{ $t('adapters.transceivers') }}</span> + </div> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items_6" + :fields="fields_2" + :empty-text="$t('global.table.emptyMessage')" + > + </b-table> + <div class="microcode-container"> + <span class="bold-16px">{{ + $t('SystemDescription.microcodeAdapter') + }}</span> + <popover + id="popover-reactive-1" + description="adapters.microcodeUpdate" + popup="adapters.microcodeUpdate_popup" + button="global.action.refresh" + :is-microcode-drivers="true" + /> + </div> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import Popover from '@/components/Global/Popover'; +import ApplySettingsPopover from '@/components/Global/SilaComponents/ApplySettingsPopover'; +import DataTabs from '@/components/Global/SilaComponents/DataTabs'; + +import iconChevron from '@carbon/icons-vue/es/chevron--down/16'; +import { items, items_2, items_3, items_4, items_5, items_6 } from './helpers'; + +export default { + components: { + PageTitle, + PageSection, + DataTabs, + Popover, + ApplySettingsPopover, + }, + data() { + return { + items, + items_2, + items_3, + items_4, + items_5, + items_6, + iconChevron, + selected: [], + show: false, + options: [ + { text: 'PXE', value: { text: 'PXE', value: 'pxe' } }, + { text: 'ISCSI-PXE', value: { text: 'ISCSI-PXE', value: 'iscsi-pxe' } }, + { text: 'ISCSI', value: { text: 'ISCSI', value: 'iscsi' } }, + { text: 'PXE2', value: { text: 'PXE2', value: 'pxe2' } }, + { text: 'ISCSI 2', value: { text: 'ISCSI 2', value: 'iscsi2' } }, + { + text: 'ISCSI-PXE 2', + value: { text: 'ISCSI-PXE 2', value: 'iscsi-pxe2' }, + }, + ], + applyType: 'reload', + currentAdapterSlot: 1, + adaptersSlots: [ + { id: 1, name: 'Адаптер 1' }, + { id: 2, name: 'Адаптер 2' }, + { id: 3, name: 'Адаптер 3' }, + ], + fields: [ + { + key: 'param', + label: '', + formatter: this.dataFormatter, + }, + { + key: 'value', + label: '', + formatter: this.dataFormatter, + }, + ], + fields_2: [ + { + key: 'name', + label: 'Параметр', + formatter: this.dataFormatter, + }, + { + key: 'model', + label: 'Модель', + formatter: this.dataFormatter, + }, + { + key: 'number', + label: 'Серийный номер', + formatter: this.dataFormatter, + }, + { + key: 'type', + label: 'Тип устройства', + formatter: this.dataFormatter, + }, + { + key: 'firmware', + label: 'Прошивка', + formatter: this.dataFormatter, + }, + { + key: 'status', + label: 'Статус', + formatter: this.dataFormatter, + }, + ], + }; + }, + methods: { + switchAdapterSlot(period) { + this.currentAdapterSlot = period; + }, + appalyOnReload() { + this.applyType = 'reload'; + }, + appalyOption1() { + this.applyType = 'option1'; + }, + appalyOption2() { + this.applyType = 'option2'; + }, + optionRemove(value) { + this.selected.splice( + this.selected.findIndex((el) => el.value === value), + 1 + ); + console.log(value); + }, + }, +}; +</script> +<style lang="scss" scoped> +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} + +.settings-container { + display: flex; + align-items: center; + justify-content: flex-start; +} + +.apply-button { + width: 245px; + height: 36px; +} + +.microcode-container { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: baseline; + justify-content: space-between; +} + +.bold-12px__caps { + display: block; + margin: 20px 0 8px 0; +} + +.flex-row { + align-items: center; +} + +.multiple-select-container { + justify-content: flex-end; +} + +.chip-column { + padding-right: 0px; +} + +.chip-container { + margin-left: -5px; +} + +.custom-chip { + display: flex; + align-items: center; + height: 24px; + margin: 2px 3px; + padding: 0 10px; + background-color: $faint-secondary-primary-10; + border-radius: 94px; +} + +.icon-container { + flex-grow: 0; + padding: 0px; + margin: 0 11px 0 5px; +} + +.clear-icon { + width: 10px; + margin-left: 6px; + cursor: pointer; +} + +.checkbox-group { + margin: 0; +} + +.checkbox-container { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + gap: 7px; +} +</style> diff --git a/src/views/_sila/SILA/NetworkAdapters/EthernetAdapters/helpers.js b/src/views/_sila/SILA/NetworkAdapters/EthernetAdapters/helpers.js new file mode 100644 index 00000000..918f5b4e --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/EthernetAdapters/helpers.js @@ -0,0 +1,99 @@ +export const items = [ + { + param: 'MAC адрес', + value: 'D0:94:66:2A:77:33', + }, + { + param: 'Виртуальный MAC адрес', + value: 'D0:94:66:2A:77:33', + }, +]; +export const items_2 = [ + { + param: 'Пробуждение по локальной сети', + value: true, + }, + { + param: 'Менеджмент проходит через', + value: true, + }, + { + param: 'Энергоэффективный Ethernet', + value: true, + }, + { + param: 'Поддерживаемый boot протокол', + value: true, + }, + { + param: 'Способность политики сохранения', + value: true, + }, +]; +export const items_3 = [ + { + param: 'Версия прошивки', + value: '20.6.6.2', + }, + { + param: 'Версия драйвера', + value: '3.134', + }, + { + param: 'PCI device ID', + value: '165F', + }, +]; + +export const items_4 = [ + { + param: 'Встроенные порты PXE', + value: true, + }, +]; + +export const items_5 = [ + { + param: 'Статус', + value: 'Подключено', + }, + { + param: 'Скорость', + value: '1000 Mbps', + }, +]; + +export const items_6 = [ + { + name: 'Приемопередатчик 1', + model: 'A0507', + number: '789578456698', + type: 'Плата', + firmware: 'v 21.6.0', + status: 'Link', + }, + { + name: 'Приемопередатчик 2', + model: 'A0507', + number: '543636577755', + type: 'Плата', + firmware: 'v 21.6.0', + status: 'Receiver Signal Averange Optical Power', + }, + { + name: 'Приемопередатчик 3', + model: 'A0408', + number: '789578456698', + type: 'Плата', + firmware: 'v 21.6.0', + status: 'Laser Output Power', + }, + { + name: 'Приемопередатчик 4', + model: 'A0408', + number: '123456789087', + type: 'Плата', + firmware: 'v 21.6.0', + status: 'Laser Output Power', + }, +]; diff --git a/src/views/_sila/SILA/NetworkAdapters/EthernetAdapters/index.js b/src/views/_sila/SILA/NetworkAdapters/EthernetAdapters/index.js new file mode 100644 index 00000000..47f49789 --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/EthernetAdapters/index.js @@ -0,0 +1 @@ +export { default } from './EthernetAdaptersPage.vue'; diff --git a/src/views/_sila/SILA/NetworkAdapters/FcHbaAdapters/FcHbaAdaptersPage.vue b/src/views/_sila/SILA/NetworkAdapters/FcHbaAdapters/FcHbaAdaptersPage.vue new file mode 100644 index 00000000..9ccc0509 --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/FcHbaAdapters/FcHbaAdaptersPage.vue @@ -0,0 +1,211 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.fcHbaAdapters')" /> + <data-tabs + :slots="adaptersSlots" + :switch-tab="switchAdapterSlot" + :current-tab="currentAdapterSlot" + :slot-width="85" + :slider-width="60" + style="margin-top: 16px" + /> + <page-section class="bootstrap-table__section"> + <div class="settings-container"> + <span class="bold-16px">{{ $t('appNavigation.settings') }}</span> + <apply-settings-popover + :appaly-on-reload="appalyOnReload" + :appaly-option1="appalyOption1" + :appaly-option2="appalyOption2" + :apply-type="applyType" + /> + <b-button variant="primary" class="apply-button"> + {{ $t('global.action.acceptChanges') }} + </b-button> + </div> + <span class="bold-12px__caps"> + {{ $t('adapters.wwnAddres') }} + </span> + <b-table + responsive="md" + class="table-stripes" + no-border-collapse + :items="items" + :fields="fields" + > + <template #cell(value)="data"> + <b-row> + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + <b-col> + <img + src="@/assets/images/icon-edit.svg" + class="icon-chevron icon-edit" + /> + </b-col> + </b-row> + </template> + </b-table> + <span class="bold-12px__caps"> + {{ $t('adapters.settings') }} + </span> + <b-table + responsive="md" + class="table-stripes" + no-border-collapse + :items="items_2" + :fields="fields" + > + <template #cell(value)="data"> + <b-row v-if="data.index === 0 || data.index === 3"> + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + </b-row> + <b-row v-else-if="data.index === 4"> + <b-col> + <span> + {{ data.value ? 'Включено' : 'Выключено' }} + </span> + </b-col> + <b-col> + <img :is="iconChevron" class="icon-chevron icon-options" /> + </b-col> + </b-row> + <b-row v-else> + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + <b-col> + <img :is="iconChevron" class="icon-chevron icon-options" /> + </b-col> + </b-row> + </template> + </b-table> + <span class="bold-12px__caps"> + {{ $t('adapters.propherties') }} + </span> + <b-table + responsive="md" + class="table-stripes" + no-border-collapse + :items="items_3" + :fields="fields" + > + </b-table> + <div class="microcode-container"> + <span class="bold-16px">{{ + $t('SystemDescription.microcodeAdapter') + }}</span> + <popover + id="popover-reactive-1" + description="adapters.microcodeUpdate" + popup="adapters.microcodeUpdate_popup" + button="global.action.refresh" + :is-microcode-drivers="true" + /> + </div> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import Popover from '@/components/Global/Popover'; +import ApplySettingsPopover from '@/components/Global/SilaComponents/ApplySettingsPopover'; +import DataTabs from '@/components/Global/SilaComponents/DataTabs'; + +import iconChevron from '@carbon/icons-vue/es/chevron--down/16'; +import { items, items_2, items_3 } from './helpers'; + +export default { + components: { + PageTitle, + PageSection, + DataTabs, + Popover, + ApplySettingsPopover, + }, + data() { + return { + items, + items_2, + items_3, + iconChevron, + applyType: 'reload', + currentAdapterSlot: 1, + adaptersSlots: [ + { id: 1, name: 'Адаптер 1' }, + { id: 2, name: 'Адаптер 2' }, + { id: 3, name: 'Адаптер 3' }, + ], + fields: [ + { + key: 'param', + label: '', + formatter: this.dataFormatter, + }, + { + key: 'value', + label: '', + formatter: this.dataFormatter, + }, + ], + }; + }, + methods: { + switchAdapterSlot(period) { + this.currentAdapterSlot = period; + }, + appalyOnReload() { + this.applyType = 'reload'; + }, + appalyOption1() { + this.applyType = 'option1'; + }, + appalyOption2() { + this.applyType = 'option2'; + }, + }, +}; +</script> +<style lang="scss" scoped> +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} + +.settings-container { + display: flex; + align-items: center; + justify-content: flex-start; +} + +.apply-button { + width: 245px; + height: 36px; +} + +.microcode-container { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: baseline; + justify-content: space-between; +} + +.bold-12px__caps { + display: block; + margin: 20px 0 8px 0; +} +</style> diff --git a/src/views/_sila/SILA/NetworkAdapters/FcHbaAdapters/helpers.js b/src/views/_sila/SILA/NetworkAdapters/FcHbaAdapters/helpers.js new file mode 100644 index 00000000..a2ace22f --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/FcHbaAdapters/helpers.js @@ -0,0 +1,42 @@ +export const items = [ + { + param: 'WWN адрес', + value: 'D0:94:66:2A:77:33', + }, +]; +export const items_2 = [ + { + param: 'Boot WWPN', + value: 'Имя порта контроллера (WWPN)', + }, + { + param: 'Select Boot Enable', + value: 'Boot Lun', + }, + { + param: 'Boot LUN', + value: 'Том 1', + }, + { + param: 'WWPN', + value: 'Имя порта HBA (WWPN)', + }, + { + param: 'Host Adapter Bios Enable', + value: false, + }, +]; +export const items_3 = [ + { + param: 'Версия прошивки', + value: '20.6.6.2', + }, + { + param: 'Версия драйвера', + value: '3.134', + }, + { + param: 'PCI device ID', + value: '165F', + }, +]; diff --git a/src/views/_sila/SILA/NetworkAdapters/FcHbaAdapters/index.js b/src/views/_sila/SILA/NetworkAdapters/FcHbaAdapters/index.js new file mode 100644 index 00000000..8f5636c3 --- /dev/null +++ b/src/views/_sila/SILA/NetworkAdapters/FcHbaAdapters/index.js @@ -0,0 +1 @@ +export { default } from './FcHbaAdaptersPage.vue'; diff --git a/src/views/_sila/SILA/PciDevices/PciDevicesPage.vue b/src/views/_sila/SILA/PciDevices/PciDevicesPage.vue new file mode 100644 index 00000000..3bc45ca2 --- /dev/null +++ b/src/views/_sila/SILA/PciDevices/PciDevicesPage.vue @@ -0,0 +1,115 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title /> + <page-section + :section-title="$t('appNavigation.pciDevicesTitle')" + class="bootstrap-table__section" + > + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(status)> + <b-row> + <b-col> + <img src="@/assets/images/status/on.svg" class="icon-chevron" /> + <span> + {{ 'Работоспособен' }} + </span> + </b-col> + </b-row> + </template> + </b-table> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; + +export default { + components: { PageTitle, PageSection }, + data() { + return { + fields: [ + { + key: 'status', + label: 'Статус', + formatter: this.dataFormatter, + }, + { + key: 'name', + label: 'Имя', + formatter: this.dataFormatter, + }, + { + key: 'type', + label: 'Тип устройства', + formatter: this.dataFormatter, + }, + { + key: 'model', + label: 'Модель', + formatter: this.dataFormatter, + }, + { + key: 'serial_number', + label: 'Серийный номер', + formatter: this.dataFormatter, + }, + { + key: 'producer', + label: 'Производитель', + formatter: this.dataFormatter, + }, + ], + items: [ + { + status: true, + name: 'PCI-устройство 1', + type: 'Плата', + model: 'A0407', + serial_number: '789578456698', + producer: 'ASUS', + }, + { + status: true, + name: 'PCI-устройство 2', + type: 'Плата', + model: 'A4897', + serial_number: '758496877563', + producer: 'ASUS', + }, + { + status: true, + name: 'PCI-устройство 3', + type: 'Плата', + model: 'A0147', + serial_number: '247856889654', + producer: 'ASUS', + }, + ], + }; + }, + methods: { + switchMemorySlot(period) { + this.currentMemorySlot = period; + }, + redirectDrivers() { + this.$router.push('/drivers-static'); + }, + redirectVirtual() { + this.$router.push('/virtual-drivers'); + }, + }, +}; +</script> diff --git a/src/views/_sila/SILA/PciDevices/index.js b/src/views/_sila/SILA/PciDevices/index.js new file mode 100644 index 00000000..20092914 --- /dev/null +++ b/src/views/_sila/SILA/PciDevices/index.js @@ -0,0 +1 @@ +export { default } from './PciDevicesPage.vue'; diff --git a/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/DriversDynamicPage.vue b/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/DriversDynamicPage.vue new file mode 100644 index 00000000..fadbf2b2 --- /dev/null +++ b/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/DriversDynamicPage.vue @@ -0,0 +1,204 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.dynamicInformation')" /> + <div class="notification__container"> + <span class="semi-bold-12px">{{ + $t('global.ariaLabel.notificationTime') + }}</span> + <div class="form-control notification"> + <b-form-input + v-model="notificationInput" + type="number" + class="notification__input" + > + </b-form-input> + <button class="notification_button"> + <img class="notification__icon" src="@/assets/images/refresh.svg" /> + </button> + </div> + </div> + <date-switch :switch-time-scale="switchTimeScale" :time-scale="timeScale" /> + <div class="limit-container"> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/non-normal.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.nonNormalMode') + }}</span> + <b-form-input + v-model="temperatureNonNormalInput" + type="number" + :min="0" + :max="temperatureCritical" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/critical.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.criticalMode') + }}</span> + <b-form-input + v-model="temperatureCritical" + type="number" + :min="temperatureNonNormalInput" + :max="temperatureWarningInput" + class="form-control medium-12px" + > + </b-form-input> + </div> + <div class="trmperature-limt"> + <img src="@/assets/images/labels/warning.svg" /> + <span class="semi-bold-12px">{{ + $t('tablesDescription.temperatureWarning') + }}</span> + <b-form-input + v-model="temperatureWarningInput" + type="number" + :min="temperatureCritical" + :max="100" + class="form-control medium-12px" + > + </b-form-input> + </div> + <b-button + class="save-button" + variant="primary" + @click="updateTemperatureLimits" + > + {{ $t('global.action.save') }} + </b-button> + </div> + <!-- Temperature Table --> + <temperature-table + :time-scale="timeScale" + :warning="temperatureWarning" + :non-normal="temperatureNonNormal" + :critical-start="temperatureCriticalStart" + /> + <accessory-table-drivers :records="accessoryData.table" /> + <accessory-table :records="accessoryData.temperatureTable" /> + </b-container> +</template> +<script> +import PageTitle from '@/components/Global/PageTitle'; +import DateSwitch from '@/components/Global/SilaComponents/DateSwitch'; +import TemperatureTable from './TemperatureTable'; +import AccessoryTable from '@/components/Global/SilaComponents/Tables/AccessoryTable'; +import AccessoryTableDrivers from '@/components/Global/SilaComponents/Tables/AccessoryTableDrivers'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +import { AccessoryData } from './helpers'; + +export default { + components: { + PageTitle, + DateSwitch, + TemperatureTable, + AccessoryTable, + AccessoryTableDrivers, + }, + data() { + return { + timeScale: 'hour', + temperatureWarning: 72, + temperatureWarningInput: 72, + temperatureNonNormal: 44, + temperatureNonNormalInput: 44, + temperatureCriticalStart: 55, + temperatureCritical: 55, + notificationInput: 42, + accessoryData: AccessoryData, + iconChevronUp: iconChevronUp, + }; + }, + methods: { + switchTimeScale(period) { + this.timeScale = period; + }, + updateTemperatureLimits() { + this.temperatureWarning = +this.temperatureWarningInput; + this.temperatureNonNormal = +this.temperatureNonNormalInput; + this.temperatureCriticalStart = +this.temperatureCritical; + }, + }, +}; +</script> +<style lang="scss" scoped> +.notification__container { + position: absolute; + top: calc(#{$header-height}); + right: 0px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; +} + +.notification { + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; + border: none; + box-shadow: none; + border-radius: 8px; + margin: 12px 32px 12px 8px; + width: 236px; + height: 40px; + z-index: 1001; +} + +.notification__icon { + width: 20px; + height: 20px; +} + +.notification__input { + border: none; + background: none; + box-shadow: none; +} + +.notification_button { + border: none; + background: none; +} + +.semi-bold-12px { + z-index: 1001; +} + +// temperature limit comtainer +.limit-container { + height: 85px; + width: 100%; + padding: 0 32px 10px 32px; + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: flex-end; + gap: 24px; +} + +.trmperature-limt { + height: 60px; + width: 100%; + max-width: 270px; + display: flex; + align-items: baseline; + flex-flow: row wrap; + gap: 8px; +} + +.form-control { + height: 36px; +} +.save-button { + width: 151px; + height: 36px; +} +</style> diff --git a/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/TemperatureTable.vue b/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/TemperatureTable.vue new file mode 100644 index 00000000..f9c149c3 --- /dev/null +++ b/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/TemperatureTable.vue @@ -0,0 +1,126 @@ +<template> + <div> + <highcharts :options="chartOptions.line" /> + </div> +</template> + +<script> +import { setTime, Series, setCategories } from './helpers'; +import { Chart } from 'highcharts-vue'; + +export default { + components: { + highcharts: Chart, + }, + props: { + timeScale: { + type: String, + default: 'hour', + }, + warning: { + type: Number, + default: 70, + }, + nonNormal: { + type: Number, + default: 70, + }, + criticalStart: { + type: Number, + default: 70, + }, + }, + computed: { + chartOptions() { + return { + line: { + chart: { + type: 'line', + margin: [12, 50, 32, 60], + height: '320px', + }, + title: null, + xAxis: { + categories: setTime(60, 'hour'), + title: null, + labels: { + step: 6, + }, + minorGridLineColor: '#1A3E5B1A', + }, + yAxis: { + categories: setCategories(101, 'С°'), + min: 0, + max: 100, + title: null, + minTickInterval: 25, + minorGridLineColor: '#1A3E5B1A', + plotLines: [ + { + color: '#E11717', + dashStyle: 'solid', + value: this.warning, + zIndex: '1000', + width: 2, + label: { + text: 'Пороговое значение предупреждения, С°', + align: 'right', + style: { + fontFamily: 'Inter, sans-serif', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '16px', + color: '#0C1C2999', + }, + }, + }, + ], + plotBands: [ + { + color: '#F0AC0C1A', + dashStyle: 'solid', + from: this.nonNormal, + to: this.criticalStart, + }, + { + color: '#FF41411A', + dashStyle: 'solid', + from: this.criticalStart, + to: this.warning, + }, + ], + }, + series: Series['temperature'].map((item) => ({ + ...item, + marker: { + enabled: false, + }, + })), + legend: { + enabled: false, + }, + tooltip: { + enabled: false, + crosshairs: false, + }, + plotOptions: { + series: { + showInLegend: true, + }, + }, + }, + }; + }, + }, +}; +</script> +<style lang="scss"> +.highcharts-credits { + display: none; +} + +.highcharts-plot-line-label { + transform: translate(-15px, 0) !important; +} +</style> diff --git a/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/helpers.js b/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/helpers.js new file mode 100644 index 00000000..9115f9e5 --- /dev/null +++ b/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/helpers.js @@ -0,0 +1,564 @@ +export const colors = [ + '#CB32F1', + '#F18638', + '#139BB9', + '#E1AB17', + '#175AE1', + '#13B937', +]; + +export const Series = { + temperature: [ + { + name: 'Sean', + data: [ + 15, + 15, + 45, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 55, + 15, + 25, + 15, + 15, + 15, + 55, + 15, + 75, + 15, + 35, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 45, + 15, + 25, + 15, + 45, + 15, + 15, + 15, + 35, + 35, + 35, + 15, + 55, + 15, + 15, + 35, + 15, + 25, + 15, + 35, + 15, + ], + color: '#CB32F1', + }, + { + name: 'Ivan', + data: [ + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 22, + 12, + 42, + 12, + 12, + 12, + 72, + 12, + 52, + 12, + 62, + 12, + 72, + 12, + 12, + 12, + 12, + 42, + 12, + 12, + 52, + 12, + 42, + 12, + 12, + 62, + 12, + 12, + 12, + 22, + 12, + 42, + 12, + 62, + 12, + 12, + 32, + 12, + 12, + 12, + 52, + 12, + 32, + 12, + 22, + 12, + 12, + 12, + ], + color: '#175AE1', + }, + { + name: 'Brendan', + data: [ + 13, + 13, + 13, + 13, + 23, + 13, + 33, + 13, + 63, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 23, + 13, + 43, + 13, + 53, + 13, + 13, + 13, + 53, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 53, + 13, + 43, + 13, + 13, + 63, + 13, + 13, + 13, + 23, + 13, + 43, + 13, + 63, + 13, + 13, + 13, + 53, + 13, + 53, + ], + color: '#B98D13', + }, + { + name: 'Matteo', + data: [ + 24, + 14, + 44, + 14, + 14, + 14, + 64, + 14, + 14, + 14, + 54, + 14, + 14, + 34, + 14, + 24, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 44, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 44, + 14, + 54, + 14, + 14, + 14, + 54, + 14, + 14, + 14, + 14, + 14, + 84, + 14, + 14, + 14, + 54, + 14, + 14, + 14, + 44, + 14, + 44, + 64, + 14, + 44, + 14, + ], + color: '#13B937', + }, + { + name: 'Joan', + data: [ + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 36, + 16, + 26, + 16, + 36, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 26, + 16, + 46, + 16, + 16, + 16, + 16, + 16, + 56, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 96, + 16, + 16, + 16, + 56, + 16, + 16, + 16, + 46, + 16, + ], + color: '#F18638', + }, + { + name: 'Avinash', + data: [ + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 69, + 19, + 19, + 19, + 19, + 19, + 59, + 19, + 19, + 39, + 19, + 29, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 49, + 19, + 19, + 19, + 59, + 19, + 29, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + color: '#139BB9', + }, + ], +}; + +export const setTime = (count) => { + const arr = [...new Array(count)].map(() => ''); + for (let i = 0; i < arr.length; i++) { + arr[i] = `15:${String(i).padStart(2, '0')}`; + } + return arr; +}; + +export const setCategories = (count, desc) => { + const arr = [...new Array(count)].map((i, k) => `${k} ${desc}`); + return arr; +}; + +export const AccessoryData = { + temperatureTable: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + }, + { + key: 'currentTemperature', + label: 'Текущее, С°', + }, + { + key: 'middleTemperature', + label: 'Среднее, С°', + }, + { + key: 'minTemperature', + label: 'Минимальное, С°', + }, + { + key: 'minDate', + label: 'Дата минимального', + }, + { + key: 'maxTemperature', + label: 'Максимальное, С°', + }, + { + key: 'maxDate', + label: 'Дата максимального', + }, + ], + items: [ + { + name: 'Накопитель 1', + currentTemperature: 19, + middleTemperature: 40, + minTemperature: 31, + minDate: { time: '15:15', date: '11.11.2021' }, + maxTemperature: 88, + maxDate: { time: '10:26', date: '15.11.2021' }, + }, + { + name: 'Накопитель 2', + currentTemperature: 29, + middleTemperature: 40, + minTemperature: 20, + minDate: { time: '15:45', date: '11.11.2021' }, + maxTemperature: 76, + maxDate: { time: '16:59', date: '16.11.2021' }, + }, + { + name: 'Накопитель 3', + currentTemperature: 48, + middleTemperature: 46, + minTemperature: 31, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 69, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + { + name: 'Накопитель 4', + currentTemperature: 48, + middleTemperature: 45, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 75, + maxDate: { time: '11:26', date: '16.11.2021' }, + }, + { + name: 'Накопитель 5', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 30, + minDate: { time: '15:23', date: '11.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '17.11.2021' }, + }, + { + name: 'Накопитель 6', + currentTemperature: 39, + middleTemperature: 44, + minTemperature: 5, + minDate: { time: '16:45', date: '25.11.2021' }, + maxTemperature: 80, + maxDate: { time: '15:26', date: '15.11.2021' }, + }, + ], + }, + table: { + fields: [ + { + key: 'name', + label: 'Имя модуля', + thStyle: { width: '34%' }, + }, + { + key: 'SMART', + label: 'Текущие значения атрибутов SMART', + thStyle: { width: '32%' }, + }, + { + key: 'resource', + label: 'Оставшийся ресурс для SSD, Мб', + thStyle: { width: '39%' }, + }, + ], + items: [ + { + name: 'Накопитель 1', + SMART: 88, + resource: '1 024 000', + }, + { + name: 'Накопитель 2', + SMART: 76, + resource: '512 000', + }, + { + name: 'Накопитель 3', + SMART: 69, + resource: '256 000', + }, + { + name: 'Накопитель 4', + SMART: 75, + resource: '128 000', + }, + { + name: 'Накопитель 5', + SMART: 69, + resource: '256 000', + }, + { + name: 'Накопитель 6', + SMART: 75, + resource: '128 000', + }, + ], + }, +}; diff --git a/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/index.js b/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/index.js new file mode 100644 index 00000000..ed9033ab --- /dev/null +++ b/src/views/_sila/SILA/PhysicalDrivers/DynamicInfo/index.js @@ -0,0 +1 @@ +export { default } from './DriversDynamicPage.vue'; diff --git a/src/views/_sila/SILA/PhysicalDrivers/StaticInfo/DriversStaticPage.vue b/src/views/_sila/SILA/PhysicalDrivers/StaticInfo/DriversStaticPage.vue new file mode 100644 index 00000000..d6f31985 --- /dev/null +++ b/src/views/_sila/SILA/PhysicalDrivers/StaticInfo/DriversStaticPage.vue @@ -0,0 +1,133 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.statisticInformation')" /> + <page-section class="bootstrap-table__section info_section"> + <span class="bold-16px">{{ $t('global.table.info') }}</span> + </page-section> + <data-tabs + :slots="memorySlots" + :switch-tab="switchMemorySlot" + :current-tab="currentMemorySlot" + :slot-width="100" + :slider-width="81" + /> + <page-section class="bootstrap-table__section info_section"> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + </b-table> + <div class="microcode-container"> + <span class="bold-16px">{{ + $t('SystemDescription.microcodeDrivers') + }}</span> + <popover + id="popover-reactive-1" + description="SystemDescription.reloadMicrocodeDrivers" + popup="SystemDescription.reloadMicrocodeDrivers" + button="global.action.refresh" + :is-microcode-drivers="true" + /> + </div> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import DataTabs from '@/components/Global/SilaComponents/DataTabs'; +import Popover from '@/components/Global/Popover'; + +export default { + components: { PageTitle, PageSection, DataTabs, Popover }, + data() { + return { + currentMemorySlot: 1, + memorySlots: [ + { id: 1, name: 'Накопитель 1' }, + { id: 2, name: 'Накопитель 2' }, + { id: 3, name: 'Накопитель 3' }, + { id: 4, name: 'Накопитель 4' }, + { id: 5, name: 'Накопитель 5' }, + { id: 6, name: 'Накопитель 6' }, + { id: 7, name: 'Накопитель 7' }, + { id: 8, name: 'Накопитель 8' }, + { id: 9, name: 'Накопитель 9' }, + { id: 10, name: 'Накопитель 10' }, + { id: 11, name: 'Накопитель 11' }, + { id: 12, name: 'Накопитель 12' }, + { id: 13, name: 'Накопитель 13' }, + { id: 14, name: 'Накопитель 14' }, + { id: 15, name: 'Накопитель 15' }, + ], + fields: [ + { + key: 'parametr', + label: 'Параметр', + formatter: this.dataFormatter, + thStyle: { width: '50%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + items: [ + { + parametr: 'Протокол', + value: '4590', + }, + { + parametr: 'Номинальная ёмкость', + value: '1 024 000 Мб', + }, + { + parametr: 'Номинальная скорость вращения HDD', + value: '2000 об./мин.', + }, + { + parametr: 'Номинальный ресурс для SDD', + value: '10 000', + }, + { + parametr: 'Слот в бэкплейне или номер порта', + value: '25', + }, + ], + }; + }, + methods: { + switchMemorySlot(period) { + this.currentMemorySlot = period; + }, + }, +}; +</script> +<style lang="scss" scoped> +.info_section { + margin-bottom: 0px; +} + +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} + +.microcode-container { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: baseline; + justify-content: space-between; +} +</style> diff --git a/src/views/_sila/SILA/PhysicalDrivers/StaticInfo/index.js b/src/views/_sila/SILA/PhysicalDrivers/StaticInfo/index.js new file mode 100644 index 00000000..2c06b5a2 --- /dev/null +++ b/src/views/_sila/SILA/PhysicalDrivers/StaticInfo/index.js @@ -0,0 +1 @@ +export { default } from './DriversStaticPage.vue'; diff --git a/src/views/_sila/SILA/RAID/Cache/RAIDCachePage.vue b/src/views/_sila/SILA/RAID/Cache/RAIDCachePage.vue new file mode 100644 index 00000000..54efa48f --- /dev/null +++ b/src/views/_sila/SILA/RAID/Cache/RAIDCachePage.vue @@ -0,0 +1,107 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('RAID.cache')" /> + <data-tabs + :slots="raidSlots" + :switch-tab="switchRaidSlot" + :current-tab="currentRaidSlot" + :slot-width="100" + :slider-width="79" + style="margin-top: 16px" + /> + <page-section class="bootstrap-table__section info_section"> + <span class="bold-16px">{{ $t('RAID.cache_info') }}</span> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + </b-table> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import DataTabs from '@/components/Global/SilaComponents/DataTabs'; + +export default { + components: { PageTitle, PageSection, DataTabs }, + data() { + return { + currentRaidSlot: 1, + raidSlots: [ + { id: 1, name: 'Контроллер 1' }, + { id: 2, name: 'Контроллер 2' }, + { id: 3, name: 'Контроллер 3' }, + ], + fields: [ + { + key: 'parametr', + label: 'Параметр', + formatter: this.dataFormatter, + thStyle: { width: '50%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + items: [ + { + parametr: 'Статус наличия', + value: 'Есть', + }, + { + parametr: 'Текущее значение занятой емкости', + value: '1 024 000 Мб', + }, + { + parametr: 'Текущее значение свободной емкости', + value: '1 004 256 Мб', + }, + { + parametr: 'Статус наличия BBU', + value: 'Есть', + }, + { + parametr: 'Статус наличия FBU', + value: 'Нет', + }, + { + parametr: 'Состояние BBU', + value: 'Готов', + }, + { + parametr: 'Состояние FBU', + value: 'Заряжается', + }, + { + parametr: 'Номинальная емкость', + value: '1 004 256 Мб', + }, + ], + }; + }, + methods: { + switchRaidSlot(period) { + this.currentRaidSlot = period; + }, + }, +}; +</script> +<style lang="scss" scoped> +.bold-16px { + display: block; + margin: 0 0 16px 0; +} +</style> diff --git a/src/views/_sila/SILA/RAID/Cache/index.js b/src/views/_sila/SILA/RAID/Cache/index.js new file mode 100644 index 00000000..76acfcbe --- /dev/null +++ b/src/views/_sila/SILA/RAID/Cache/index.js @@ -0,0 +1 @@ +export { default } from './RAIDCachePage.vue'; diff --git a/src/views/_sila/SILA/RAID/Settings/ActionSettingsPopover.vue b/src/views/_sila/SILA/RAID/Settings/ActionSettingsPopover.vue new file mode 100644 index 00000000..2f3093ab --- /dev/null +++ b/src/views/_sila/SILA/RAID/Settings/ActionSettingsPopover.vue @@ -0,0 +1,131 @@ +<template> + <b-popover + placement="left" + triggers="focus" + :show.sync="show" + custom-class="apply-reload-popover" + :target="`popover-action-${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> +</template> + +<script> +export default { + props: { + id: { + type: Number, + default: null, + }, + 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"> +.analytical-table__status { + width: 10%; +} + +#popover-apply-ractive { + padding-left: 5px; +} + +.hovered-apply-button { + color: $white; +} +</style> +<style lang="scss" scoped> +#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/views/_sila/SILA/RAID/Settings/OptionsPopover.vue b/src/views/_sila/SILA/RAID/Settings/OptionsPopover.vue new file mode 100644 index 00000000..d1b85754 --- /dev/null +++ b/src/views/_sila/SILA/RAID/Settings/OptionsPopover.vue @@ -0,0 +1,87 @@ +<template> + <b-popover + placement="left" + triggers="focus" + :show.sync="show" + custom-class="popover-heigth-100" + :target="`popover-option-${id}`" + > + <b-button + id="popover-option-button" + variant="popover" + @click=" + () => { + show = false; + openModal(); + } + " + > + <img + src="@/assets/images/icon-settings-red.svg" + class="icon-chevron icon-settings" + /> + Настройки + </b-button> + <b-button + id="popover-option-button" + class="delete" + variant="popover" + @click="show = false" + > + <img + src="@/assets/images/icon-clear-red.svg" + class="icon-chevron icon-delete" + /> + Удалить + </b-button> + </b-popover> +</template> + +<script> +export default { + props: { + id: { + type: Number, + default: null, + }, + }, + data() { + return { + show: false, + }; + }, + methods: { + openModal() { + this.$bvModal.show(`edit-tome${this.id}`); + }, + }, +}; +</script> +<style lang="scss" scoped> +.popover-heigth-100 { + max-width: 120px; +} + +#popover-option-button { + padding: 0 9px 0 10px; + font-size: 12px; + &:hover { + color: $red-brand-primary; + box-shadow: 1px 2px 4px -1px rgb(79 37 37 / 25%) inset; + } + &:active { + box-shadow: 1px 2px 4px -1px rgb(79 37 37 / 50%) inset; + } + &.delete { + padding: 0 26px 0 13px; + } +} + +.icon-settings { + padding-right: 5px; +} + +.icon-delete { + padding-right: 10px; +} +</style> diff --git a/src/views/_sila/SILA/RAID/Settings/RAIDSettingsPage.vue b/src/views/_sila/SILA/RAID/Settings/RAIDSettingsPage.vue new file mode 100644 index 00000000..19caafe0 --- /dev/null +++ b/src/views/_sila/SILA/RAID/Settings/RAIDSettingsPage.vue @@ -0,0 +1,288 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.settings')" /> + <data-tabs + :slots="raidSlots" + :switch-tab="switchRaidSlot" + :current-tab="currentRaidSlot" + :slot-width="100" + :slider-width="79" + style="margin-top: 16px" + /> + <page-section class="bootstrap-table__section" style="margin-top: 0px"> + <div class="settings-container"> + <span class="bold-16px">{{ $t('appNavigation.settings') }}</span> + <apply-settings-popover + :appaly-on-reload="appalyOnReload" + :appaly-option1="appalyOption1" + :appaly-option2="appalyOption2" + :apply-type="applyType" + /> + <b-button variant="primary" class="apply-button"> + {{ $t('global.action.acceptChanges') }} + </b-button> + </div> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(action)="data"> + <b-row v-if="data.index <= 5"> + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + <b-col> + <b-button + :id="`popover-action-${data.index}`" + class="popover-action-ractive" + variant="toogle-popover" + > + <img :is="iconChevron" class="icon-chevron" /> + </b-button> + <action-settings-popover + :id="data.index" + :appaly-on-reload="appalyOnReload" + :appaly-option1="appalyOption1" + :appaly-option2="appalyOption2" + :apply-type="applyType" + /> + </b-col> + </b-row> + <b-row v-else> + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + <b-col> + <img + src="@/assets/images/icon-edit.svg" + class="icon-chevron icon-edit" + /> + </b-col> + </b-row> + </template> + </b-table> + </page-section> + <page-section class="bootstrap-table__section"> + <div class="settings-container"> + <span class="bold-16px">{{ $t('RAID.lun') }}</span> + <b-button + variant="primary" + class="apply-button apply-button__lun" + @click="$bvModal.show('add-tome-images')" + > + {{ $t('global.action.addNew') }} + </b-button> + </div> + <b-table + id="table-raid-settings" + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items_2" + :fields="fields_2" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(action)="data"> + <b-row> + <b-col> + <span> + {{ data.value }} + </span> + </b-col> + <b-col> + <b-button + :id="`popover-option-${data.index}`" + class="popover-option-ractive" + variant="toogle-popover" + > + <img + src="@/assets/images/icon-options.svg" + class="icon-chevron icon-options" + /> + </b-button> + <options-popover :id="data.index" /> + <tome-modal + :id="`edit-tome${data.index}`" + :title="'Настройка виртуального тома'" + :action="editTome" + :index="data.index" + :item="items_2[data.index]" + /> + </b-col> + </b-row> + </template> + </b-table> + <div class="microcode-container"> + <span class="bold-16px">{{ $t('RAID.microcode') }}</span> + <popover + id="popover-reactive-1" + description="RAID.microcode_update" + popup="RAID.microcode_popoveer" + button="global.action.refresh" + :is-microcode-drivers="true" + /> + </div> + </page-section> + <tome-modal + :id="'add-tome-images'" + :title="'Новый виртуальный том'" + :action="createTome" + /> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import Popover from '@/components/Global/Popover'; +import ApplySettingsPopover from '@/components/Global/SilaComponents/ApplySettingsPopover'; +import DataTabs from '@/components/Global/SilaComponents/DataTabs'; + +import iconChevron from '@carbon/icons-vue/es/chevron--down/16'; +import ActionSettingsPopover from './ActionSettingsPopover'; +import OptionsPopover from './OptionsPopover'; +import TomeModal from './TomeModal'; +import { items, items_2 } from './helpers'; + +export default { + components: { + PageTitle, + PageSection, + DataTabs, + Popover, + ApplySettingsPopover, + ActionSettingsPopover, + OptionsPopover, + TomeModal, + }, + data() { + return { + items, + items_2, + iconChevron, + applyType: 'reload', + currentRaidSlot: 1, + titleModal: '', + editIndex: null, + raidSlots: [ + { id: 1, name: 'Контроллер 1' }, + { id: 2, name: 'Контроллер 2' }, + { id: 3, name: 'Контроллер 3' }, + ], + fields: [ + { + key: 'parametr', + label: 'Имя', + formatter: this.dataFormatter, + thStyle: { width: '35%' }, + }, + { + key: 'value', + label: 'Текущее значение', + formatter: this.dataFormatter, + }, + { + key: 'action', + label: 'Действие', + formatter: this.dataFormatter, + }, + ], + fields_2: [ + { + key: 'name', + label: 'Параметр', + formatter: this.dataFormatter, + }, + { + key: 'type', + label: 'Значение', + formatter: this.dataFormatter, + }, + { + key: 'size', + label: 'Значение', + formatter: this.dataFormatter, + }, + { + key: 'action', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + }; + }, + methods: { + switchRaidSlot(period) { + this.currentRaidSlot = period; + }, + appalyOnReload() { + this.applyType = 'reload'; + }, + appalyOption1() { + this.applyType = 'option1'; + }, + appalyOption2() { + this.applyType = 'option2'; + }, + createTome() { + console.log('Новый том создан'); + }, + editTome(index) { + console.log('Том изменен'); + console.log(index); + }, + }, +}; +</script> +<style lang="scss" scoped> +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} + +.settings-container { + display: flex; + align-items: center; + justify-content: flex-start; +} + +.apply-button { + width: 245px; + height: 36px; +} + +.popover-action-ractive { + margin: 0 0 0 83%; +} + +.popover-option-ractive { + margin: 0 0 0 70%; +} + +.apply-button__lun { + width: 245px; + height: 36px; + margin-left: auto; +} + +.microcode-container { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: baseline; + justify-content: space-between; +} +</style> diff --git a/src/views/_sila/SILA/RAID/Settings/TomeModal.vue b/src/views/_sila/SILA/RAID/Settings/TomeModal.vue new file mode 100644 index 00000000..c245bd1b --- /dev/null +++ b/src/views/_sila/SILA/RAID/Settings/TomeModal.vue @@ -0,0 +1,179 @@ +<template> + <b-modal :id="id" class="modal-images" hide-footer> + <template #modal-title> + <span class="semi-bold-20px">{{ title }}</span> + </template> + <div class="modal-body"> + <label class="regular-12px tretiatry" for="name"> Имя </label> + <b-form-input + id="name" + v-model="tomeName" + placeholder="Название тома" + type="text" + class="form-control form-input" + > + </b-form-input> + + <form-control> + <label class="regular-12px tretiatry type-select-label" for="type" + >Тип</label + > + <b-form-select + id="type" + v-model="tomeType" + :options="tomeTypes" + class="select-connection regular-14px" + /> + </form-control> + + <form-control> + <label class="regular-12px tretiatry driver-select-label" for="driver" + >Физический накопитель</label + > + <b-form-select + id="driver" + v-model="tomeDriver" + :options="tomeDrivers" + class="select-connection regular-14px" + /> + </form-control> + + <label class="regular-12px tretiatry" for="size"> Размер Мб </label> + <b-form-input + id="size" + v-model="tomeSize" + placeholder="Размер Тома" + type="text" + class="form-control form-input" + > + </b-form-input> + <b-button class="upload-button" variant="primary" @click="action(index)"> + {{ item ? $t('global.action.save') : $t('global.action.addTome') }} + </b-button> + </div> + </b-modal> +</template> +<script> +import { required } from 'vuelidate/lib/validators'; +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; + +export default { + mixins: [VuelidateMixin], + props: { + id: { + type: String, + default: null, + }, + title: { + type: String, + default: null, + }, + index: { + type: Number, + default: null, + }, + action: { + type: Function, + default: null, + }, + item: { + type: Object, + default: null, + }, + }, + data() { + return { + tomeName: '', + tomeSize: '', + tomeType: 1, + tomeTypes: [ + { value: 1, text: 'RAID-0' }, + { value: 2, text: 'RAID-1' }, + ], + tomeDriver: 1, + tomeDrivers: [ + { value: 1, text: 'Накопитель 1' }, + { value: 2, text: 'Накопитель 2' }, + ], + }; + }, + mounted() { + this.tomeName = this.item.name; + this.tomeSize = this.item.size; + }, + validations: { + userInfo: { + username: { + required, + }, + password: { + required, + }, + }, + }, +}; +</script> +<style lang="scss"> +.modal-content { + border-radius: 16px; + width: 480px; +} +.modal-header { + border-bottom: none; +} +</style> +<style lang="scss" scoped> +.form-input { + height: 56px; + padding-top: 30px; + font-family: 'Inter', sans-serif; + font-size: 14px; + font-weight: 500; + line-height: 20px; + letter-spacing: 0em; + margin: -30px 0 20px 0; +} + +.modal-body { + width: 446px; +} + +.caption-12px, +.regular-12px { + padding-left: 10px; +} + +.select-connection { + height: 56px; + padding-top: 30px; + border: none; + border-radius: 8px; + margin: -0px 0 18px 0; + background-color: $surface-secondary; + background-image: url('../../../../../assets/images/_sila/icon-chevron.svg'); +} + +.type-select-label { + position: absolute; + top: 25%; +} + +.driver-select-label { + position: absolute; + top: 44%; + left: 4%; +} + +.form-background .custom-select, +.form-background .form-control { + border-radius: 8px; + border: none; + background-color: rgba(26, 62, 91, 0.05); +} + +.upload-button { + width: 100%; + height: 40px; + margin: 0 auto 10px; +} +</style> diff --git a/src/views/_sila/SILA/RAID/Settings/helpers.js b/src/views/_sila/SILA/RAID/Settings/helpers.js new file mode 100644 index 00000000..5727102a --- /dev/null +++ b/src/views/_sila/SILA/RAID/Settings/helpers.js @@ -0,0 +1,62 @@ +export const items = [ + { + parametr: 'Режим контроллера', + value: 'RAID', + action: 'Автоматически', + }, + { + parametr: 'Режим чтения патруля', + value: 'Автоматически', + action: 'Действие', + }, + { + parametr: 'Действие в режиме ручного патрулирования', + value: 'Запущен', + action: 'Действие', + }, + { + parametr: 'Название атрибута', + value: 'Запущен', + action: 'Действие', + }, + { + parametr: 'Название атрибута', + value: 'Запущен', + action: 'Действие', + }, + { + parametr: 'Название атрибута', + value: 'Запущен', + action: 'Действие', + }, + { + parametr: 'Название атрибута', + value: '30%', + action: '30', + }, + { + parametr: 'Название атрибута', + value: '30%', + action: '30', + }, + { + parametr: 'Название атрибута', + value: '30%', + action: '30', + }, +]; + +export const items_2 = [ + { + name: 'Том 1', + type: 'RAID-0', + size: '100 125 Мб', + action: 'Загружен', + }, + { + name: 'Том 2', + type: 'RAID-1', + size: '200 250 Мб', + action: 'Загружен', + }, +]; diff --git a/src/views/_sila/SILA/RAID/Settings/index.js b/src/views/_sila/SILA/RAID/Settings/index.js new file mode 100644 index 00000000..535dbe3a --- /dev/null +++ b/src/views/_sila/SILA/RAID/Settings/index.js @@ -0,0 +1 @@ +export { default } from './RAIDSettingsPage.vue'; diff --git a/src/views/_sila/SILA/RAID/Specification/RAIDSpecificationPage.vue b/src/views/_sila/SILA/RAID/Specification/RAIDSpecificationPage.vue new file mode 100644 index 00000000..239a41e4 --- /dev/null +++ b/src/views/_sila/SILA/RAID/Specification/RAIDSpecificationPage.vue @@ -0,0 +1,221 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.specification')" /> + <data-tabs + :slots="raidSlots" + :switch-tab="switchRaidSlot" + :current-tab="currentRaidSlot" + :slot-width="100" + :slider-width="79" + style="margin-top: 16px" + /> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(value)="{ index, value }"> + <b-row v-if="index === 0"> + <b-col> + <img src="@/assets/images/status/on.svg" class="icon-chevron" /> + <span> + {{ 'Работоспособен' }} + </span> + </b-col> + </b-row> + <b-row v-else-if="index === 7"> + <b-col> + <span class="regular-12px pointer" @click="redirectDrivers"> + {{ value }} + </span> + </b-col> + </b-row> + <b-row v-else-if="index === 8"> + <b-col> + <span class="regular-12px pointer" @click="redirectVirtual"> + {{ value }} + </span> + </b-col> + </b-row> + <b-row v-else> + <b-col> + <span>{{ value }}</span> + </b-col> + </b-row> + </template> + </b-table> + </page-section> + <b-button + v-b-toggle.toggle-collapse + variant="link" + class="collapse-button semi-bold-16px" + > + <component :is="iconChevronUp" class="icon-expand-right" /> + {{ $t('RAID.extraParam') }} + </b-button> + <b-collapse id="toggle-collapse" class="nav-item__nav"> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + class="table-stripes" + :items="items_2" + :fields="fields_2" + > + <template #cell(value)="{ index, value }"> + <b-row v-if="index === 0"> + <b-col> + <img src="@/assets/images/status/on.svg" class="icon-chevron" /> + <span> + {{ 'Работоспособен' }} + </span> + </b-col> + </b-row> + <b-row v-else> + <b-col> + <span>{{ value }}</span> + </b-col> + </b-row> + </template> + </b-table> + </page-section> + </b-collapse> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import DataTabs from '@/components/Global/SilaComponents/DataTabs'; + +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +export default { + components: { PageTitle, PageSection, DataTabs }, + data() { + return { + iconChevronUp: iconChevronUp, + currentRaidSlot: 1, + raidSlots: [ + { id: 1, name: 'Контроллер 1' }, + { id: 2, name: 'Контроллер 2' }, + { id: 3, name: 'Контроллер 3' }, + ], + fields: [ + { + key: 'parametr', + label: 'Параметр', + formatter: this.dataFormatter, + thStyle: { width: '50%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + items: [ + { + parametr: 'Статус', + value: true, + }, + { + parametr: 'Имя', + value: 'RAID-контроллер 1', + }, + { + parametr: 'Описание', + value: 'Встроенный', + }, + { + parametr: 'PCI слот', + value: 'Информация недоступна', + }, + { + parametr: 'Версия прошивки', + value: 'Информация недоступна', + }, + { + parametr: 'Версия драйвера', + value: 'Информация недоступна', + }, + { + parametr: 'Объем cache памяти, Мб', + value: '8 096', + }, + { + parametr: 'Виртуальные накопители', + value: 'Перейти к накопителям', + }, + { + parametr: 'Физические накопители', + value: 'Перейти к накопителям', + }, + ], + fields_2: [ + { + key: 'param', + label: '', + formatter: this.dataFormatter, + }, + { + key: 'value', + label: '', + formatter: this.dataFormatter, + }, + ], + items_2: [ + { + param: 'Статус', + value: true, + }, + { + param: 'Скорость', + value: 'Информация недоступна', + }, + { + param: 'PCI Vendor ID', + value: 'Информация недоступна', + }, + { + param: 'Скорость', + value: 'Информация недоступна', + }, + { + param: 'PCI Vendor ID', + value: 'Информация недоступна', + }, + ], + }; + }, + methods: { + switchRaidSlot(period) { + this.currentRaidSlot = period; + }, + redirectDrivers() { + this.$router.push('/drivers-static'); + }, + redirectVirtual() { + this.$router.push('/virtual-drivers'); + }, + }, +}; +</script> +<style lang="scss" scoped> +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} + +.pointer { + color: $red-brand-primary; + cursor: pointer; +} +</style> diff --git a/src/views/_sila/SILA/RAID/Specification/index.js b/src/views/_sila/SILA/RAID/Specification/index.js new file mode 100644 index 00000000..a040cff4 --- /dev/null +++ b/src/views/_sila/SILA/RAID/Specification/index.js @@ -0,0 +1 @@ +export { default } from './RAIDSpecificationPage.vue'; diff --git a/src/views/_sila/SILA/VirtualDrivers/VirtualDriversPage.vue b/src/views/_sila/SILA/VirtualDrivers/VirtualDriversPage.vue new file mode 100644 index 00000000..73633901 --- /dev/null +++ b/src/views/_sila/SILA/VirtualDrivers/VirtualDriversPage.vue @@ -0,0 +1,122 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title /> + <page-section class="bootstrap-table__section info_section"> + <span class="bold-16px">{{ $t('global.table.info') }}</span> + </page-section> + <data-tabs + :slots="memorySlots" + :switch-tab="switchMemorySlot" + :current-tab="currentMemorySlot" + :slot-width="180" + :slider-width="156" + /> + <page-section class="bootstrap-table__section info_section"> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + </b-table> + <div class="microcode-container"> + <span class="bold-16px">{{ + $t('SystemDescription.microcodeDrivers') + }}</span> + <popover + id="popover-reactive-1" + description="SystemDescription.reloadMicrocodeDrivers" + popup="SystemDescription.reloadMicrocodeDrivers" + button="global.action.refresh" + :is-microcode-drivers="true" + /> + </div> + </page-section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; +import DataTabs from '@/components/Global/SilaComponents/DataTabs'; +import Popover from '@/components/Global/Popover'; + +export default { + components: { PageTitle, PageSection, DataTabs, Popover }, + data() { + return { + currentMemorySlot: 1, + memorySlots: [ + { id: 1, name: 'Виртуальный накопитель 1' }, + { id: 2, name: 'Виртуальный накопитель 2' }, + { id: 3, name: 'Виртуальный накопитель 3' }, + { id: 4, name: 'Виртуальный накопитель 4' }, + { id: 5, name: 'Виртуальный накопитель 5' }, + { id: 6, name: 'Виртуальный накопитель 6' }, + { id: 7, name: 'Виртуальный накопитель 7' }, + { id: 8, name: 'Виртуальный накопитель 8' }, + ], + fields: [ + { + key: 'parametr', + label: 'Параметр', + formatter: this.dataFormatter, + thStyle: { width: '50%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + items: [ + { + parametr: 'Текущий статус', + value: 'Готов', + }, + { + parametr: 'Номинальная емкость', + value: '1 024 000 Мб', + }, + { + parametr: 'Тип избыточности', + value: 'RAID', + }, + { + parametr: 'Входящие в состав физические накопители', + value: 'Накопитель 1, Накопитель 2, Накопитель 3', + }, + ], + }; + }, + methods: { + switchMemorySlot(period) { + this.currentMemorySlot = period; + }, + }, +}; +</script> +<style lang="scss" scoped> +.info_section { + margin-bottom: 0px; +} + +.bold-16px { + display: block; + margin: 25px 0 16px 0; +} + +.microcode-container { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: baseline; + justify-content: space-between; +} +</style> diff --git a/src/views/_sila/SILA/VirtualDrivers/index.js b/src/views/_sila/SILA/VirtualDrivers/index.js new file mode 100644 index 00000000..23cd6fe1 --- /dev/null +++ b/src/views/_sila/SILA/VirtualDrivers/index.js @@ -0,0 +1 @@ +export { default } from './VirtualDriversPage.vue'; diff --git a/src/views/_sila/SecurityAndAccess/Certificates/Certificates.vue b/src/views/_sila/SecurityAndAccess/Certificates/Certificates.vue index 27950b76..8aee85cf 100644 --- a/src/views/_sila/SecurityAndAccess/Certificates/Certificates.vue +++ b/src/views/_sila/SecurityAndAccess/Certificates/Certificates.vue @@ -1,98 +1,101 @@ <template> - <b-container fluid="xl"> + <b-container fluid="pt-0 m-0"> <page-title /> - <b-row> - <b-col xl="11"> - <!-- Expired certificates banner --> - <alert :show="expiredCertificateTypes.length > 0" variant="danger"> - <template v-if="expiredCertificateTypes.length > 1"> - {{ $t('pageCertificates.alert.certificatesExpiredMessage') }} - </template> - <template v-else> - {{ - $t('pageCertificates.alert.certificateExpiredMessage', { - certificate: expiredCertificateTypes[0], - }) - }} - </template> - </alert> - <!-- Expiring certificates banner --> - <alert :show="expiringCertificateTypes.length > 0" variant="warning"> - <template v-if="expiringCertificateTypes.length > 1"> - {{ $t('pageCertificates.alert.certificatesExpiringMessage') }} - </template> - <template v-else> - {{ - $t('pageCertificates.alert.certificateExpiringMessage', { - certificate: expiringCertificateTypes[0], - }) - }} - </template> - </alert> - </b-col> - </b-row> - <b-row> - <b-col xl="11" class="text-right"> - <b-button - v-b-modal.generate-csr - data-test-id="certificates-button-generateCsr" - variant="link" - > - <icon-add /> - {{ $t('pageCertificates.generateCsr') }} - </b-button> - <b-button - variant="primary" - :disabled="certificatesForUpload.length === 0" - @click="initModalUploadCertificate(null)" - > - <icon-add /> - {{ $t('pageCertificates.addNewCertificate') }} - </b-button> - </b-col> - </b-row> - <b-row> - <b-col xl="11"> - <b-table - responsive="md" - show-empty - hover - :busy="isBusy" - :fields="fields" - :items="tableItems" - :empty-text="$t('global.table.emptyMessage')" - > - <template #cell(validFrom)="{ value }"> - {{ value | formatDate }} - </template> + <page-section class="bootstrap-table__section"> + <b-row class="margin-bottom--16px"> + <b-col xl="11"> + <!-- Expired certificates banner --> + <alert :show="expiredCertificateTypes.length > 0" variant="danger"> + <template v-if="expiredCertificateTypes.length > 1"> + {{ $t('pageCertificates.alert.certificatesExpiredMessage') }} + </template> + <template v-else> + {{ + $t('pageCertificates.alert.certificateExpiredMessage', { + certificate: expiredCertificateTypes[0], + }) + }} + </template> + </alert> + <!-- Expiring certificates banner --> + <alert :show="expiringCertificateTypes.length > 0" variant="warning"> + <template v-if="expiringCertificateTypes.length > 1"> + {{ $t('pageCertificates.alert.certificatesExpiringMessage') }} + </template> + <template v-else> + {{ + $t('pageCertificates.alert.certificateExpiringMessage', { + certificate: expiringCertificateTypes[0], + }) + }} + </template> + </alert> + </b-col> + </b-row> + <b-row class="margin-bottom--16px"> + <b-col xl="11" class="text-right"> + <b-button + v-b-modal.generate-csr + data-test-id="certificates-button-generateCsr" + variant="link" + > + <icon-add /> + {{ $t('pageCertificates.generateCsr') }} + </b-button> + <b-button + variant="primary" + :disabled="certificatesForUpload.length === 0" + @click="initModalUploadCertificate(null)" + > + <icon-add /> + {{ $t('pageCertificates.addNewCertificate') }} + </b-button> + </b-col> + </b-row> + <b-row> + <b-col xl="11"> + <b-table + responsive="md" + show-empty + hover + class="table-rounded" + no-border-collapse + :busy="isBusy" + :fields="fields" + :items="tableItems" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(validFrom)="{ value }"> + {{ value | formatDate }} + </template> - <template #cell(validUntil)="{ value }"> - <status-icon - v-if="getDaysUntilExpired(value) < 31" - :status="getIconStatus(value)" - /> - {{ value | formatDate }} - </template> - - <template #cell(actions)="{ value, item }"> - <table-row-action - v-for="(action, index) in value" - :key="index" - :value="action.value" - :title="action.title" - :enabled="action.enabled" - @click-table-action="onTableRowAction($event, item)" - > - <template #icon> - <icon-replace v-if="action.value === 'replace'" /> - <icon-trashcan v-if="action.value === 'delete'" /> - </template> - </table-row-action> - </template> - </b-table> - </b-col> - </b-row> + <template #cell(validUntil)="{ value }"> + <status-icon + v-if="getDaysUntilExpired(value) < 31" + :status="getIconStatus(value)" + /> + {{ value | formatDate }} + </template> + <template #cell(actions)="{ value, item }"> + <table-row-action + v-for="(action, index) in value" + :key="index" + :value="action.value" + :title="action.title" + :enabled="action.enabled" + @click-table-action="onTableRowAction($event, item)" + > + <template #icon> + <icon-replace v-if="action.value === 'replace'" /> + <icon-trashcan v-if="action.value === 'delete'" /> + </template> + </table-row-action> + </template> + </b-table> + </b-col> + </b-row> + </page-section> <!-- Modals --> <modal-upload-certificate :certificate="modalCertificate" @ok="onModalOk" /> <modal-generate-csr /> @@ -107,6 +110,7 @@ import IconTrashcan from '@carbon/icons-vue/es/trash-can/20'; import ModalGenerateCsr from './ModalGenerateCsr'; import ModalUploadCertificate from './ModalUploadCertificate'; import PageTitle from '@/components/_sila/Global/PageTitle'; +import PageSection from '@/components/_sila/Global/PageSection'; import TableRowAction from '@/components/_sila/Global/TableRowAction'; import StatusIcon from '@/components/_sila/Global/StatusIcon'; import Alert from '@/components/_sila/Global/Alert'; @@ -124,6 +128,7 @@ export default { ModalGenerateCsr, ModalUploadCertificate, PageTitle, + PageSection, StatusIcon, TableRowAction, }, @@ -320,3 +325,8 @@ export default { }, }; </script> +<style lang="scss" scoped> +.margin-bottom--16px { + margin-bottom: 16px; +} +</style> diff --git a/src/views/_sila/SecurityAndAccess/Certificates/ModalGenerateCsr.vue b/src/views/_sila/SecurityAndAccess/Certificates/ModalGenerateCsr.vue index 5bfb81b4..63258a40 100644 --- a/src/views/_sila/SecurityAndAccess/Certificates/ModalGenerateCsr.vue +++ b/src/views/_sila/SecurityAndAccess/Certificates/ModalGenerateCsr.vue @@ -3,7 +3,6 @@ <b-modal id="generate-csr" ref="modal" - size="lg" no-stacking :title="$t('pageCertificates.modal.generateACertificateSigningRequest')" @ok="onOkGenerateCsrModal" @@ -13,7 +12,88 @@ <b-form id="generate-csr-form" novalidate @submit.prevent="handleSubmit"> <b-container fluid> <b-row> - <b-col lg="9"> + <b-col lg="12"> + <b-row> + <b-col lg="12"> + <p class="col-form-label"> + {{ $t('pageCertificates.modal.privateKey') }} + </p> + <b-form-group + :label="$t('pageCertificates.modal.keyPairAlgorithm')" + label-for="key-pair-algorithm" + > + <b-form-select + id="key-pair-algorithm" + v-model="form.keyPairAlgorithm" + data-test-id="modalGenerateCsr-select-keyPairAlgorithm" + :options="keyPairAlgorithmOptions" + :state="getValidationState($v.form.keyPairAlgorithm)" + @input="$v.form.keyPairAlgorithm.$touch()" + > + <template #first> + <b-form-select-option :value="null" disabled> + {{ $t('global.form.selectAnOption') }} + </b-form-select-option> + </template> + </b-form-select> + <b-form-invalid-feedback role="alert"> + {{ $t('global.form.fieldRequired') }} + </b-form-invalid-feedback> + </b-form-group> + </b-col> + </b-row> + <b-row> + <b-col lg="12"> + <template v-if="$v.form.keyPairAlgorithm.$model === 'EC'"> + <b-form-group + :label="$t('pageCertificates.modal.keyCurveId')" + label-for="key-curve-id" + > + <b-form-select + id="key-curve-id" + v-model="form.keyCurveId" + data-test-id="modalGenerateCsr-select-keyCurveId" + :options="keyCurveIdOptions" + :state="getValidationState($v.form.keyCurveId)" + @input="$v.form.keyCurveId.$touch()" + > + <template #first> + <b-form-select-option :value="null" disabled> + {{ $t('global.form.selectAnOption') }} + </b-form-select-option> + </template> + </b-form-select> + <b-form-invalid-feedback role="alert"> + {{ $t('global.form.fieldRequired') }} + </b-form-invalid-feedback> + </b-form-group> + </template> + <template v-if="$v.form.keyPairAlgorithm.$model === 'RSA'"> + <b-form-group + :label="$t('pageCertificates.modal.keyBitLength')" + label-for="key-bit-length" + > + <b-form-select + id="key-bit-length" + v-model="form.keyBitLength" + data-test-id="modalGenerateCsr-select-keyBitLength" + :options="keyBitLengthOptions" + :state="getValidationState($v.form.keyBitLength)" + @input="$v.form.keyBitLength.$touch()" + > + <template #first> + <b-form-select-option :value="null" disabled> + {{ $t('global.form.selectAnOption') }} + </b-form-select-option> + </template> + </b-form-select> + <b-form-invalid-feedback role="alert"> + {{ $t('global.form.fieldRequired') }} + </b-form-invalid-feedback> + </b-form-group> + </template> + </b-col> + </b-row> <b-row> <b-col lg="6"> <b-form-group @@ -241,89 +321,6 @@ </b-col> </b-row> </b-col> - <b-col lg="3"> - <b-row> - <b-col lg="12"> - <p class="col-form-label"> - {{ $t('pageCertificates.modal.privateKey') }} - </p> - <b-form-group - :label="$t('pageCertificates.modal.keyPairAlgorithm')" - label-for="key-pair-algorithm" - > - <b-form-select - id="key-pair-algorithm" - v-model="form.keyPairAlgorithm" - data-test-id="modalGenerateCsr-select-keyPairAlgorithm" - :options="keyPairAlgorithmOptions" - :state="getValidationState($v.form.keyPairAlgorithm)" - @input="$v.form.keyPairAlgorithm.$touch()" - > - <template #first> - <b-form-select-option :value="null" disabled> - {{ $t('global.form.selectAnOption') }} - </b-form-select-option> - </template> - </b-form-select> - <b-form-invalid-feedback role="alert"> - {{ $t('global.form.fieldRequired') }} - </b-form-invalid-feedback> - </b-form-group> - </b-col> - </b-row> - <b-row> - <b-col lg="12"> - <template v-if="$v.form.keyPairAlgorithm.$model === 'EC'"> - <b-form-group - :label="$t('pageCertificates.modal.keyCurveId')" - label-for="key-curve-id" - > - <b-form-select - id="key-curve-id" - v-model="form.keyCurveId" - data-test-id="modalGenerateCsr-select-keyCurveId" - :options="keyCurveIdOptions" - :state="getValidationState($v.form.keyCurveId)" - @input="$v.form.keyCurveId.$touch()" - > - <template #first> - <b-form-select-option :value="null" disabled> - {{ $t('global.form.selectAnOption') }} - </b-form-select-option> - </template> - </b-form-select> - <b-form-invalid-feedback role="alert"> - {{ $t('global.form.fieldRequired') }} - </b-form-invalid-feedback> - </b-form-group> - </template> - <template v-if="$v.form.keyPairAlgorithm.$model === 'RSA'"> - <b-form-group - :label="$t('pageCertificates.modal.keyBitLength')" - label-for="key-bit-length" - > - <b-form-select - id="key-bit-length" - v-model="form.keyBitLength" - data-test-id="modalGenerateCsr-select-keyBitLength" - :options="keyBitLengthOptions" - :state="getValidationState($v.form.keyBitLength)" - @input="$v.form.keyBitLength.$touch()" - > - <template #first> - <b-form-select-option :value="null" disabled> - {{ $t('global.form.selectAnOption') }} - </b-form-select-option> - </template> - </b-form-select> - <b-form-invalid-feedback role="alert"> - {{ $t('global.form.fieldRequired') }} - </b-form-invalid-feedback> - </b-form-group> - </template> - </b-col> - </b-row> - </b-col> </b-row> </b-container> </b-form> diff --git a/src/views/_sila/SecurityAndAccess/Policies/Policies.vue b/src/views/_sila/SecurityAndAccess/Policies/Policies.vue index c594bde5..44493882 100644 --- a/src/views/_sila/SecurityAndAccess/Policies/Policies.vue +++ b/src/views/_sila/SecurityAndAccess/Policies/Policies.vue @@ -1,122 +1,125 @@ <template> - <b-container fluid="xl"> + <b-container fluid="pt-0 m-0"> <page-title /> - <b-row> - <b-col md="8"> - <b-row v-if="!modifySSHPolicyDisabled" class="setting-section"> - <b-col class="d-flex align-items-center justify-content-between"> - <dl class="mr-3 w-75"> - <dt>{{ $t('pagePolicies.ssh') }}</dt> - <dd> - {{ $t('pagePolicies.sshDescription') }} - </dd> - </dl> - <b-form-checkbox - id="sshSwitch" - v-model="sshProtocolState" - data-test-id="policies-toggle-bmcShell" - switch - @change="changeSshProtocolState" - > - <span class="sr-only"> - {{ $t('pagePolicies.ssh') }} - </span> - <span v-if="sshProtocolState"> - {{ $t('global.status.enabled') }} - </span> - <span v-else>{{ $t('global.status.disabled') }}</span> - </b-form-checkbox> - </b-col> - </b-row> - <b-row class="setting-section"> - <b-col class="d-flex align-items-center justify-content-between"> - <dl class="mt-3 mr-3 w-75"> - <dt>{{ $t('pagePolicies.ipmi') }}</dt> - <dd> - {{ $t('pagePolicies.ipmiDescription') }} - </dd> - </dl> - <b-form-checkbox - id="ipmiSwitch" - v-model="ipmiProtocolState" - data-test-id="polices-toggle-networkIpmi" - switch - @change="changeIpmiProtocolState" - > - <span class="sr-only"> - {{ $t('pagePolicies.ipmi') }} - </span> - <span v-if="ipmiProtocolState"> - {{ $t('global.status.enabled') }} - </span> - <span v-else>{{ $t('global.status.disabled') }}</span> - </b-form-checkbox> - </b-col> - </b-row> - <b-row class="setting-section"> - <b-col class="d-flex align-items-center justify-content-between"> - <dl class="mt-3 mr-3 w-75"> - <dt>{{ $t('pagePolicies.vtpm') }}</dt> - <dd> - {{ $t('pagePolicies.vtpmDescription') }} - </dd> - </dl> - <b-form-checkbox - id="vtpmSwitch" - v-model="vtpmState" - data-test-id="policies-toggle-vtpm" - switch - @change="changeVtpmState" - > - <span class="sr-only"> - {{ $t('pagePolicies.vtpm') }} - </span> - <span v-if="vtpmState"> - {{ $t('global.status.enabled') }} - </span> - <span v-else>{{ $t('global.status.disabled') }}</span> - </b-form-checkbox> - </b-col> - </b-row> - <b-row class="setting-section"> - <b-col class="d-flex align-items-center justify-content-between"> - <dl class="mt-3 mr-3 w-75"> - <dt>{{ $t('pagePolicies.rtad') }}</dt> - <dd> - {{ $t('pagePolicies.rtadDescription') }} - </dd> - </dl> - <b-form-checkbox - id="rtadSwitch" - v-model="rtadState" - data-test-id="policies-toggle-rtad" - switch - @change="changeRtadState" - > - <span class="sr-only"> - {{ $t('pagePolicies.rtad') }} - </span> - <span v-if="rtadState"> - {{ $t('global.status.enabled') }} - </span> - <span v-else>{{ $t('global.status.disabled') }}</span> - </b-form-checkbox> - </b-col> - </b-row> - </b-col> - </b-row> + <page-section class="bootstrap-table__section"> + <b-row> + <b-col md="8"> + <b-row v-if="!modifySSHPolicyDisabled" class="setting-section"> + <b-col class="d-flex align-items-center justify-content-between"> + <dl class="mr-3 w-75"> + <dt>{{ $t('pagePolicies.ssh') }}</dt> + <dd> + {{ $t('pagePolicies.sshDescription') }} + </dd> + </dl> + <b-form-checkbox + id="sshSwitch" + v-model="sshProtocolState" + data-test-id="policies-toggle-bmcShell" + switch + @change="changeSshProtocolState" + > + <span class="sr-only"> + {{ $t('pagePolicies.ssh') }} + </span> + <span v-if="sshProtocolState"> + {{ $t('global.status.enabled') }} + </span> + <span v-else>{{ $t('global.status.disabled') }}</span> + </b-form-checkbox> + </b-col> + </b-row> + <b-row class="setting-section"> + <b-col class="d-flex align-items-center justify-content-between"> + <dl class="mt-3 mr-3 w-75"> + <dt>{{ $t('pagePolicies.ipmi') }}</dt> + <dd> + {{ $t('pagePolicies.ipmiDescription') }} + </dd> + </dl> + <b-form-checkbox + id="ipmiSwitch" + v-model="ipmiProtocolState" + data-test-id="polices-toggle-networkIpmi" + switch + @change="changeIpmiProtocolState" + > + <span class="sr-only"> + {{ $t('pagePolicies.ipmi') }} + </span> + <span v-if="ipmiProtocolState"> + {{ $t('global.status.enabled') }} + </span> + <span v-else>{{ $t('global.status.disabled') }}</span> + </b-form-checkbox> + </b-col> + </b-row> + <b-row class="setting-section"> + <b-col class="d-flex align-items-center justify-content-between"> + <dl class="mt-3 mr-3 w-75"> + <dt>{{ $t('pagePolicies.vtpm') }}</dt> + <dd> + {{ $t('pagePolicies.vtpmDescription') }} + </dd> + </dl> + <b-form-checkbox + id="vtpmSwitch" + v-model="vtpmState" + data-test-id="policies-toggle-vtpm" + switch + @change="changeVtpmState" + > + <span class="sr-only"> + {{ $t('pagePolicies.vtpm') }} + </span> + <span v-if="vtpmState"> + {{ $t('global.status.enabled') }} + </span> + <span v-else>{{ $t('global.status.disabled') }}</span> + </b-form-checkbox> + </b-col> + </b-row> + <b-row class="setting-section"> + <b-col class="d-flex align-items-center justify-content-between"> + <dl class="mt-3 mr-3 w-75"> + <dt>{{ $t('pagePolicies.rtad') }}</dt> + <dd> + {{ $t('pagePolicies.rtadDescription') }} + </dd> + </dl> + <b-form-checkbox + id="rtadSwitch" + v-model="rtadState" + data-test-id="policies-toggle-rtad" + switch + @change="changeRtadState" + > + <span class="sr-only"> + {{ $t('pagePolicies.rtad') }} + </span> + <span v-if="rtadState"> + {{ $t('global.status.enabled') }} + </span> + <span v-else>{{ $t('global.status.disabled') }}</span> + </b-form-checkbox> + </b-col> + </b-row> + </b-col> + </b-row> + </page-section> </b-container> </template> <script> import PageTitle from '@/components/_sila/Global/PageTitle'; +import PageSection from '@/components/_sila/Global/PageSection'; import LoadingBarMixin from '@/components/_sila/Mixins/LoadingBarMixin'; import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; export default { name: 'Policies', - components: { PageTitle }, + components: { PageTitle, PageSection }, mixins: [LoadingBarMixin, BVToastMixin], beforeRouteLeave(to, from, next) { this.hideLoader(); diff --git a/src/views/_sila/SecurityAndAccess/Sessions/Sessions.vue b/src/views/_sila/SecurityAndAccess/Sessions/Sessions.vue index d922c241..5178b180 100644 --- a/src/views/_sila/SecurityAndAccess/Sessions/Sessions.vue +++ b/src/views/_sila/SecurityAndAccess/Sessions/Sessions.vue @@ -35,6 +35,7 @@ no-select-on-click hover show-empty + class="table-rounded" no-border-collapse sort-by="clientID" :busy="isBusy" @@ -289,10 +290,3 @@ export default { }, }; </script> -<style lang="scss"> -#table-session-logs { - td .btn-link { - width: auto !important; - } -} -</style> diff --git a/src/views/_sila/SecurityAndAccess/UserManagement/UserManagement.vue b/src/views/_sila/SecurityAndAccess/UserManagement/UserManagement.vue index 423ce4c1..f5179d46 100644 --- a/src/views/_sila/SecurityAndAccess/UserManagement/UserManagement.vue +++ b/src/views/_sila/SecurityAndAccess/UserManagement/UserManagement.vue @@ -1,7 +1,7 @@ <template> - <b-container fluid="xl"> + <b-container fluid class="m-0 p-0"> <page-title /> - <b-row> + <b-row class="bootstrap-table__section"> <b-col xl="9" class="text-right"> <b-button variant="link" @click="initModalSettings"> <icon-settings /> @@ -17,7 +17,7 @@ </b-button> </b-col> </b-row> - <b-row> + <b-row class="bootstrap-table__section"> <b-col xl="9"> <table-toolbar ref="toolbar" @@ -31,8 +31,10 @@ responsive="md" selectable show-empty + class="table-rounded" no-select-on-click hover + no-border-collapse :busy="isBusy" :fields="fields" :items="tableItems" @@ -85,7 +87,7 @@ </b-table> </b-col> </b-row> - <b-row> + <b-row class="bootstrap-table__section"> <b-col xl="8"> <b-button v-b-toggle.collapse-role-table @@ -381,7 +383,6 @@ export default { }, }; </script> - <style lang="scss" scoped> .btn.collapsed { svg { diff --git a/src/views/_sila/Settings/DateTime/DateTime.vue b/src/views/_sila/Settings/DateTime/DateTime.vue new file mode 100644 index 00000000..63570c39 --- /dev/null +++ b/src/views/_sila/Settings/DateTime/DateTime.vue @@ -0,0 +1,435 @@ +<template> + <b-container id="date-time" fluid="xxl" class="pt-0 m-0"> + <page-title /> + <b-row class="bootstrap-table__section"> + <b-col md="8" xl="6"> + <alert variant="info" class="mb-4"> + <span> + {{ $t('pageDateTime.alert.message') }} + <b-link to="/profile-settings"> + {{ $t('pageDateTime.alert.link') }}</b-link + > + </span> + </alert> + </b-col> + </b-row> + <page-section class="bootstrap-table__section"> + <b-row> + <b-col lg="3"> + <dl> + <dt>{{ $t('pageDateTime.form.date') }}</dt> + <dd v-if="bmcTime">{{ bmcTime | formatDate }}</dd> + <dd v-else>--</dd> + </dl> + </b-col> + <b-col lg="3"> + <dl> + <dt>{{ $t('pageDateTime.form.time.label') }}</dt> + <dd v-if="bmcTime">{{ bmcTime | formatTime }}</dd> + <dd v-else>--</dd> + </dl> + </b-col> + </b-row> + </page-section> + <page-section + class="bootstrap-table__section" + :section-title="$t('pageDateTime.configureSettings')" + > + <b-form novalidate @submit.prevent="submitForm"> + <b-form-group + label="Configure date and time" + :disabled="loading" + label-sr-only + > + <b-form-radio + v-model="form.configurationSelected" + value="manual" + data-test-id="dateTime-radio-configureManual" + > + {{ $t('pageDateTime.form.manual') }} + </b-form-radio> + <b-row class="mt-3 ml-3"> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTime.form.date')" + label-for="input-manual-date" + label-class="regular-14px" + > + <b-form-text id="date-format-help">YYYY-MM-DD</b-form-text> + <b-input-group> + <b-form-input + id="input-manual-date" + v-model="form.manual.date" + :state="getValidationState($v.form.manual.date)" + :disabled="ntpOptionSelected" + data-test-id="dateTime-input-manualDate" + class="form-control-with-button" + @blur="$v.form.manual.date.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <div v-if="!$v.form.manual.date.pattern"> + {{ $t('global.form.invalidFormat') }} + </div> + <div v-if="!$v.form.manual.date.required"> + {{ $t('global.form.fieldRequired') }} + </div> + </b-form-invalid-feedback> + <b-form-datepicker + v-model="form.manual.date" + class="btn-datepicker btn-icon-only" + button-only + right + :hide-header="true" + :locale="locale" + :label-help=" + $t('global.calendar.useCursorKeysToNavigateCalendarDates') + " + :title="$t('global.calendar.selectDate')" + :disabled="ntpOptionSelected" + button-variant="link" + aria-controls="input-manual-date" + > + <template #button-content> + <icon-calendar /> + <span class="sr-only"> + {{ $t('global.calendar.selectDate') }} + </span> + </template> + </b-form-datepicker> + </b-input-group> + </b-form-group> + </b-col> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTime.form.time.timezone', { timezone })" + label-for="input-manual-time" + label-class="regular-14px" + > + <b-form-text id="time-format-help">HH:MM</b-form-text> + <b-input-group> + <b-form-input + id="input-manual-time" + v-model="form.manual.time" + :state="getValidationState($v.form.manual.time)" + :disabled="ntpOptionSelected" + data-test-id="dateTime-input-manualTime" + @blur="$v.form.manual.time.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <div v-if="!$v.form.manual.time.pattern"> + {{ $t('global.form.invalidFormat') }} + </div> + <div v-if="!$v.form.manual.time.required"> + {{ $t('global.form.fieldRequired') }} + </div> + </b-form-invalid-feedback> + </b-input-group> + </b-form-group> + </b-col> + </b-row> + <b-form-radio + v-model="form.configurationSelected" + value="ntp" + data-test-id="dateTime-radio-configureNTP" + > + {{ $t('SystemDescription.GetNtpFromServer') }} + </b-form-radio> + <b-row class="mt-3 ml-3"> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTime.form.ntpServers.server1')" + label-for="input-ntp-1" + label-class="regular-14px" + > + <b-input-group> + <b-form-input + id="input-ntp-1" + v-model="form.ntp.firstAddress" + :state="getValidationState($v.form.ntp.firstAddress)" + :disabled="manualOptionSelected" + data-test-id="dateTime-input-ntpServer1" + @blur="$v.form.ntp.firstAddress.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <div v-if="!$v.form.ntp.firstAddress.required"> + {{ $t('global.form.fieldRequired') }} + </div> + </b-form-invalid-feedback> + </b-input-group> + </b-form-group> + </b-col> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTime.form.ntpServers.server2')" + label-for="input-ntp-2" + label-class="regular-14px" + > + <b-input-group> + <b-form-input + id="input-ntp-2" + v-model="form.ntp.secondAddress" + :disabled="manualOptionSelected" + data-test-id="dateTime-input-ntpServer2" + /> + </b-input-group> + </b-form-group> + </b-col> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTime.form.ntpServers.server3')" + label-for="input-ntp-3" + label-class="regular-14px" + > + <b-input-group> + <b-form-input + id="input-ntp-3" + v-model="form.ntp.thirdAddress" + :disabled="manualOptionSelected" + data-test-id="dateTime-input-ntpServer3" + /> + </b-input-group> + </b-form-group> + </b-col> + </b-row> + <b-button + variant="primary" + type="submit" + data-test-id="dateTime-button-saveSettings" + > + {{ $t('global.action.saveSettings') }} + </b-button> + </b-form-group> + </b-form> + </page-section> + </b-container> +</template> + +<script> +import Alert from '@/components/Global/Alert'; +import IconCalendar from '@carbon/icons-vue/es/calendar/20'; +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; + +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; +import LocalTimezoneLabelMixin from '@/components/Mixins/LocalTimezoneLabelMixin'; +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; + +import { mapState } from 'vuex'; +import { requiredIf, helpers } from 'vuelidate/lib/validators'; + +const isoDateRegex = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/; +const isoTimeRegex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/; + +export default { + name: 'DateTime', + components: { Alert, IconCalendar, PageTitle, PageSection }, + mixins: [ + BVToastMixin, + LoadingBarMixin, + LocalTimezoneLabelMixin, + VuelidateMixin, + ], + beforeRouteLeave(to, from, next) { + this.hideLoader(); + next(); + }, + data() { + return { + locale: this.$store.getters['global/languagePreference'], + form: { + configurationSelected: 'manual', + manual: { + date: '', + time: '', + }, + ntp: { firstAddress: '', secondAddress: '', thirdAddress: '' }, + }, + loading, + }; + }, + validations() { + return { + form: { + manual: { + date: { + required: requiredIf(function () { + return this.form.configurationSelected === 'manual'; + }), + pattern: helpers.regex('pattern', isoDateRegex), + }, + time: { + required: requiredIf(function () { + return this.form.configurationSelected === 'manual'; + }), + pattern: helpers.regex('pattern', isoTimeRegex), + }, + }, + ntp: { + firstAddress: { + required: requiredIf(function () { + return this.form.configurationSelected === 'ntp'; + }), + }, + }, + }, + }; + }, + computed: { + ...mapState('dateTime', ['ntpServers', 'isNtpProtocolEnabled']), + bmcTime() { + return this.$store.getters['global/bmcTime']; + }, + ntpOptionSelected() { + return this.form.configurationSelected === 'ntp'; + }, + manualOptionSelected() { + return this.form.configurationSelected === 'manual'; + }, + isUtcDisplay() { + return this.$store.getters['global/isUtcDisplay']; + }, + timezone() { + if (this.isUtcDisplay) { + return 'UTC'; + } + return this.localOffset(); + }, + }, + watch: { + ntpServers() { + this.setNtpValues(); + }, + manualDate() { + this.emitChange(); + }, + bmcTime() { + this.form.manual.date = this.$options.filters.formatDate( + this.$store.getters['global/bmcTime'] + ); + this.form.manual.time = this.$options.filters + .formatTime(this.$store.getters['global/bmcTime']) + .slice(0, 5); + }, + }, + created() { + this.startLoader(); + this.setNtpValues(); + Promise.all([ + this.$store.dispatch('global/getBmcTime'), + this.$store.dispatch('dateTime/getNtpData'), + ]).finally(() => this.endLoader()); + }, + methods: { + emitChange() { + if (this.$v.$invalid) return; + this.$v.$reset(); //reset to re-validate on blur + this.$emit('change', { + manualDate: this.manualDate ? new Date(this.manualDate) : null, + }); + }, + setNtpValues() { + this.form.configurationSelected = this.isNtpProtocolEnabled + ? 'ntp' + : 'manual'; + [ + this.form.ntp.firstAddress = '', + this.form.ntp.secondAddress = '', + this.form.ntp.thirdAddress = '', + ] = [this.ntpServers[0], this.ntpServers[1], this.ntpServers[2]]; + }, + submitForm() { + this.$v.$touch(); + if (this.$v.$invalid) return; + this.startLoader(); + + let dateTimeForm = {}; + let isNTPEnabled = this.form.configurationSelected === 'ntp'; + + if (!isNTPEnabled) { + const isUtcDisplay = this.$store.getters['global/isUtcDisplay']; + let date; + + dateTimeForm.ntpProtocolEnabled = false; + + if (isUtcDisplay) { + // Create UTC Date + date = this.getUtcDate(this.form.manual.date, this.form.manual.time); + } else { + // Create local Date + date = new Date(`${this.form.manual.date} ${this.form.manual.time}`); + } + + dateTimeForm.updatedDateTime = date.toISOString(); + } else { + dateTimeForm.ntpProtocolEnabled = true; + + const ntpArray = [ + this.form.ntp.firstAddress, + this.form.ntp.secondAddress, + this.form.ntp.thirdAddress, + ]; + + // Filter the ntpArray to remove empty strings, + // per Redfish spec there should be no empty strings or null on the ntp array. + const ntpArrayFiltered = ntpArray.filter((x) => x); + + dateTimeForm.ntpServersArray = [...ntpArrayFiltered]; + + [this.ntpServers[0], this.ntpServers[1], this.ntpServers[2]] = [ + ...dateTimeForm.ntpServersArray, + ]; + + this.setNtpValues(); + } + + this.$store + .dispatch('dateTime/updateDateTime', dateTimeForm) + .then((success) => { + this.successToast(success); + if (!isNTPEnabled) return; + // Shift address up if second address is empty + // to avoid refreshing after delay when updating NTP + if (!this.form.ntp.secondAddress && this.form.ntp.thirdAddres) { + this.form.ntp.secondAddress = this.form.ntp.thirdAddres; + this.form.ntp.thirdAddress = ''; + } + }) + .then(() => { + this.$store.dispatch('global/getBmcTime'); + }) + .catch(({ message }) => this.errorToast(message)) + .finally(() => { + this.$v.form.$reset(); + this.endLoader(); + }); + }, + getUtcDate(date, time) { + // Split user input string values to create + // a UTC Date object + const datesArray = date.split('-'); + const timeArray = time.split(':'); + let utcDate = Date.UTC( + datesArray[0], // User input year + //UTC expects zero-index month value 0-11 (January-December) + //for reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC#Parameters + parseInt(datesArray[1]) - 1, // User input month + datesArray[2], // User input day + timeArray[0], // User input hour + timeArray[1] // User input minute + ); + return new Date(utcDate); + }, + }, +}; +</script> +<style lang="scss" scoped> +.form-control { + height: 40px; + max-width: 270px; +} + +.input-group { + max-width: 270px; +} +</style> diff --git a/src/views/_sila/Settings/DateTime/index.js b/src/views/_sila/Settings/DateTime/index.js new file mode 100644 index 00000000..2df21eae --- /dev/null +++ b/src/views/_sila/Settings/DateTime/index.js @@ -0,0 +1,2 @@ +import DateTime from './DateTime.vue'; +export default DateTime; diff --git a/src/views/_sila/Settings/Network/ModalDns.vue b/src/views/_sila/Settings/Network/ModalDns.vue new file mode 100644 index 00000000..a1dc9cec --- /dev/null +++ b/src/views/_sila/Settings/Network/ModalDns.vue @@ -0,0 +1,92 @@ +<template> + <b-modal + id="modal-dns" + ref="modal" + :title="$t('pageNetwork.table.addDnsAddress')" + @hidden="resetForm" + > + <b-form id="form-dns" style="width: 100%" @submit.prevent="handleSubmit"> + <b-row> + <b-col> + <b-form-group + :label="$t('pageNetwork.modal.staticDns')" + label-for="staticDns" + > + <b-form-input + id="staticDns" + v-model="form.staticDns" + type="text" + :state="getValidationState($v.form.staticDns)" + @input="$v.form.staticDns.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <template v-if="!$v.form.staticDns.required"> + {{ $t('global.form.fieldRequired') }} + </template> + <template v-if="!$v.form.staticDns.ipAddress"> + {{ $t('global.form.invalidFormat') }} + </template> + </b-form-invalid-feedback> + </b-form-group> + </b-col> + </b-row> + </b-form> + <template #modal-footer="{ cancel }"> + <b-button variant="secondary" @click="cancel()"> + {{ $t('global.action.cancel') }} + </b-button> + <b-button form="form-dns" type="submit" variant="primary" @click="onOk"> + {{ $t('global.action.add') }} + </b-button> + </template> + </b-modal> +</template> + +<script> +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; +import { ipAddress, required } from 'vuelidate/lib/validators'; + +export default { + mixins: [VuelidateMixin], + data() { + return { + form: { + staticDns: null, + }, + }; + }, + validations() { + return { + form: { + staticDns: { + required, + ipAddress, + }, + }, + }; + }, + methods: { + handleSubmit() { + this.$v.$touch(); + if (this.$v.$invalid) return; + this.$emit('ok', [this.form.staticDns]); + this.closeModal(); + }, + closeModal() { + this.$nextTick(() => { + this.$refs.modal.hide(); + }); + }, + resetForm() { + this.form.staticDns = null; + this.$v.$reset(); + this.$emit('hidden'); + }, + onOk(bvModalEvt) { + // prevent modal close + bvModalEvt.preventDefault(); + this.handleSubmit(); + }, + }, +}; +</script> diff --git a/src/views/_sila/Settings/Network/ModalHostname.vue b/src/views/_sila/Settings/Network/ModalHostname.vue new file mode 100644 index 00000000..f3221ec7 --- /dev/null +++ b/src/views/_sila/Settings/Network/ModalHostname.vue @@ -0,0 +1,110 @@ +<template> + <b-modal + id="modal-hostname" + ref="modal" + :title="$t('pageNetwork.modal.editHostnameTitle')" + @hidden="resetForm" + > + <b-form id="hostname-settings" @submit.prevent="handleSubmit"> + <b-row> + <b-col sm="6"> + <b-form-group + :label="$t('pageNetwork.hostname')" + label-for="hostname" + > + <b-form-input + id="hostname" + v-model="form.hostname" + type="text" + :state="getValidationState($v.form.hostname)" + @input="$v.form.hostname.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <template v-if="!$v.form.hostname.required"> + {{ $t('global.form.fieldRequired') }} + </template> + <template v-if="!$v.form.hostname.validateHostname"> + {{ $t('global.form.lengthMustBeBetween', { min: 1, max: 64 }) }} + </template> + </b-form-invalid-feedback> + </b-form-group> + </b-col> + </b-row> + </b-form> + <template #modal-footer="{ cancel }"> + <b-button variant="secondary" @click="cancel()"> + {{ $t('global.action.cancel') }} + </b-button> + <b-button + form="hostname-settings" + type="submit" + variant="primary" + @click="onOk" + > + {{ $t('global.action.add') }} + </b-button> + </template> + </b-modal> +</template> + +<script> +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; +import { required, helpers } from 'vuelidate/lib/validators'; + +const validateHostname = helpers.regex('validateHostname', /^\S{0,64}$/); + +export default { + mixins: [VuelidateMixin], + props: { + hostname: { + type: String, + default: '', + }, + }, + data() { + return { + form: { + hostname: '', + }, + }; + }, + watch: { + hostname() { + this.form.hostname = this.hostname; + }, + }, + validations() { + return { + form: { + hostname: { + required, + validateHostname, + }, + }, + }; + }, + methods: { + handleSubmit() { + this.$v.$touch(); + if (this.$v.$invalid) return; + this.$emit('ok', { HostName: this.form.hostname }); + this.closeModal(); + }, + closeModal() { + this.$nextTick(() => { + this.$refs.modal.hide(); + }); + }, + resetForm() { + this.form.hostname = this.hostname; + this.$v.$reset(); + this.$emit('hidden'); + }, + onOk(bvModalEvt) { + // prevent modal close + bvModalEvt.preventDefault(); + this.handleSubmit(); + }, + }, +}; +</script> diff --git a/src/views/_sila/Settings/Network/ModalIpv4.vue b/src/views/_sila/Settings/Network/ModalIpv4.vue new file mode 100644 index 00000000..dcf4a579 --- /dev/null +++ b/src/views/_sila/Settings/Network/ModalIpv4.vue @@ -0,0 +1,165 @@ +<template> + <b-modal + id="modal-add-ipv4" + ref="modal" + :title="$t('pageNetwork.table.addIpv4Address')" + @hidden="resetForm" + > + <b-form id="form-ipv4" @submit.prevent="handleSubmit"> + <b-row> + <b-col sm="6"> + <b-form-group + :label="$t('pageNetwork.modal.ipAddress')" + label-for="ipAddress" + > + <b-form-input + id="ipAddress" + v-model="form.ipAddress" + type="text" + :state="getValidationState($v.form.ipAddress)" + @input="$v.form.ipAddress.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <template v-if="!$v.form.ipAddress.required"> + {{ $t('global.form.fieldRequired') }} + </template> + <template v-if="!$v.form.ipAddress.ipAddress"> + {{ $t('global.form.invalidFormat') }} + </template> + </b-form-invalid-feedback> + </b-form-group> + </b-col> + <b-col sm="6"> + <b-form-group + :label="$t('pageNetwork.modal.gateway')" + label-for="gateway" + > + <b-form-input + id="gateway" + v-model="form.gateway" + type="text" + :state="getValidationState($v.form.gateway)" + @input="$v.form.gateway.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <template v-if="!$v.form.gateway.required"> + {{ $t('global.form.fieldRequired') }} + </template> + <template v-if="!$v.form.gateway.ipAddress"> + {{ $t('global.form.invalidFormat') }} + </template> + </b-form-invalid-feedback> + </b-form-group> + </b-col> + </b-row> + <b-row> + <b-col sm="6"> + <b-form-group + :label="$t('pageNetwork.modal.subnetMask')" + label-for="subnetMask" + > + <b-form-input + id="subnetMask" + v-model="form.subnetMask" + type="text" + :state="getValidationState($v.form.subnetMask)" + @input="$v.form.subnetMask.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <template v-if="!$v.form.subnetMask.required"> + {{ $t('global.form.fieldRequired') }} + </template> + <template v-if="!$v.form.subnetMask.ipAddress"> + {{ $t('global.form.invalidFormat') }} + </template> + </b-form-invalid-feedback> + </b-form-group> + </b-col> + </b-row> + </b-form> + <template #modal-footer="{ cancel }"> + <b-button variant="secondary" @click="cancel()"> + {{ $t('global.action.cancel') }} + </b-button> + <b-button form="form-ipv4" type="submit" variant="primary" @click="onOk"> + {{ $t('global.action.add') }} + </b-button> + </template> + </b-modal> +</template> + +<script> +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; +import { ipAddress, required } from 'vuelidate/lib/validators'; + +export default { + mixins: [VuelidateMixin], + props: { + defaultGateway: { + type: String, + default: '', + }, + }, + data() { + return { + form: { + ipAddress: '', + gateway: '', + subnetMask: '', + }, + }; + }, + watch: { + defaultGateway() { + this.form.gateway = this.defaultGateway; + }, + }, + validations() { + return { + form: { + ipAddress: { + required, + ipAddress, + }, + gateway: { + required, + ipAddress, + }, + subnetMask: { + required, + ipAddress, + }, + }, + }; + }, + methods: { + handleSubmit() { + this.$v.$touch(); + if (this.$v.$invalid) return; + this.$emit('ok', { + Address: this.form.ipAddress, + Gateway: this.form.gateway, + SubnetMask: this.form.subnetMask, + }); + this.closeModal(); + }, + closeModal() { + this.$nextTick(() => { + this.$refs.modal.hide(); + }); + }, + resetForm() { + this.form.ipAddress = null; + this.form.gateway = this.defaultGateway; + this.form.subnetMask = null; + this.$v.$reset(); + this.$emit('hidden'); + }, + onOk(bvModalEvt) { + // prevent modal close + bvModalEvt.preventDefault(); + this.handleSubmit(); + }, + }, +}; +</script> diff --git a/src/views/_sila/Settings/Network/ModalMacAddress.vue b/src/views/_sila/Settings/Network/ModalMacAddress.vue new file mode 100644 index 00000000..d563f4ce --- /dev/null +++ b/src/views/_sila/Settings/Network/ModalMacAddress.vue @@ -0,0 +1,109 @@ +<template> + <b-modal + id="modal-mac-address" + ref="modal" + :title="$t('pageNetwork.modal.editMacAddressTitle')" + @hidden="resetForm" + > + <b-form id="mac-settings" @submit.prevent="handleSubmit"> + <b-row> + <b-col sm="6"> + <b-form-group + :label="$t('pageNetwork.macAddress')" + label-for="macAddress" + > + <b-form-input + id="mac-address" + v-model.trim="form.macAddress" + data-test-id="network-input-macAddress" + type="text" + :state="getValidationState($v.form.macAddress)" + @change="$v.form.macAddress.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <div v-if="!$v.form.macAddress.required"> + {{ $t('global.form.fieldRequired') }} + </div> + <div v-if="!$v.form.macAddress.macAddress"> + {{ $t('global.form.invalidFormat') }} + </div> + </b-form-invalid-feedback> + </b-form-group> + </b-col> + </b-row> + </b-form> + <template #modal-footer="{ cancel }"> + <b-button variant="secondary" @click="cancel()"> + {{ $t('global.action.cancel') }} + </b-button> + <b-button + form="mac-settings" + type="submit" + variant="primary" + @click="onOk" + > + {{ $t('global.action.add') }} + </b-button> + </template> + </b-modal> +</template> + +<script> +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; +import { macAddress, required } from 'vuelidate/lib/validators'; + +export default { + mixins: [VuelidateMixin], + props: { + macAddress: { + type: String, + default: '', + }, + }, + data() { + return { + form: { + macAddress: '', + }, + }; + }, + watch: { + macAddress() { + this.form.macAddress = this.macAddress; + }, + }, + validations() { + return { + form: { + macAddress: { + required, + macAddress: macAddress(), + }, + }, + }; + }, + methods: { + handleSubmit() { + this.$v.$touch(); + if (this.$v.$invalid) return; + this.$emit('ok', { MACAddress: this.form.macAddress }); + this.closeModal(); + }, + closeModal() { + this.$nextTick(() => { + this.$refs.modal.hide(); + }); + }, + resetForm() { + this.form.macAddress = this.macAddress; + this.$v.$reset(); + this.$emit('hidden'); + }, + onOk(bvModalEvt) { + // prevent modal close + bvModalEvt.preventDefault(); + this.handleSubmit(); + }, + }, +}; +</script> diff --git a/src/views/_sila/Settings/Network/Network.vue b/src/views/_sila/Settings/Network/Network.vue new file mode 100644 index 00000000..50442be4 --- /dev/null +++ b/src/views/_sila/Settings/Network/Network.vue @@ -0,0 +1,166 @@ +<template> + <b-container id="page-network" fluid="m-0 p-0"> + <page-title :description="$t('pageNetwork.pageDescription')" /> + + <page-section v-show="ethernetData" class="m-4"> + <network-global-settings class="m-4" /> + <b-row> + <b-col> + <b-card no-body> + <b-tabs + active-nav-item-class="font-weight-bold" + card + content-class="mt-3" + > + <b-tab + v-for="(data, index) in ethernetData" + :key="data.Id" + :title="data.Id" + @click="getTabIndex(index)" + > + <!-- Interface settings --> + <network-interface-settings :tab-index="tabIndex" /> + <!-- IPV4 table --> + <table-ipv-4 :tab-index="tabIndex" /> + <!-- Static DNS table --> + <table-dns :tab-index="tabIndex" /> + </b-tab> + </b-tabs> + </b-card> + </b-col> + </b-row> + </page-section> + <!-- Modals --> + <modal-ipv4 :default-gateway="defaultGateway" @ok="saveIpv4Address" /> + <modal-dns @ok="saveDnsAddress" /> + <modal-hostname :hostname="currentHostname" @ok="saveSettings" /> + <modal-mac-address :mac-address="currentMacAddress" @ok="saveSettings" /> + </b-container> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; +import ModalMacAddress from './ModalMacAddress.vue'; +import ModalHostname from './ModalHostname.vue'; +import ModalIpv4 from './ModalIpv4.vue'; +import ModalDns from './ModalDns.vue'; +import NetworkGlobalSettings from './NetworkGlobalSettings.vue'; +import NetworkInterfaceSettings from './NetworkInterfaceSettings.vue'; +import PageSection from '@/components/Global/PageSection'; +import PageTitle from '@/components/Global/PageTitle'; +import TableIpv4 from './TableIpv4.vue'; +import TableDns from './TableDns.vue'; +import { mapState } from 'vuex'; + +export default { + name: 'Network', + components: { + ModalHostname, + ModalMacAddress, + ModalIpv4, + ModalDns, + NetworkGlobalSettings, + NetworkInterfaceSettings, + PageSection, + PageTitle, + TableDns, + TableIpv4, + }, + mixins: [BVToastMixin, DataFormatterMixin, LoadingBarMixin], + beforeRouteLeave(to, from, next) { + this.hideLoader(); + next(); + }, + data() { + return { + currentHostname: '', + currentMacAddress: '', + defaultGateway: '', + loading, + tabIndex: 0, + }; + }, + computed: { + ...mapState('network', ['ethernetData']), + }, + watch: { + ethernetData() { + this.getModalInfo(); + }, + }, + created() { + this.startLoader(); + const globalSettings = new Promise((resolve) => { + this.$root.$on('network-global-settings-complete', () => resolve()); + }); + const interfaceSettings = new Promise((resolve) => { + this.$root.$on('network-interface-settings-complete', () => resolve()); + }); + const networkTableDns = new Promise((resolve) => { + this.$root.$on('network-table-dns-complete', () => resolve()); + }); + const networkTableIpv4 = new Promise((resolve) => { + this.$root.$on('network-table-ipv4-complete', () => resolve()); + }); + // Combine all child component Promises to indicate + // when page data load complete + Promise.all([ + this.$store.dispatch('network/getEthernetData'), + globalSettings, + interfaceSettings, + networkTableDns, + networkTableIpv4, + ]).finally(() => this.endLoader()); + }, + methods: { + getModalInfo() { + this.defaultGateway = this.$store.getters[ + 'network/globalNetworkSettings' + ][this.tabIndex].defaultGateway; + + this.currentHostname = this.$store.getters[ + 'network/globalNetworkSettings' + ][this.tabIndex].hostname; + + this.currentMacAddress = this.$store.getters[ + 'network/globalNetworkSettings' + ][this.tabIndex].macAddress; + }, + getTabIndex(selectedIndex) { + this.tabIndex = selectedIndex; + this.$store.dispatch('network/setSelectedTabIndex', this.tabIndex); + this.$store.dispatch( + 'network/setSelectedTabId', + this.ethernetData[selectedIndex].Id + ); + this.getModalInfo(); + }, + saveIpv4Address(modalFormData) { + this.startLoader(); + this.$store + .dispatch('network/saveIpv4Address', modalFormData) + .then((message) => this.successToast(message)) + .catch(({ message }) => this.errorToast(message)) + .finally(() => this.endLoader()); + }, + saveDnsAddress(modalFormData) { + this.startLoader(); + this.$store + .dispatch('network/saveDnsAddress', modalFormData) + .then((message) => this.successToast(message)) + .catch(({ message }) => this.errorToast(message)) + .finally(() => this.endLoader()); + }, + saveSettings(modalFormData) { + this.startLoader(); + this.$store + .dispatch('network/saveSettings', modalFormData) + .then((message) => this.successToast(message)) + .catch(({ message }) => this.errorToast(message)) + .finally(() => this.endLoader()); + }, + }, +}; +</script> diff --git a/src/views/_sila/Settings/Network/NetworkGlobalSettings.vue b/src/views/_sila/Settings/Network/NetworkGlobalSettings.vue new file mode 100644 index 00000000..30287673 --- /dev/null +++ b/src/views/_sila/Settings/Network/NetworkGlobalSettings.vue @@ -0,0 +1,161 @@ +<template> + <page-section + v-if="firstInterface" + :section-title="$t('pageNetwork.networkSettings')" + > + <b-row> + <b-col md="3"> + <dl> + <dt> + {{ $t('pageNetwork.hostname') }} + <b-button variant="link" class="p-1" @click="initSettingsModal()"> + <icon-edit :title="$t('pageNetwork.modal.editHostnameTitle')" /> + </b-button> + </dt> + <dd>{{ dataFormatter(firstInterface.hostname) }}</dd> + </dl> + </b-col> + <b-col md="3"> + <dl> + <dt>{{ $t('pageNetwork.useDomainName') }}</dt> + <dd> + <b-form-checkbox + id="useDomainNameSwitch" + v-model="useDomainNameState" + data-test-id="networkSettings-switch-useDomainName" + switch + @change="changeDomainNameState" + > + <span v-if="useDomainNameState"> + {{ $t('global.status.enabled') }} + </span> + <span v-else>{{ $t('global.status.disabled') }}</span> + </b-form-checkbox> + </dd> + </dl> + </b-col> + <b-col md="3"> + <dl> + <dt>{{ $t('pageNetwork.useDns') }}</dt> + <dd> + <b-form-checkbox + id="useDnsSwitch" + v-model="useDnsState" + data-test-id="networkSettings-switch-useDns" + switch + @change="changeDnsState" + > + <span v-if="useDnsState"> + {{ $t('global.status.enabled') }} + </span> + <span v-else>{{ $t('global.status.disabled') }}</span> + </b-form-checkbox> + </dd> + </dl> + </b-col> + <b-col md="3"> + <dl> + <dt>{{ $t('pageNetwork.useNtp') }}</dt> + <dd> + <b-form-checkbox + id="useNtpSwitch" + v-model="useNtpState" + data-test-id="networkSettings-switch-useNtp" + switch + @change="changeNtpState" + > + <span v-if="useNtpState"> + {{ $t('global.status.enabled') }} + </span> + <span v-else>{{ $t('global.status.disabled') }}</span> + </b-form-checkbox> + </dd> + </dl> + </b-col> + </b-row> + </page-section> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import IconEdit from '@carbon/icons-vue/es/edit/16'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import PageSection from '@/components/Global/PageSection'; +import { mapState } from 'vuex'; + +export default { + name: 'GlobalNetworkSettings', + components: { IconEdit, PageSection }, + mixins: [BVToastMixin, DataFormatterMixin], + + data() { + return { + hostname: '', + }; + }, + computed: { + ...mapState('network', ['ethernetData']), + firstInterface() { + return this.$store.getters['network/globalNetworkSettings'][0]; + }, + useDomainNameState: { + get() { + return this.$store.getters['network/globalNetworkSettings'][0] + .useDomainNameEnabled; + }, + set(newValue) { + return newValue; + }, + }, + useDnsState: { + get() { + return this.$store.getters['network/globalNetworkSettings'][0] + .useDnsEnabled; + }, + set(newValue) { + return newValue; + }, + }, + useNtpState: { + get() { + return this.$store.getters['network/globalNetworkSettings'][0] + .useNtpEnabled; + }, + set(newValue) { + return newValue; + }, + }, + }, + created() { + this.$store.dispatch('network/getEthernetData').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('network-global-settings-complete'); + }); + }, + methods: { + changeDomainNameState(state) { + this.$store + .dispatch('network/saveDomainNameState', state) + .then((success) => { + this.successToast(success); + }) + .catch(({ message }) => this.errorToast(message)); + }, + changeDnsState(state) { + this.$store + .dispatch('network/saveDnsState', state) + .then((message) => this.successToast(message)) + .catch(({ message }) => this.errorToast(message)); + }, + changeNtpState(state) { + this.$store + .dispatch('network/saveNtpState', state) + .then((message) => this.successToast(message)) + .catch(({ message }) => this.errorToast(message)); + }, + initSettingsModal() { + this.$bvModal.show('modal-hostname'); + }, + }, +}; +</script> diff --git a/src/views/_sila/Settings/Network/NetworkInterfaceSettings.vue b/src/views/_sila/Settings/Network/NetworkInterfaceSettings.vue new file mode 100644 index 00000000..023d29bc --- /dev/null +++ b/src/views/_sila/Settings/Network/NetworkInterfaceSettings.vue @@ -0,0 +1,117 @@ +<template> + <div> + <page-section> + <b-row> + <b-col md="3"> + <dl> + <dt>{{ $t('pageNetwork.linkStatus') }}</dt> + <dd> + {{ dataFormatter(linkStatus) }} + </dd> + </dl> + </b-col> + <b-col md="3"> + <dl> + <dt>{{ $t('pageNetwork.speed') }}</dt> + <dd> + {{ dataFormatter(linkSpeed) }} + </dd> + </dl> + </b-col> + </b-row> + </page-section> + <page-section :section-title="$t('pageNetwork.interfaceSection')"> + <b-row> + <b-col md="3"> + <dl> + <dt> + {{ $t('pageNetwork.fqdn') }} + </dt> + <dd> + {{ dataFormatter(fqdn) }} + </dd> + </dl> + </b-col> + <b-col md="3"> + <dl class="text-nowrap"> + <dt> + {{ $t('pageNetwork.macAddress') }} + <b-button + variant="link" + class="p-1" + @click="initMacAddressModal()" + > + <icon-edit + :title="$t('pageNetwork.modal.editMacAddressTitle')" + /> + </b-button> + </dt> + <dd> + {{ dataFormatter(macAddress) }} + </dd> + </dl> + </b-col> + </b-row> + </page-section> + </div> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import IconEdit from '@carbon/icons-vue/es/edit/16'; +import PageSection from '@/components/Global/PageSection'; +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import { mapState } from 'vuex'; + +export default { + name: 'Ipv4Table', + components: { + IconEdit, + PageSection, + }, + mixins: [BVToastMixin, DataFormatterMixin], + props: { + tabIndex: { + type: Number, + default: 0, + }, + }, + data() { + return { + selectedInterface: '', + linkStatus: '', + linkSpeed: '', + fqdn: '', + macAddress: '', + }; + }, + computed: { + ...mapState('network', ['ethernetData']), + }, + watch: { + // Watch for change in tab index + tabIndex() { + this.getSettings(); + }, + }, + created() { + this.getSettings(); + this.$store.dispatch('network/getEthernetData').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('network-interface-settings-complete'); + }); + }, + methods: { + getSettings() { + this.selectedInterface = this.tabIndex; + this.linkStatus = this.ethernetData[this.selectedInterface].LinkStatus; + this.linkSpeed = this.ethernetData[this.selectedInterface].SpeedMbps; + this.fqdn = this.ethernetData[this.selectedInterface].FQDN; + this.macAddress = this.ethernetData[this.selectedInterface].MACAddress; + }, + initMacAddressModal() { + this.$bvModal.show('modal-mac-address'); + }, + }, +}; +</script> diff --git a/src/views/_sila/Settings/Network/TableDns.vue b/src/views/_sila/Settings/Network/TableDns.vue new file mode 100644 index 00000000..c37c6e9f --- /dev/null +++ b/src/views/_sila/Settings/Network/TableDns.vue @@ -0,0 +1,156 @@ +<template> + <page-section :section-title="$t('pageNetwork.staticDns')"> + <b-row> + <b-col lg="6"> + <div class="text-right"> + <b-button variant="primary" class="mb-2" @click="initDnsModal()"> + <icon-add /> + {{ $t('pageNetwork.table.addDnsAddress') }} + </b-button> + </div> + <b-table + responsive="md" + hover + no-border-collapse + :fields="dnsTableFields" + :items="form.dnsStaticTableItems" + :empty-text="$t('global.table.emptyMessage')" + class="mb-0" + show-empty + > + <template #cell(actions)="{ item, index }"> + <table-row-action + v-for="(action, actionIndex) in item.actions" + :key="actionIndex" + :value="action.value" + :title="action.title" + :enabled="action.enabled" + @click-table-action="onDnsTableAction(action, $event, index)" + > + <template #icon> + <icon-edit v-if="action.value === 'edit'" /> + <icon-trashcan v-if="action.value === 'delete'" /> + </template> + </table-row-action> + </template> + </b-table> + </b-col> + </b-row> + </page-section> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import IconAdd from '@carbon/icons-vue/es/add--alt/20'; +import IconEdit from '@carbon/icons-vue/es/edit/20'; +import IconTrashcan from '@carbon/icons-vue/es/trash-can/20'; +import PageSection from '@/components/Global/PageSection'; +import TableRowAction from '@/components/Global/TableRowAction'; +import { mapState } from 'vuex'; + +export default { + name: 'DNSTable', + components: { + IconAdd, + IconEdit, + IconTrashcan, + PageSection, + TableRowAction, + }, + mixins: [BVToastMixin], + props: { + tabIndex: { + type: Number, + default: 0, + }, + }, + data() { + return { + form: { + dnsStaticTableItems: [], + }, + actions: [ + { + value: 'edit', + title: this.$t('global.action.edit'), + }, + { + value: 'delete', + title: this.$t('global.action.delete'), + }, + ], + dnsTableFields: [ + { + key: 'address', + label: this.$t('pageNetwork.table.ipAddress'), + }, + { + key: 'actions', + thStyle: { width: '30%' }, + label: this.$t('pageNetwork.table.actions'), + thClass: 'text-right', + tdClass: 'text-right', + }, + ], + }; + }, + computed: { + ...mapState('network', ['ethernetData']), + }, + watch: { + // Watch for change in tab index + tabIndex() { + this.getStaticDnsItems(); + }, + ethernetData() { + this.getStaticDnsItems(); + }, + }, + created() { + this.getStaticDnsItems(); + this.$store.dispatch('network/getEthernetData').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('network-table-dns-complete'); + }); + }, + methods: { + getStaticDnsItems() { + const index = this.tabIndex; + const dns = this.ethernetData[index].StaticNameServers || []; + this.form.dnsStaticTableItems = dns.map((server) => { + return { + address: server, + actions: [ + { + value: 'edit', + title: this.$t('global.action.edit'), + }, + { + value: 'delete', + title: this.$t('pageNetwork.table.deleteDns'), + }, + ], + }; + }); + }, + onDnsTableAction(action, $event, index) { + if ($event === 'delete') { + this.deleteDnsTableRow(index); + } + }, + deleteDnsTableRow(index) { + this.form.dnsStaticTableItems.splice(index, 1); + const newDnsArray = this.form.dnsStaticTableItems.map((dns) => { + return dns.address; + }); + this.$store + .dispatch('network/editDnsAddress', newDnsArray) + .then((message) => this.successToast(message)) + .catch(({ message }) => this.errorToast(message)); + }, + initDnsModal() { + this.$bvModal.show('modal-dns'); + }, + }, +}; +</script> diff --git a/src/views/_sila/Settings/Network/TableIpv4.vue b/src/views/_sila/Settings/Network/TableIpv4.vue new file mode 100644 index 00000000..6514a59c --- /dev/null +++ b/src/views/_sila/Settings/Network/TableIpv4.vue @@ -0,0 +1,183 @@ +<template> + <page-section :section-title="$t('pageNetwork.ipv4')"> + <b-row> + <b-col> + <h3 class="h5"> + {{ $t('pageNetwork.ipv4Addresses') }} + </h3> + </b-col> + <b-col class="text-right"> + <b-button variant="primary" class="mb-2" @click="initAddIpv4Address()"> + <icon-add /> + {{ $t('pageNetwork.table.addIpv4Address') }} + </b-button> + </b-col> + </b-row> + <b-table + responsive="md" + hover + class="mb-0" + show-empty + no-border-collapse + :fields="ipv4TableFields" + :items="form.ipv4TableItems" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(actions)="{ item, index }"> + <table-row-action + v-for="(action, actionIndex) in item.actions" + :key="actionIndex" + :value="action.value" + :title="action.title" + :enabled="action.enabled" + @click-table-action="onIpv4TableAction(action, $event, index)" + > + <template #icon> + <icon-edit v-if="action.value === 'edit'" /> + <icon-trashcan v-if="action.value === 'delete'" /> + </template> + </table-row-action> + </template> + </b-table> + </page-section> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import IconAdd from '@carbon/icons-vue/es/add--alt/20'; +import IconEdit from '@carbon/icons-vue/es/edit/20'; +import IconTrashcan from '@carbon/icons-vue/es/trash-can/20'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import PageSection from '@/components/Global/PageSection'; +import TableRowAction from '@/components/Global/TableRowAction'; +import { mapState } from 'vuex'; + +export default { + name: 'Ipv4Table', + components: { + IconAdd, + IconEdit, + IconTrashcan, + PageSection, + TableRowAction, + }, + mixins: [BVToastMixin, LoadingBarMixin], + props: { + tabIndex: { + type: Number, + default: 0, + }, + }, + data() { + return { + form: { + ipv4TableItems: [], + }, + actions: [ + { + value: 'edit', + title: this.$t('global.action.edit'), + }, + { + value: 'delete', + title: this.$t('global.action.delete'), + }, + ], + ipv4TableFields: [ + { + key: 'Address', + label: this.$t('pageNetwork.table.ipAddress'), + thStyle: { width: '20%' }, + }, + { + key: 'Gateway', + label: this.$t('pageNetwork.table.gateway'), + thStyle: { width: '20%' }, + }, + { + key: 'SubnetMask', + label: this.$t('pageNetwork.table.subnet'), + }, + { + key: 'AddressOrigin', + label: this.$t('pageNetwork.table.addressOrigin'), + thStyle: { width: '20%' }, + }, + { + key: 'actions', + label: this.$t('pageNetwork.table.actions'), + thStyle: { width: '10%' }, + thClass: 'text-right', + tdClass: 'text-right', + }, + ], + }; + }, + computed: { + ...mapState('network', ['ethernetData']), + }, + watch: { + // Watch for change in tab index + tabIndex() { + this.getIpv4TableItems(); + }, + ethernetData() { + this.getIpv4TableItems(); + }, + }, + created() { + this.getIpv4TableItems(); + this.$store.dispatch('network/getEthernetData').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('network-table-ipv4-complete'); + }); + }, + methods: { + getIpv4TableItems() { + const index = this.tabIndex; + const addresses = this.ethernetData[index].IPv4Addresses || []; + this.form.ipv4TableItems = addresses.map((ipv4) => { + return { + Address: ipv4.Address, + SubnetMask: ipv4.SubnetMask, + Gateway: ipv4.Gateway, + AddressOrigin: ipv4.AddressOrigin, + actions: [ + { + value: 'edit', + title: this.$t('pageNetwork.table.edit'), + }, + { + value: 'delete', + title: this.$t('pageNetwork.table.deleteIpv4'), + }, + ], + }; + }); + }, + onIpv4TableAction(action, $event, index) { + if ($event === 'delete') { + this.deleteIpv4TableRow(index); + } + }, + deleteIpv4TableRow(index) { + this.form.ipv4TableItems.splice(index, 1); + const newIpv4Array = this.form.ipv4TableItems.map((ipv4) => { + const { Address, SubnetMask, Gateway } = ipv4; + return { + Address, + SubnetMask, + Gateway, + }; + }); + this.$store + .dispatch('network/editIpv4Address', newIpv4Array) + .then((message) => this.successToast(message)) + .catch(({ message }) => this.errorToast(message)); + }, + initAddIpv4Address() { + this.$bvModal.show('modal-add-ipv4'); + }, + }, +}; +</script> diff --git a/src/views/_sila/Settings/Network/index.js b/src/views/_sila/Settings/Network/index.js new file mode 100644 index 00000000..97bf0397 --- /dev/null +++ b/src/views/_sila/Settings/Network/index.js @@ -0,0 +1,2 @@ +import Network from './Network.vue'; +export default Network; diff --git a/src/views/_sila/Settings/PowerRestorePolicy/PowerRestorePolicy.vue b/src/views/_sila/Settings/PowerRestorePolicy/PowerRestorePolicy.vue index c357d47a..06e30f3e 100644 --- a/src/views/_sila/Settings/PowerRestorePolicy/PowerRestorePolicy.vue +++ b/src/views/_sila/Settings/PowerRestorePolicy/PowerRestorePolicy.vue @@ -21,10 +21,10 @@ </template> <script> -import PageTitle from '@/components/_sila/Global/PageTitle'; -import LoadingBarMixin from '@/components/_sila/Mixins/LoadingBarMixin'; -import VuelidateMixin from '@/components/_sila/Mixins/VuelidateMixin.js'; -import BVToastMixin from '@/components/_sila/Mixins/BVToastMixin'; +import PageTitle from '@/components/Global/PageTitle'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; export default { name: 'PowerRestorePolicy', diff --git a/src/views/_sila/Support/Support.vue b/src/views/_sila/Support/Support.vue new file mode 100644 index 00000000..a23ad55f --- /dev/null +++ b/src/views/_sila/Support/Support.vue @@ -0,0 +1,9 @@ +<template> + <span>This Page (Support) In develop</span> +</template> + +<script> +export default { + name: 'Support', +}; +</script> diff --git a/src/views/_sila/Support/index.js b/src/views/_sila/Support/index.js new file mode 100644 index 00000000..80b31621 --- /dev/null +++ b/src/views/_sila/Support/index.js @@ -0,0 +1 @@ +export { default } from './Support.vue'; diff --git a/src/views/_sila/SystemDescription/Info/InventoryTableSystem.vue b/src/views/_sila/SystemDescription/Info/InventoryTableSystem.vue new file mode 100644 index 00000000..0c32d2c0 --- /dev/null +++ b/src/views/_sila/SystemDescription/Info/InventoryTableSystem.vue @@ -0,0 +1,81 @@ +<template> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + show-empty + class="table-rounded" + no-border-collapse + :items="items" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + </b-table> + </page-section> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import PageSection from '@/components/Global/PageSection'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { PageSection }, + mixins: [BVToastMixin, TableRowExpandMixin, LoadingBarMixin], + data() { + return { + fields: [ + { + key: 'param', + label: 'Параметр', + formatter: this.dataFormatter, + thStyle: { width: '50%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + }, + ], + expandRowLabel: expandRowLabel, + items: null, + }; + }, + computed: { + systems() { + return this.$store.getters['system/systems']; + }, + }, + watch: { + systems() { + this.items = [ + { + param: 'id', + value: this.id, + }, + { param: 'Модель', value: this.systems[0].model }, + { param: 'Производитель', value: '' }, + { param: 'Операционная система', value: '' }, + { param: 'Серийный номер', value: this.systems[0].serialNumber }, + { + param: 'Свободное место на Flash-накопителе', + value: '1 024 000 Мб', + }, + ]; + }, + }, + created() { + this.$store.dispatch('system/getSystem').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-system-complete'); + }); + this.startLoader(); + const systemTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-system-complete', () => resolve()); + }); + Promise.all([systemTablePromise]).finally(() => this.endLoader()); + }, +}; +</script> diff --git a/src/views/_sila/SystemDescription/Info/SystemDescription.vue b/src/views/_sila/SystemDescription/Info/SystemDescription.vue new file mode 100644 index 00000000..0a2bb305 --- /dev/null +++ b/src/views/_sila/SystemDescription/Info/SystemDescription.vue @@ -0,0 +1,148 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title /> + <!-- System table --> + <table-system /> + <!-- Notes Administration --> + <section class="notes-section"> + <div class="semi-bold-12px textarea-description"> + <span>{{ $t('SystemDescription.title.Notes') }}</span> + </div> + <div class="textarea-container"> + <div class="buttons-container"> + <button class="notes-button"> + <img src="@/assets/images/textarea-buttons/button-icon-bold.svg" /> + </button> + <button class="notes-button"> + <img + src="@/assets/images/textarea-buttons/button-icon-cursive.svg" + /> + </button> + <button class="notes-button"> + <img + src="@/assets/images/textarea-buttons/button-icon-underline.svg" + /> + </button> + <button class="notes-button"> + <img + src="@/assets/images/textarea-buttons/button-icon-crossline.svg" + /> + </button> + <button class="notes-button"> + <img src="@/assets/images/textarea-buttons/button-icon-link.svg" /> + </button> + <div class="line"></div> + <button class="notes-button"> + <img src="@/assets/images/textarea-buttons/button-icon-list.svg" /> + </button> + <button class="notes-button"> + <img + src="@/assets/images/textarea-buttons/button-icon-number-list.svg" + /> + </button> + </div> + <textarea + id="" + name="area" + placeholder="Тут будет текст который ввел администратор и работать в качестве блокнота" + cols="30" + rows="10" + class="notes-textarea" + ></textarea> + </div> + </section> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import TableSystem from './InventoryTableSystem'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +export default { + components: { + PageTitle, + TableSystem, + }, + data() { + return { + text: '', + iconChevronUp: iconChevronUp, + }; + }, +}; +</script> +<style lang="scss" scoped> +//nav items style +.nav-item, +.nav-link { + padding: 0; +} + +.nav-item { + list-style-type: none; +} + +a { + color: $text-primary !important; + &:hover { + color: $text-primary !important; + } +} +//section style +.notes-section { + display: flex; + flex-direction: column; +} + +.textarea-description { + margin: 5px 0 10px 2rem; +} + +.buttons-container { + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; +} + +.notes-button { + width: 2rem; + height: 2rem; + margin: 22px 0 22px 25px; + background-color: $surface-secondary; + border: none; + &:hover { + border-radius: 8px; + background-color: $faint-secondary-primary-5-hover; + } +} + +.line { + display: inline-block; + width: 1px; + height: 2rem; + margin: 22px 0 22px 25px; + border-left: 1px solid rgba(26, 62, 91, 0.2); +} + +.textarea-container { + display: flex; + flex-direction: column; + border-radius: 8px; + margin: 0 1rem 1rem 1rem; + background-color: $surface-secondary; +} + +.notes-textarea { + resize: none; + border: none; + flex: 0 0 auto; + margin: 0 1rem 1rem 1rem; + border-radius: 8px; + background-color: $white; +} +</style> diff --git a/src/views/_sila/SystemDescription/Info/index.js b/src/views/_sila/SystemDescription/Info/index.js new file mode 100644 index 00000000..8b483f2d --- /dev/null +++ b/src/views/_sila/SystemDescription/Info/index.js @@ -0,0 +1,2 @@ +import SystemDescription from './SystemDescription.vue'; +export default SystemDescription; diff --git a/src/views/_sila/SystemDescription/Network/InventoryIPv4Settings.vue b/src/views/_sila/SystemDescription/Network/InventoryIPv4Settings.vue new file mode 100644 index 00000000..980aee5a --- /dev/null +++ b/src/views/_sila/SystemDescription/Network/InventoryIPv4Settings.vue @@ -0,0 +1,277 @@ +<template> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + show-empty + no-border-collapse + :items="systems" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(value)="data"> + <b-row v-if="data.index === 0"> + <b-col> + <span> + {{ data.value ? 'Включен' : 'Выключен' }} + </span> + </b-col> + <b-col> + <b-button + :id="`popover-choice-${data.index}`" + class="popover-option-ractive" + variant="toogle-popover" + > + <img :is="iconChevron" class="icon-chevron" /> + </b-button> + <two-chioce-popover + :id="data.index" + fitst-option="Включен" + second-option="Выключен" + :chosen-option="chosenOption" + :first-action="setOn" + :second-action="setOff" + placement="leftbottom" + /> + </b-col> + </b-row> + <b-row v-else> + <b-form-input + v-if="systems[data.index].isEdit" + ref="input" + v-model="selectedCell" + class="regular-12px" + :class="{ validateIP: isIpInvalid }" + type="text" + @keydown.enter="clickOk(data)" + @keydown.escape="clickCancel(data)" + ></b-form-input> + <b-col v-else>{{ data.value }}</b-col> + <b-col class="table-network__icon"> + <b-row v-if="systems[data.index].isEdit"> + <img + src="@/assets/images/edit-ok.svg" + class="system-network-table__icon pointer" + @click="clickOk(data)" + /> + <img + src="@/assets/images/edit-no.svg" + class="system-network-table__icon close_icon pointer" + @click="clickCancel(data)" + /> + </b-row> + <img + v-else + src="@/assets/images/icon-edit.svg" + class="system-network-table__icon icon-edit pointer" + @click="editCellHandler(data)" + /> + </b-col> + </b-row> + </template> + </b-table> + </page-section> +</template> + +<script> +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import PageSection from '@/components/Global/PageSection'; +import iconChevron from '@carbon/icons-vue/es/chevron--down/16'; +import TwoChiocePopover from '@/components/Global/SilaComponents/TwoChiocePopover'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { PageSection, TwoChiocePopover }, + mixins: [BVToastMixin, TableRowExpandMixin, DataFormatterMixin], + data() { + return { + selectedCell: null, + isActive: false, + isIpInvalid: false, + chosenOption: 'Выключен', + fields: [ + { + key: 'param', + label: 'Параметр', + formatter: this.dataFormatter, + thStyle: { width: '30%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + { + key: 'comment', + label: 'Комментарий', + formatter: this.dataFormatter, + }, + ], + expandRowLabel: expandRowLabel, + iconChevron, + systems: [ + { + param: 'DHCP Server (откуда получен IP)', + value: false, + comment: 'Когда DHPC Server включен параметры вводятся автоматически', + }, + { + param: 'IP-адрес', + value: '192.168.1.1', + comment: 'Введите IP адрес', + }, + { + param: 'Маска', + value: '192.168.0.1', + comment: 'Введите маску сети', + }, + { + param: 'Сетевой шлюз', + value: '192.168.0.1', + comment: 'Введите сетевой шлюз', + }, + { + param: 'DNS', + value: '8.8.8.8', + comment: 'Введите DNS', + }, + ], + }; + }, + methods: { + editCellHandler(data) { + this.systems = this.systems.map((item) => ({ ...item, isEdit: false })); + this.systems[data.index].isEdit = true; + this.selectedCell = this.systems[data.index].value; + this.$nextTick(() => this.$refs.input.focus()); + }, + clickOk(data) { + const EDIT_VALUE = this.selectedCell.trim(); + if (this.validateIP(EDIT_VALUE)) { + this.systems[data.index].value = EDIT_VALUE; + this.isIpInvalid = false; + this.selectedCell = null; + this.systems[data.index].isEdit = false; + } else { + this.isIpInvalid = true; + } + }, + clickCancel(data) { + this.isIpInvalid = false; + this.selectedCell = null; + this.systems[data.index].isEdit = false; + }, + validateIP(ipCheck) { + return /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test( + ipCheck + ); + }, + setOn() { + this.chosenOption = 'Включен'; + this.systems[0].value = true; + }, + setOff() { + this.chosenOption = 'Выключен'; + this.systems[0].value = false; + }, + DHCPoff() { + this.systems[0].value = false; + this.isActive = false; + }, + DHCPon() { + this.systems[0].value = true; + this.isActive = false; + }, + }, +}; +</script> +<style lang="scss" scoped> +.row { + align-items: baseline; + flex-wrap: nowrap; +} +.icon-expand { + margin: 0 !important; +} + +.close_icon { + margin-left: 5px; +} + +.form-control { + max-height: 16px; + width: 60%; + background-color: transparent; + &:focus { + box-shadow: none; + } + &.validateIP { + color: $red-brand-primary; + } +} +.popup-container { + position: relative; +} + +.popup { + position: absolute; + top: -12px; + left: 3px; + width: 97%; + background: $white; + z-index: 1000; + border: 1px solid rgba(26, 62, 91, 0.1); + box-sizing: border-box; + box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1); + border-radius: 8px; + visibility: hidden; +} + +.popup-button { + width: 96%; + height: 50px; + margin: 4px; + border-radius: 8px; + border: none; + + display: flex; + align-items: center; + &.popup-on { + color: $red-brand-primary; + border-radius: 8px; + &:hover { + background-color: $faint-secondary-primary-5-hover; + } + &:active { + background-color: $faint-secondary-primary-20; + } + } + + &.popup-off { + background-color: $red-brand-primary; + color: $white; + &:hover { + background-color: $red-brand-primary-hover; + } + &:active { + background-color: $red-brand-primary-active; + } + } +} + +.popup-text { + margin-left: 20px; +} + +.popup_active { + visibility: visible; +} + +.popover-option-ractive { + display: block; + margin: -6px 9px 0 auto; +} +</style> diff --git a/src/views/_sila/SystemDescription/Network/InventoryIPv6Settings.vue b/src/views/_sila/SystemDescription/Network/InventoryIPv6Settings.vue new file mode 100644 index 00000000..a8c590b1 --- /dev/null +++ b/src/views/_sila/SystemDescription/Network/InventoryIPv6Settings.vue @@ -0,0 +1,263 @@ +<template> + <page-section class="bootstrap-table__section"> + <b-table + responsive="md" + show-empty + no-border-collapse + :items="systems" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <template #cell(value)="data"> + <b-row v-if="!(typeof data.value === 'boolean')"> + <b-form-input + v-if="systems[data.index].isEdit" + ref="input" + v-model="selectedCell" + class="regular-12px" + :class="{ validateIP: isIpInvalid }" + type="text" + @keydown.enter="clickOk(data)" + @keydown.escape="clickCancel(data)" + ></b-form-input> + <b-col v-else>{{ data.value }}</b-col> + <b-col class="table-network__icon"> + <b-row v-if="systems[data.index].isEdit"> + <img + src="@/assets/images/edit-ok.svg" + class="system-network-table__icon pointer" + @click="clickOk(data)" + /> + <img + src="@/assets/images/edit-no.svg" + class="system-network-table__icon close_icon pointer" + @click="clickCancel(data)" + /> + </b-row> + <img + v-else + src="@/assets/images/icon-edit.svg" + class="system-network-table__icon pointer" + @click="editCellHandler(data)" + /> + </b-col> + </b-row> + <b-row v-else class="popup-container"> + <b-col v-if="data.value">{{ 'Включен' }}</b-col> + <b-col v-else>{{ 'Выключен' }}</b-col> + <div + v-if="isActive" + class="popup" + :class="{ popup_active: isActive }" + > + <button class="popup-button popup-on medium-12px" @click="DHCPon"> + <span class="popup-text">Включен</span> + </button> + <button class="popup-button popup-off medium-12px" @click="DHCPoff"> + <span class="popup-text">Выключен</span> + </button> + </div> + <b-col class="table-network__icon"> + <img + :is="iconChevron" + class="pointer" + @click="isActive = !isActive" + /> + </b-col> + </b-row> + </template> + </b-table> + </page-section> +</template> + +<script> +import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import PageSection from '@/components/Global/PageSection'; +import iconChevron from '@carbon/icons-vue/es/chevron--down/16'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { PageSection }, + mixins: [BVToastMixin, TableRowExpandMixin, DataFormatterMixin], + data() { + return { + isBusy: true, + selectedCell: null, + isIpInvalid: false, + isActive: false, + fields: [ + { + key: 'param', + label: 'Параметр', + formatter: this.dataFormatter, + thStyle: { width: '30%' }, + }, + { + key: 'value', + label: 'Значение', + formatter: this.dataFormatter, + thStyle: { width: '20%' }, + }, + { + key: 'comment', + label: 'Комментарий', + formatter: this.dataFormatter, + }, + ], + expandRowLabel: expandRowLabel, + iconChevron, + systems: [ + { + param: 'DHCP Server (откуда получен IP)', + value: false, + comment: 'Когда DHPC Server включен параметры вводятся автоматически', + }, + { + param: 'IP-адрес', + value: 'fe80::1ff:fe23:4567:890a', + comment: 'Введите IP адрес', + }, + { + param: 'Маска', + value: 'fe80::1ff:fe23:4567:890a', + comment: 'Введите маску сети', + }, + { + param: 'Сетевой шлюз', + value: 'fe80::1ff:fe23:4567:890a', + comment: 'Введите сетевой шлюз', + }, + { + param: 'DNS', + value: 'fe80::1ff:fe23:4567:890a', + comment: 'Введите DNS', + }, + ], + }; + }, + methods: { + editCellHandler(data) { + this.systems = this.systems.map((item) => ({ ...item, isEdit: false })); + this.systems[data.index].isEdit = true; + this.selectedCell = this.systems[data.index].value; + this.$nextTick(() => this.$refs.input.focus()); + }, + clickOk(data) { + const EDIT_VALUE = this.selectedCell.trim(); + if (this.validateIP(EDIT_VALUE)) { + this.systems[data.index].value = EDIT_VALUE; + this.isIpInvalid = false; + this.selectedCell = null; + this.systems[data.index].isEdit = false; + } else { + this.isIpInvalid = true; + } + }, + clickCancel(data) { + this.isIpInvalid = false; + this.selectedCell = null; + this.systems[data.index].isEdit = false; + }, + validateIP(ipCheck) { + return /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/gi.test( + ipCheck + ); + }, + DHCPoff() { + this.systems[0].value = false; + this.isActive = false; + }, + DHCPon() { + this.systems[0].value = true; + this.isActive = false; + }, + }, +}; +</script> +<style lang="scss" scoped> +.row { + align-items: baseline; + flex-wrap: nowrap; +} +.icon-expand { + margin: 0 !important; +} + +.close_icon { + margin-left: 5px; +} + +.form-control { + max-height: 16px; + width: 60%; + background-color: transparent; + &:focus { + box-shadow: none; + } + &.validateIP { + color: $red-brand-primary; + } +} + +.popup-container { + position: relative; +} + +.popup { + position: absolute; + top: -12px; + left: 3px; + width: 97%; + background: $white; + z-index: 1000; + border: 1px solid rgba(26, 62, 91, 0.1); + box-sizing: border-box; + box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1); + border-radius: 8px; + visibility: hidden; +} + +.popup-button { + width: 96%; + height: 50px; + margin: 4px; + border-radius: 8px; + border: none; + cursor: pointer; + + display: flex; + align-items: center; + &.popup-on { + color: $red-brand-primary; + border-radius: 8px; + &:hover { + background-color: $faint-secondary-primary-5-hover; + } + &:active { + background-color: $faint-secondary-primary-20; + } + } + + &.popup-off { + background-color: $red-brand-primary; + color: $white; + &:hover { + background-color: $red-brand-primary-hover; + } + &:active { + background-color: $red-brand-primary-active; + } + } +} + +.popup-text { + margin-left: 20px; +} + +.popup_active { + visibility: visible; +} +</style> diff --git a/src/views/_sila/SystemDescription/Network/SystemNetwork.vue b/src/views/_sila/SystemDescription/Network/SystemNetwork.vue new file mode 100644 index 00000000..19243493 --- /dev/null +++ b/src/views/_sila/SystemDescription/Network/SystemNetwork.vue @@ -0,0 +1,101 @@ +<template> + <b-container + id="page-system-network" + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.networkParametrs')" /> + <!-- IPv4 --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_1 + variant="link" + class="collapse-button semi-bold-16px" + @click="ipv4Handler" + > + {{ $t('SystemDescription.title.Ipv4Settings') }} + <b-form-checkbox + id="checkbox-1" + v-model="ipv4" + @click.native.prevent + ></b-form-checkbox> + </b-button> + <b-collapse id="toggle-collapse_1" class="nav-item__nav"> + <i-pv4-settings ref="system" /> + </b-collapse> + </div> + <!-- IPv6 --> + <div class="page-collapse-decorator"> + <b-button + v-b-toggle.toggle-collapse_2 + variant="link" + class="collapse-button semi-bold-16px" + @click="ipv6Handler" + > + {{ $t('SystemDescription.title.Ipv6Settings') }} + <b-form-checkbox + id="checkbox-2" + v-model="ipv6" + @click.native.prevent + ></b-form-checkbox> + </b-button> + <b-collapse id="toggle-collapse_2" class="nav-item__nav"> + <i-pv6-settings ref="system" /> + </b-collapse> + </div> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import IPv4Settings from './InventoryIPv4Settings'; +import IPv6Settings from './InventoryIPv6Settings'; +import iconChevronUp from '@carbon/icons-vue/es/chevron--up/16'; + +export default { + components: { + PageTitle, + IPv4Settings, + IPv6Settings, + }, + data() { + return { + text: '', + iconChevronUp: iconChevronUp, + ipv4: false, + ipv6: false, + }; + }, + methods: { + ipv4Handler() { + this.ipv4 = !this.ipv4; + }, + ipv6Handler() { + this.ipv6 = !this.ipv6; + }, + }, +}; +</script> +<style lang="scss" scoped> +//nav items style +.nav-item, +.nav-link { + padding: 0; +} + +.nav-item { + list-style-type: none; +} + +a { + color: $text-primary !important; + &:hover { + color: $text-primary !important; + } +} + +.custom-checkbox { + background-color: none; + margin: 0 20px 0 auto; +} +</style> diff --git a/src/views/_sila/SystemDescription/Network/index.js b/src/views/_sila/SystemDescription/Network/index.js new file mode 100644 index 00000000..25f85f3c --- /dev/null +++ b/src/views/_sila/SystemDescription/Network/index.js @@ -0,0 +1,2 @@ +import SystemNetwork from './SystemNetwork.vue'; +export default SystemNetwork; diff --git a/src/views/_sila/SystemDescription/ServerParametrs/ServerParametrs.vue b/src/views/_sila/SystemDescription/ServerParametrs/ServerParametrs.vue new file mode 100644 index 00000000..9eb57301 --- /dev/null +++ b/src/views/_sila/SystemDescription/ServerParametrs/ServerParametrs.vue @@ -0,0 +1,21 @@ +<template> + <b-container + :style="{ display: 'flex', 'flex-direction': 'column' }" + fluid="xxl pt-0 m-0" + > + <page-title :description="$t('appNavigation.serverParam')" /> + <servere-parametrs-section /> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import ServereParametrsSection from './ServereParametrsSection'; + +export default { + components: { + PageTitle, + ServereParametrsSection, + }, +}; +</script> diff --git a/src/views/_sila/SystemDescription/ServerParametrs/ServereParametrsSection.vue b/src/views/_sila/SystemDescription/ServerParametrs/ServereParametrsSection.vue new file mode 100644 index 00000000..f8aebeb6 --- /dev/null +++ b/src/views/_sila/SystemDescription/ServerParametrs/ServereParametrsSection.vue @@ -0,0 +1,128 @@ +<template> + <page-section class="bootstrap-table__section"> + <span class="bold-12px__caps"> + {{ $t('SystemDescription.LoadingQueue') }} + </span> + <b-table + responsive="md" + class="table-stripes" + :items="queueItems" + :fields="fields" + > + <template #cell(active)="data"> + <b-row> + <b-col> + <span v-if="queueItems[data.index].active"> + {{ $t('global.status.enabled') }} + </span> + <span v-else> + {{ $t('global.status.disabled') }} + </span> + </b-col> + <b-col> + <b-form-checkbox + v-model="queueItems[data.index].active" + switch + @change="toggleLoad" + > + </b-form-checkbox> + </b-col> + </b-row> + </template> + </b-table> + <span class="bold-12px__caps"> + {{ $t('SystemDescription.DiskParametrs') }} + </span> + <b-table + responsive="md" + class="table-stripes" + :items="diskItems" + :fields="fields" + > + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import iconChevron from '@carbon/icons-vue/es/chevron--down/16'; + +export default { + components: { PageSection }, + data() { + return { + selectedCell: null, + fields: [ + { + key: 'param', + label: '', + formatter: this.dataFormatter, + }, + { + key: 'active', + label: '', + formatter: this.dataFormatter, + }, + ], + iconChevron, + queueItems: [ + { + param: 'Hard Drive C:', + active: false, + }, + { + param: + 'Embedded NIC 2 Port 1 Partition 1: BRCM MBA Slot E101 v21.6.0', + active: false, + }, + { + param: 'Virtual Floppy Drive', + active: false, + }, + { + param: 'Virtual Optical Drive', + active: false, + }, + ], + diskItems: [ + { + param: 'Internal SD', + active: 'IDSM', + }, + { + param: 'ACHI Controller in SL7', + active: 'A0S0 MTFDDAV480TDS', + }, + { + param: 'ACHI Controller in SL7', + active: 'A0S1 MTFDDAV480TDS', + }, + { + param: 'RAID Controller in SL8', + active: 'PERC H755 Front (bus 01 dev 00)', + }, + ], + }; + }, + methods: { + toggleLoad(data, value) { + this.queueItems[data.index].value = !value; + }, + }, +}; +</script> +<style lang="scss" scoped> +.row { + align-items: center; + flex-wrap: nowrap; + justify-content: flex-end; +} +.icon-expand { + margin: 0 !important; +} + +.bold-12px__caps { + display: block; + margin: 16px 0 7px 0; +} +</style> diff --git a/src/views/_sila/SystemDescription/ServerParametrs/index.js b/src/views/_sila/SystemDescription/ServerParametrs/index.js new file mode 100644 index 00000000..5ce6ca74 --- /dev/null +++ b/src/views/_sila/SystemDescription/ServerParametrs/index.js @@ -0,0 +1,2 @@ +import ServerParametrs from './ServerParametrs.vue'; +export default ServerParametrs; |