├── .browserslistrc ├── .env.ibm ├── .env.intel ├── .eslintrc.js ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── design-review.md │ └── feature_request.md └── workflows │ └── deploy-docs-to-gh-pages.yml ├── .gitignore ├── .npmrc ├── .shellcheck ├── CONTRIBUTING.md ├── LICENSE ├── OWNERS ├── README.md ├── babel.config.js ├── docs ├── .vuepress │ ├── components │ │ └── colors │ │ │ ├── all.vue │ │ │ ├── blues.vue │ │ │ ├── colors.scss │ │ │ ├── grays.vue │ │ │ ├── greens.vue │ │ │ ├── reds.vue │ │ │ ├── theme.vue │ │ │ └── yellows.vue │ ├── config.js │ ├── public │ │ └── openbmc-logo.svg │ └── styles │ │ └── palette.styl ├── customization │ ├── build.md │ ├── readme.md │ └── theme.md ├── guide │ ├── coding-standards │ │ ├── accessibility.md │ │ └── readme.md │ ├── components │ │ ├── alerts │ │ │ ├── alert.png │ │ │ └── index.md │ │ ├── buttons │ │ │ ├── button-disabled.png │ │ │ ├── button.png │ │ │ └── index.md │ │ ├── file-upload │ │ │ ├── formfile.png │ │ │ └── readme.md │ │ ├── info-tooltip │ │ │ ├── index.md │ │ │ └── info-tooltip.png │ │ ├── page-section │ │ │ └── index.md │ │ ├── page-title │ │ │ └── index.md │ │ ├── readme.md │ │ ├── status-icon │ │ │ ├── appHeaderWithStatusIcon.png │ │ │ ├── danger.png │ │ │ ├── eventLogsWithSatusIcon.png │ │ │ ├── info.png │ │ │ ├── readme.md │ │ │ ├── secondary.png │ │ │ ├── success.png │ │ │ └── warning.png │ │ ├── table │ │ │ ├── index.md │ │ │ ├── table-batch-action-active.png │ │ │ ├── table-batch-action.png │ │ │ ├── table-empty.png │ │ │ ├── table-expand-row.png │ │ │ ├── table-filter-active.png │ │ │ ├── table-filter.png │ │ │ ├── table-pagination.png │ │ │ ├── table-row-actions.png │ │ │ ├── table-search-active.png │ │ │ ├── table-search-empty.png │ │ │ ├── table-search.png │ │ │ ├── table-sort.png │ │ │ └── table.png │ │ └── toasts │ │ │ ├── index.md │ │ │ ├── toast-options.png │ │ │ └── toast.png │ ├── guidelines │ │ ├── colors.md │ │ ├── internationalization.md │ │ ├── motion.md │ │ └── typography.md │ ├── quickstart │ │ ├── forms.md │ │ ├── page-anatomy.md │ │ └── store-anatomy.md │ ├── readme.md │ └── unit-testing │ │ └── readme.md └── readme.md ├── format-code.sh ├── jest.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico └── index.html ├── run-ci ├── src ├── App.vue ├── assets │ ├── images │ │ ├── built-on-openbmc-logo.svg │ │ ├── login-company-logo.svg │ │ └── logo-header.svg │ └── styles │ │ ├── _obmc-custom.scss │ │ ├── bmc │ │ ├── custom │ │ │ ├── _alert.scss │ │ │ ├── _badge.scss │ │ │ ├── _base.scss │ │ │ ├── _bootstrap-grid.scss │ │ │ ├── _buttons.scss │ │ │ ├── _calendar.scss │ │ │ ├── _card.scss │ │ │ ├── _dropdown.scss │ │ │ ├── _forms.scss │ │ │ ├── _index.scss │ │ │ ├── _kvm.scss │ │ │ ├── _modal.scss │ │ │ ├── _pagination.scss │ │ │ ├── _section-divider.scss │ │ │ ├── _sol.scss │ │ │ ├── _tables.scss │ │ │ └── _toasts.scss │ │ └── helpers │ │ │ ├── _colors.scss │ │ │ ├── _functions.scss │ │ │ ├── _index.scss │ │ │ ├── _motion.scss │ │ │ └── _variables.scss │ │ └── bootstrap │ │ ├── _helpers.scss │ │ └── _index.scss ├── components │ ├── AppHeader │ │ ├── AppHeader.vue │ │ └── index.js │ ├── AppNavigation │ │ ├── AppNavigation.vue │ │ ├── AppNavigationMixin.js │ │ └── index.js │ ├── Global │ │ ├── Alert.vue │ │ ├── ButtonBackToTop.vue │ │ ├── FormFile.vue │ │ ├── InfoTooltip.vue │ │ ├── InputPasswordToggle.vue │ │ ├── LoadingBar.vue │ │ ├── PageContainer.vue │ │ ├── PageSection.vue │ │ ├── PageTitle.vue │ │ ├── Search.vue │ │ ├── StatusIcon.vue │ │ ├── TableCellCount.vue │ │ ├── TableDateFilter.vue │ │ ├── TableFilter.vue │ │ ├── TableRowAction.vue │ │ ├── TableToolbar.vue │ │ └── TableToolbarExport.vue │ └── Mixins │ │ ├── BVPaginationMixin.js │ │ ├── BVTableSelectableMixin.js │ │ ├── BVToastMixin.js │ │ ├── DataFormatterMixin.js │ │ ├── JumpLinkMixin.js │ │ ├── LoadingBarMixin.js │ │ ├── LocalTimezoneLabelMixin.js │ │ ├── SearchFilterMixin.js │ │ ├── TableFilterMixin.js │ │ ├── TableRowExpandMixin.js │ │ ├── TableSortMixin.js │ │ └── VuelidateMixin.js ├── env │ ├── assets │ │ ├── fonts │ │ │ ├── IBM_Plex_Sans │ │ │ │ ├── IBMPlexSans-Italic.woff │ │ │ │ ├── IBMPlexSans-Light.woff │ │ │ │ ├── IBMPlexSans-LightItalic.woff │ │ │ │ ├── IBMPlexSans-Regular.woff │ │ │ │ ├── IBMPlexSans-SemiBold.woff │ │ │ │ └── IBMPlexSans-SemiBoldItalic.woff │ │ │ └── Intel_Clear │ │ │ │ ├── IntelClear-Bold-webfont.woff │ │ │ │ ├── IntelClear-Light-webfont.woff │ │ │ │ └── IntelClear-Regular-webfont.woff │ │ └── styles │ │ │ ├── _default.scss │ │ │ ├── _ibm.scss │ │ │ └── _intel.scss │ ├── components │ │ └── AppNavigation │ │ │ ├── ibm.js │ │ │ └── intel.js │ ├── router │ │ ├── ibm.js │ │ └── intel.js │ └── store │ │ ├── ibm.js │ │ └── intel.js ├── eventBus.js ├── i18n.js ├── layouts │ ├── AppLayout.vue │ ├── ConsoleLayout.vue │ └── LoginLayout.vue ├── locales │ ├── en-US.json │ ├── ka-GE.json │ └── ru-RU.json ├── main.js ├── router │ ├── index.js │ └── routes.js ├── store │ ├── api.js │ ├── index.js │ └── modules │ │ ├── Authentication │ │ └── AuthenticanStore.js │ │ ├── GlobalStore.js │ │ ├── HardwareStatus │ │ ├── AssemblyStore.js │ │ ├── BmcStore.js │ │ ├── ChassisStore.js │ │ ├── FanStore.js │ │ ├── MemoryStore.js │ │ ├── PowerSupplyStore.js │ │ ├── ProcessorStore.js │ │ ├── SensorsStore.js │ │ ├── ServerLedStore.js │ │ └── SystemStore.js │ │ ├── Logs │ │ ├── DumpsStore.js │ │ ├── EventLogStore.js │ │ └── PostCodeLogsStore.js │ │ ├── Operations │ │ ├── BootSettingsStore.js │ │ ├── ControlStore.js │ │ ├── FactoryResetStore.js │ │ ├── FirmwareStore.js │ │ ├── KeyClearStore.js │ │ └── VirtualMediaStore.js │ │ ├── ResourceManagement │ │ └── PowerControlStore.js │ │ ├── SecurityAndAccess │ │ ├── CertificatesStore.js │ │ ├── LdapStore.js │ │ ├── PoliciesStore.js │ │ ├── SessionsStore.js │ │ └── UserManagementStore.js │ │ └── Settings │ │ ├── DateTimeStore.js │ │ ├── NetworkStore.js │ │ ├── PowerPolicyStore.js │ │ └── SnmpAlertsStore.js ├── utilities │ └── NBDServer.js └── views │ ├── ChangePassword │ ├── ChangePassword.vue │ └── index.js │ ├── HardwareStatus │ ├── Inventory │ │ ├── Inventory.vue │ │ ├── InventoryServiceIndicator.vue │ │ ├── InventoryTableAssembly.vue │ │ ├── InventoryTableBmcManager.vue │ │ ├── InventoryTableChassis.vue │ │ ├── InventoryTableDimmSlot.vue │ │ ├── InventoryTableFans.vue │ │ ├── InventoryTablePowerSupplies.vue │ │ ├── InventoryTableProcessors.vue │ │ ├── InventoryTableSystem.vue │ │ └── index.js │ └── Sensors │ │ ├── Sensors.vue │ │ └── index.js │ ├── Login │ ├── Login.vue │ └── index.js │ ├── Logs │ ├── Dumps │ │ ├── Dumps.vue │ │ ├── DumpsForm.vue │ │ ├── DumpsModalConfirmation.vue │ │ └── index.js │ ├── EventLogs │ │ ├── EventLogs.vue │ │ └── index.js │ └── PostCodeLogs │ │ ├── PostCodeLogs.vue │ │ └── index.js │ ├── Operations │ ├── FactoryReset │ │ ├── FactoryReset.vue │ │ ├── FactoryResetModal.vue │ │ └── index.js │ ├── Firmware │ │ ├── Firmware.vue │ │ ├── FirmwareAlertServerPower.vue │ │ ├── FirmwareCardsBios.vue │ │ ├── FirmwareCardsBmc.vue │ │ ├── FirmwareFormUpdate.vue │ │ ├── FirmwareModalSwitchToRunning.vue │ │ ├── FirmwareModalUpdateFirmware.vue │ │ └── index.js │ ├── KeyClear │ │ ├── KeyClear.vue │ │ └── index.js │ ├── Kvm │ │ ├── Kvm.vue │ │ ├── KvmConsole.vue │ │ └── index.js │ ├── RebootBmc │ │ ├── RebootBmc.vue │ │ └── index.js │ ├── SerialOverLan │ │ ├── SerialOverLan.vue │ │ ├── SerialOverLanConsole.vue │ │ └── index.js │ ├── ServerPowerOperations │ │ ├── BootSettings.vue │ │ ├── ServerPowerOperations.vue │ │ └── index.js │ └── VirtualMedia │ │ ├── ModalConfigureConnection.vue │ │ ├── VirtualMedia.vue │ │ └── index.js │ ├── Overview │ ├── Overview.vue │ ├── OverviewCard.vue │ ├── OverviewDumps.vue │ ├── OverviewEvents.vue │ ├── OverviewFirmware.vue │ ├── OverviewInventory.vue │ ├── OverviewNetwork.vue │ ├── OverviewPower.vue │ ├── OverviewQuickLinks.vue │ ├── OverviewServer.vue │ └── index.js │ ├── PageNotFound │ ├── PageNotFound.vue │ └── index.js │ ├── ProfileSettings │ ├── ProfileSettings.vue │ └── index.js │ ├── ResourceManagement │ ├── Power.vue │ └── index.js │ ├── SecurityAndAccess │ ├── Certificates │ │ ├── Certificates.vue │ │ ├── CsrCountryCodes.js │ │ ├── ModalGenerateCsr.vue │ │ ├── ModalUploadCertificate.vue │ │ └── index.js │ ├── Ldap │ │ ├── Ldap.vue │ │ ├── ModalAddRoleGroup.vue │ │ ├── TableRoleGroups.vue │ │ └── index.js │ ├── Policies │ │ ├── Policies.vue │ │ └── index.js │ ├── Sessions │ │ ├── Sessions.vue │ │ └── index.js │ └── UserManagement │ │ ├── ModalSettings.vue │ │ ├── ModalUser.vue │ │ ├── TableRoles.vue │ │ ├── UserManagement.vue │ │ └── index.js │ └── Settings │ ├── DateTime │ ├── DateTime.vue │ └── index.js │ ├── Network │ ├── ModalDefaultGateway.vue │ ├── ModalDns.vue │ ├── ModalHostname.vue │ ├── ModalIpv4.vue │ ├── ModalIpv6.vue │ ├── ModalMacAddress.vue │ ├── Network.vue │ ├── NetworkGlobalSettings.vue │ ├── NetworkInterfaceSettings.vue │ ├── TableDns.vue │ ├── TableIpv4.vue │ ├── TableIpv6.vue │ └── index.js │ ├── PowerRestorePolicy │ ├── PowerRestorePolicy.vue │ └── index.js │ └── SnmpAlerts │ ├── ModalAddDestination.vue │ ├── SnmpAlerts.vue │ └── index.js ├── tests └── unit │ ├── AppHeader.spec.js │ ├── AppNavigation.spec.js │ ├── Global │ ├── InfoTooltip.spec.js │ ├── InputPasswordToggle.spec.js │ ├── LoadingBar.spec.js │ ├── PageContainer.spec.js │ ├── PageSection.spec.js │ ├── PageTitle.spec.js │ ├── Search.spec.js │ ├── StatusIcon.spec.js │ ├── TableCellCount.spec.js │ ├── TableToolbar.spec.js │ └── __snapshots__ │ │ ├── InfoTooltip.spec.js.snap │ │ ├── InputPasswordToggle.spec.js.snap │ │ ├── LoadingBar.spec.js.snap │ │ ├── PageContainer.spec.js.snap │ │ ├── PageSection.spec.js.snap │ │ ├── PageTitle.spec.js.snap │ │ ├── Search.spec.js.snap │ │ ├── StatusIcon.spec.js.snap │ │ ├── TableCellCount.spec.js.snap │ │ └── TableToolbar.spec.js.snap │ └── __snapshots__ │ ├── AppHeader.spec.js.snap │ └── AppNavigation.spec.js.snap └── vue.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults -------------------------------------------------------------------------------- /.env.ibm: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_ENV_NAME=ibm 3 | VUE_APP_COMPANY_NAME="IBM" 4 | VUE_APP_GUI_NAME="BMC System Management" 5 | CUSTOM_STYLES=true 6 | CUSTOM_APP_NAV=true 7 | CUSTOM_ROUTER=true 8 | CUSTOM_STORE=true 9 | VUE_APP_SERVER_OFF_REQUIRED=true 10 | -------------------------------------------------------------------------------- /.env.intel: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_ENV_NAME="intel" 3 | VUE_APP_COMPANY_NAME="intel" 4 | VUE_APP_GUI_NAME="BMC System Management" 5 | VUE_APP_SWITCH_TO_BACKUP_IMAGE_DISABLED="true" 6 | VUE_APP_MODIFY_SSH_POLICY_DISABLED="true" 7 | VUE_APP_VIRTUAL_MEDIA_LIST_ENABLED="true" 8 | VUE_APP_EVENT_LOGS_DELETE_BUTTON_DISABLED="true" 9 | VUE_APP_EVENT_LOGS_TOGGLE_BUTTON_DISABLED="true" 10 | CUSTOM_STYLES="true" 11 | CUSTOM_APP_NAV="true" 12 | CUSTOM_STORE="true" 13 | CUSTOM_ROUTER="true" 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-recommended', 8 | 'eslint:recommended', 9 | '@vue/prettier', 10 | ], 11 | rules: { 12 | 'no-console': 'off', 13 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 14 | 'prettier/prettier': [ 15 | 'error', 16 | { 17 | singleQuote: true, 18 | trailingComma: 'all', 19 | }, 20 | ], 21 | 'vue/component-name-in-template-casing': ['error', 'kebab-case'], 22 | 'vue/multi-word-component-names': 'off', 23 | 'vue/no-deprecated-filter': 'off', 24 | 'vue/no-useless-template-attributes': 'off', 25 | 'vue/no-deprecated-props-default-this': 'off', 26 | }, 27 | parser: 'vue-eslint-parser', 28 | overrides: [ 29 | { 30 | files: [ 31 | '**/__tests__/*.{j,t}s?(x)', 32 | '**/tests/unit/**/*.spec.{j,t}s?(x)', 33 | ], 34 | env: { 35 | jest: true, 36 | }, 37 | }, 38 | ], 39 | }; 40 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.png binary 3 | *.woff binary -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** A clear and concise description of what the bug is. 10 | 11 | **To Reproduce** Steps to reproduce the behavior: 12 | 13 | 1. Go to '...' 14 | 2. Click on '....' 15 | 3. Scroll down to '....' 16 | 4. See error 17 | 18 | **Expected behavior** A clear and concise description of what you expected to 19 | happen. 20 | 21 | **Screenshots** If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | 25 | - OS: [e.g. iOS] 26 | - Browser [e.g. chrome, safari] 27 | - Version [e.g. 22] 28 | 29 | **Smartphone (please complete the following information):** 30 | 31 | - Device: [e.g. iPhone6] 32 | - OS: [e.g. iOS8.1] 33 | - Browser [e.g. stock browser, safari] 34 | - Version [e.g. 22] 35 | 36 | **Additional context** Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/design-review.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Design review 3 | about: Create story used to track design proposals and decisions 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | ## Use Case 10 | 11 | As a **\_\_\_** user, I need to be able to **\_\_\_** so that I can **\_\_\_**. 12 | 13 | ## Invision Prototype Guidance 14 | 15 | The Invision prototypes have hot spots allowing the reviewer to step through the 16 | workflow. To navigate a prototype, you can: 17 | 18 | 1. Click on the page and the hotspots will be revealed in blue 19 | 1. Use the arrow-right and arrow-left keys on your keyboard 20 | 21 | ## Design Review Workflow 22 | 23 | 1. Each design iteration will have a comment section 24 | 1. The section will include: 25 | - A title with the iteration number 26 | - Any description or specific feedback the designer is requesting 27 | - Screenshots of the workflow 28 | 1. Based on community and user feedback, we create a comment for the new 29 | iteration and repeat these steps 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** A clear and 10 | concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** A clear and concise description of what you 13 | want to happen. 14 | 15 | **Describe alternatives you've considered** A clear and concise description of 16 | any alternative solutions or features you've considered. 17 | 18 | **Additional context** Add any other context or screenshots about the feature 19 | request here. 20 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs-to-gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy documentation to GitHub pages 2 | on: 3 | push: 4 | branches: [master] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Generate static vuepress files 13 | uses: actions/setup-node@v1 14 | with: 15 | node-version: "12.x" 16 | - run: npm ci 17 | - run: npm run docs:build 18 | 19 | - name: Init new repo in dist folder and commit generated files 20 | run: | 21 | cd docs/.vuepress/dist 22 | git init 23 | git add -A 24 | git config --local user.email "action@github.com" 25 | git config --local user.name "GitHub Action" 26 | git commit -m 'deploy' 27 | 28 | - name: Force push to gh-pages branch 29 | uses: ad-m/github-push-action@v0.5.0 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | branch: gh-pages 33 | force: true 34 | directory: ./docs/.vuepress/dist 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /docs/.vuepress/dist 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | .env 24 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true -------------------------------------------------------------------------------- /.shellcheck: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/.shellcheck -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | # OWNERS 2 | # ------ 3 | # 4 | # The OWNERS file maintains the list of individuals responsible for various 5 | # parts of this repository, including code review and approval. We use the 6 | # Gerrit 'owners' plugin, which consumes this file, along with some extra 7 | # keywords for our own purposes and tooling. 8 | # 9 | # For details on the configuration used by 'owners' see: 10 | # https://gerrit.googlesource.com/plugins/owners/+/refs/heads/master/owners/src/main/resources/Documentation/config.md 11 | # 12 | # An OWNERS file must be in the root of a repository but may also be present 13 | # in any subdirectory. The contents of the subdirectory OWNERS file are 14 | # combined with parent directories unless 'inherit: false' is set. 15 | # 16 | # The owners file is YAML and has [up to] 4 top-level keywords. 17 | # * owners: A list of individuals who have approval authority on the 18 | # repository. 19 | # 20 | # * reviewers: A list of individuals who have requested review notification 21 | # on the repository. 22 | # 23 | # * matchers: A list of specific file/path matchers for granular 'owners' and 24 | # 'reviewers'. See 'owners' plugin documentation. 25 | # 26 | # * openbmc: A list of openbmc-specific meta-data about owners and reviewers. 27 | # - name: preferred name of the individual. 28 | # - email: preferred email address of the individual. 29 | # - discord: Discord nickname of the individual. 30 | # 31 | # It is expected that these 4 sections will be listed in the order above and 32 | # data within them will be kept sorted. 33 | 34 | owners: 35 | - gunnar@gmills.xyz 36 | - kirankumarb@ami.com 37 | 38 | reviewers: 39 | - a.nikhil@ibm.com 40 | - jwestover@nvidia.com 41 | - sivaprabug@ami.com 42 | 43 | matchers: 44 | 45 | openbmc: 46 | - name: Gunnar Mills 47 | email: gunnar@gmills.xyz 48 | discord: GunnarM 49 | - name: Jason Westover 50 | email: jwestover@nvidia.com 51 | discord: jasonwestover 52 | - name: Kirankumar Ballapalli 53 | email: kirankumarb@ami.com 54 | discord: kirankumarb 55 | - name: Nikhil Ashoka 56 | email: a.nikhil@ibm.com 57 | discord: NikhilAshoka 58 | - name: Sivaprabu Ganesan 59 | email: sivaprabug@ami.com 60 | discord: sivaprabug 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webui-vue 2 | 3 | webui-vue is a web-based user interface for the OpenBMC firmware stack built on 4 | [Vue.js](https://vuejs.org/). 5 | 6 | ## Hold on... What happened to phosphor-webui? 7 | 8 | [phosphor-webui](https://github.com/openbmc/phosphor-webui) was built on 9 | AngularJS and 10 | [AngularJS went End of Life](https://www.convective.com/angularjs-end-of-life/) 11 | on June 30, 2021. This repository is its replacement. 12 | 13 | ## When will this new Vue.js application reach feature parity with phosphor-webui? 14 | 15 | A few, mostly minor, features remain for feature parity. See 16 | [GitHub Issues label:phosphor-webui-feature-parity](https://github.com/openbmc/webui-vue/issues?q=is%3Aissue+is%3Aopen+label%3Aphosphor-webui-feature-parity) 17 | for the complete list. 18 | 19 | ## What improvements does webui-vue have? 20 | 21 | As mentioned, this application is built using Vue.js, a modern open-source 22 | Model-View-ViewModel JavaScript framework supported by an active community and 23 | strong documentation. It has been architected to allow organizations to easily 24 | update the theme to support their brand. This rewrite takes advantage of 25 | front-end development best practices and does not suffer from some of the 26 | anti-patterns that exist in phosphor-webui today. 27 | 28 | ## Should I switch to webui-vue from phosphor-webui? 29 | 30 | It is recommended you switch from phosphor-webui if you haven't already. 31 | 32 | - [Commit moving several systems to webui-vue](https://github.com/openbmc/openbmc/commit/4a3fa4d6d865b46ba54f2652c82f58a406455ebc) 33 | - [Discussion about webui-vue being the standard](https://lists.ozlabs.org/pipermail/openbmc/2020-September/023160.html) 34 | 35 | webui-vue has the following additional features: 36 | 37 | - Ability to easily theme to meet brand guidelines 38 | - Accessibility 39 | - Full Redfish 40 | - Improved user experience based on user feedback 41 | - Language translation-ready 42 | - Modern front-end framework with an active community and future development 43 | roadmap 44 | 45 | ## How can I get involved? 46 | 47 | - Visit the [CONTRIBUTING.md](CONTRIBUTING.md) for more on how to contribute 48 | code 49 | - Review some code in 50 | [Gerrit](https://gerrit.openbmc-project.xyz/q/project:openbmc%252Fwebui-vue+status:open) 51 | - Join us in the 52 | [GUI design workgroup meeting](https://github.com/openbmc/openbmc/wiki/GUI-Design-work-group). 53 | 54 | ## Documentation 55 | 56 | The documentation for coding standards and components is located in the `docs` 57 | directory. It is created using the [VuePress](https://vuepress.vuejs.org/) 58 | static site generator. Information about how to write documentation can be found 59 | on the [VuePress website](https://vuepress.vuejs.org/). 60 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@vue/app', 5 | { 6 | targets: { esmodules: false }, 7 | polyfills: false, 8 | }, 9 | ], 10 | ], 11 | env: { 12 | test: { 13 | plugins: ['transform-require-context'], 14 | }, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /docs/.vuepress/components/colors/all.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 53 | 54 | 57 | -------------------------------------------------------------------------------- /docs/.vuepress/components/colors/blues.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /docs/.vuepress/components/colors/colors.scss: -------------------------------------------------------------------------------- 1 | .color-tile-container { 2 | display: grid; 3 | grid-gap: 1rem; 4 | grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); 5 | margin: 1rem 0; 6 | } 7 | 8 | .color-tile { 9 | display: block; 10 | height: 140px; 11 | 12 | &--border { 13 | border: 1px dashed #ccc; 14 | } 15 | } 16 | 17 | .color-tile-desc { 18 | margin: 0.5rem 0; 19 | } 20 | 21 | .color-tile-desc dt { 22 | display: inline; 23 | font-weight: bold; 24 | } 25 | 26 | .color-tile-desc dd { 27 | display: inline; 28 | margin: 0; 29 | } -------------------------------------------------------------------------------- /docs/.vuepress/components/colors/grays.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 70 | 71 | 74 | -------------------------------------------------------------------------------- /docs/.vuepress/components/colors/greens.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /docs/.vuepress/components/colors/reds.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /docs/.vuepress/components/colors/theme.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 74 | 75 | 78 | -------------------------------------------------------------------------------- /docs/.vuepress/components/colors/yellows.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 37 | 38 | < 41 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | base: "/webui-vue/", 5 | title: "OpenBMC Web UI Style Guide", 6 | description: 7 | "Guidance on code style and development for the OpenBMC browser-based UI", 8 | smoothScroll: true, 9 | themeConfig: { 10 | nav: [ 11 | { 12 | text: "Guide", 13 | link: "/guide/" 14 | }, 15 | { 16 | text: "Customization", 17 | link: "/customization/" 18 | }, 19 | { 20 | text: "Github", 21 | link: "https://github.com/openbmc/webui-vue" 22 | } 23 | ], 24 | sidebarDepth: 1, 25 | sidebar: { 26 | "/guide/": [ 27 | "", 28 | { 29 | title: "Coding Standards", 30 | children: [ 31 | ["/guide/coding-standards/", "JavaScript and SASS"], 32 | ["/guide/coding-standards/accessibility", "Accessibility"], 33 | ] 34 | }, 35 | { 36 | title: "Guidelines", 37 | children: [ 38 | "/guide/guidelines/colors", 39 | "/guide/guidelines/internationalization", 40 | "/guide/guidelines/motion", 41 | "/guide/guidelines/typography" 42 | ] 43 | }, 44 | "/guide/unit-testing/", 45 | { 46 | title: "Components", 47 | children: [ 48 | "/guide/components/", 49 | "/guide/components/alerts/", 50 | "/guide/components/buttons/", 51 | "/guide/components/file-upload/", 52 | "/guide/components/info-tooltip/", 53 | "/guide/components/page-section/", 54 | "/guide/components/page-title/", 55 | "/guide/components/status-icon/", 56 | "/guide/components/table/", 57 | "/guide/components/toasts/" 58 | ] 59 | }, 60 | { 61 | title: "Quick Start", 62 | children: [ 63 | "/guide/quickstart/forms", 64 | "/guide/quickstart/page-anatomy", 65 | "/guide/quickstart/store-anatomy" 66 | ] 67 | } 68 | ], 69 | "/customization/": ["", "theme", "build"] 70 | }, 71 | } 72 | }; -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | // VuePress Theme overrides 2 | // Change content to be left aligned 3 | .theme-default-content:not(.custom), 4 | .page-nav 5 | max-width 72ch 6 | margin 0 7 | padding 2rem 8 | 9 | // Overide default margins for p tags 10 | // VuePress is nesting li content in p tags 11 | // unless they are nested li elements creating 12 | // inconsistent margins between different lists 13 | li p 14 | margin-bottom .5rem 15 | margin-top .5rem 16 | -------------------------------------------------------------------------------- /docs/guide/coding-standards/readme.md: -------------------------------------------------------------------------------- 1 | # JavaScript and SASS 2 | 3 | This project uses the following libraries to determine the best practices and 4 | guidelines for both SCSS and JavaScript syntax. 5 | 6 | - [ESLint](https://eslint.org/) 7 | - [Prettier](https://prettier.io/) 8 | - [ESLint Plugin for Vue](https://eslint.vuejs.org/) 9 | 10 | The guidelines we are following are: 11 | 12 | - [Vue Recommended](https://eslint.vuejs.org/rules/#priority-c-recommended-minimizing-arbitrary-choices-and-cognitive-overhead-for-vue-js-3-x) 13 | - [ESLint Recommended](https://eslint.org/docs/rules/) 14 | - [Prettier](https://prettier.io/docs/en/options.html) 15 | 16 | The rules are applied in the following order: 17 | 18 | 1. Vue rules 19 | 1. ESLint recommended 20 | 1. Prettier 21 | 22 | ## Overrides 23 | 24 | Any overrides to a rule are located in the ESLint configuration file, 25 | `.eslintrc.js`, located in the root directory. 26 | 27 | ## Running the lint test 28 | 29 | To test all files for linting, run `npm run lint`. This command will evaluate 30 | the syntax of all files and update any code that that does not require manual 31 | review. 32 | 33 | The linting script runs when code is committed, during pre-commit, and when the 34 | CI tool runs after a push to Gerrit. There is a shell script named 35 | `format-code.sh` that installs node package dependencies and runs the test 36 | script in your CI. 37 | -------------------------------------------------------------------------------- /docs/guide/components/alerts/alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/alerts/alert.png -------------------------------------------------------------------------------- /docs/guide/components/alerts/index.md: -------------------------------------------------------------------------------- 1 | # Alerts 2 | 3 | An alert is an inline message that contains a short description that a user 4 | cannot manually dismiss. With exception to the error message on the login page, 5 | alerts are not triggered by user action. Success and error notifications based 6 | on user actions are created using a toast component. 7 | 8 | [Learn more about Bootstrap-vue alert options](https://bootstrap-vue.js.org/docs/components/alert) 9 | 10 | ![Alert examples](./alert.png) 11 | 12 | ```vue 13 | This is a warning message 14 | This is an error message 15 | This is an info message 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/guide/components/buttons/button-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/buttons/button-disabled.png -------------------------------------------------------------------------------- /docs/guide/components/buttons/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/buttons/button.png -------------------------------------------------------------------------------- /docs/guide/components/buttons/index.md: -------------------------------------------------------------------------------- 1 | # Buttons 2 | 3 | Buttons are used to perform an action. The main buttons in the application are 4 | the `primary` and `secondary` buttons. Buttons, like all Boostrap-vue components 5 | can be themed by setting the `variant` prop on the component to one of the 6 | [theme-color map keys](/guide/guidelines/colors). To create a button that looks 7 | like a link, set the variant value to `link`. 8 | 9 | [Learn more about Bootstrap-vue buttons](https://bootstrap-vue.js.org/docs/components/button) 10 | 11 | ### Icon only buttons 12 | 13 | Add `btn-icon-only` class to the button and add `title` attribute to get helper 14 | text on hover over the button. 15 | 16 | ### Enabled buttons 17 | 18 | ![Button examples](./button.png) 19 | 20 | ```vue 21 | // Enabled Buttons 22 | Primary 23 | 24 | 25 | Primary with icon 26 | 27 | Secondary 28 | Danger 29 | Link Button 30 | 31 | 32 | Link Button 33 | 34 | 35 | 36 | 37 | ``` 38 | 39 | ### Disabled buttons 40 | 41 | ![Disabled button examples](./button-disabled.png) 42 | 43 | ```vue 44 | // Disabled Buttons 45 | Primary 46 | 47 | 48 | Primary with icon 49 | 50 | Secondary 51 | Danger 52 | Link Button 53 | 54 | 55 | Link Button 56 | 57 | 58 | 59 | 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/guide/components/file-upload/formfile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/file-upload/formfile.png -------------------------------------------------------------------------------- /docs/guide/components/file-upload/readme.md: -------------------------------------------------------------------------------- 1 | # FormFile 2 | 3 | `FormFile` is a custom component wrapper around the Bootstrap-vue Form File 4 | component. The purpose of this component is to upload files to the BMC. 5 | 6 | To use this component: 7 | 8 | 1. Import it into the single file component (SFC) 9 | 2. Add the `` tag 10 | 3. Add the optional `id` , `disabled`, `accept` and `state` prop as required 11 | 12 | [Learn more about the Bootstrap-vue Form File component](https://bootstrap-vue.org/docs/components/form-file) 13 | 14 | ### Optional properties 15 | 16 | - `id`- Used to set the `id` attribute on the rendered content, and used as the 17 | base to generate any additional element IDs as needed 18 | - `disabled` - When set to `true`, disables the component's functionality and 19 | places it in a disabled state 20 | - `accept` - Set value to specify which file types to allow 21 | - `state` - Controls the validation state appearance of the component. `true` 22 | for valid, `false` for invalid, or `null` for no validation state 23 | 24 | ## Example of form file 25 | 26 | ```vue 27 | 28 | 29 | ``` 30 | 31 | ![Formfile example in firmware](./formfile.png) 32 | -------------------------------------------------------------------------------- /docs/guide/components/info-tooltip/index.md: -------------------------------------------------------------------------------- 1 | # InfoTooltip 2 | 3 | The `InfoTooltip` is a custom component that uses a Bootstrap-vue tooltip with 4 | an info icon. This custom component requires a title property containing the 5 | tooltip text to display to the user. 6 | 7 | [Read more about the Bootstrap-vue tooltip component](https://bootstrap-vue.org/docs/components/tooltip) 8 | 9 | ## Example 10 | 11 | ```vue 12 | 13 | ``` 14 | 15 | ![Tooltip example](./info-tooltip.png) 16 | -------------------------------------------------------------------------------- /docs/guide/components/info-tooltip/info-tooltip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/info-tooltip/info-tooltip.png -------------------------------------------------------------------------------- /docs/guide/components/page-section/index.md: -------------------------------------------------------------------------------- 1 | # Page section 2 | 3 | The `` component will render semantic HTML. By adding a 4 | `:section-title` prop to the `` component, the localized text 5 | string will be rendered in an `h2` header element. 6 | 7 | ```vue 8 | // Example: `src/views/AccessControl/Ldap/Ldap.vue` 9 | 10 | ``` 11 | 12 | [View the page section component source code](https://github.com/openbmc/webui-vue/blob/master/src/components/Global/PageSection.vue). 13 | -------------------------------------------------------------------------------- /docs/guide/components/page-title/index.md: -------------------------------------------------------------------------------- 1 | # Page title 2 | 3 | The `` component will automatically render the page title that 4 | corresponds with the title property set in the route record's meta field in 5 | `src/router/routes.js`. 6 | 7 | ```js 8 | // src/router/routes.js 9 | { 10 | path: '', 11 | name: 'login', 12 | component: Login, 13 | meta: { 14 | title: i18n.global.t('appPageTitle.login'), 15 | }, 16 | }, 17 | ``` 18 | 19 | Optional page descriptions can be included by using the description prop 20 | `:description` prop and passing in the i18n localized text string. Translations 21 | are found in the `src/locales` folder. 22 | 23 | ```vue 24 | // Example: `src/views/AccessControl/Ldap/Ldap.vue` 25 | 26 | ``` 27 | 28 | [View the page title component source code](https://github.com/openbmc/webui-vue/blob/master/src/components/Global/PageTitle.vue). 29 | -------------------------------------------------------------------------------- /docs/guide/components/readme.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Vue components are the building blocks of the OpenBMC Web UI. It uses both 4 | Boostrap-Vue components, as well as custom components. Using these components 5 | assures consistency throughout the application. They also improve the developer 6 | experience and increase efficiency. Review the existing components before using 7 | HTML markup. If the feature you're working on includes a new layout pattern, 8 | rather than adding raw markup to the page, consider creating a component that 9 | other sections of the application can use as well. 10 | 11 | [Learn more about Vue components](https://vuejs.org/v2/guide/components.html) 12 | -------------------------------------------------------------------------------- /docs/guide/components/status-icon/appHeaderWithStatusIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/status-icon/appHeaderWithStatusIcon.png -------------------------------------------------------------------------------- /docs/guide/components/status-icon/danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/status-icon/danger.png -------------------------------------------------------------------------------- /docs/guide/components/status-icon/eventLogsWithSatusIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/status-icon/eventLogsWithSatusIcon.png -------------------------------------------------------------------------------- /docs/guide/components/status-icon/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/status-icon/info.png -------------------------------------------------------------------------------- /docs/guide/components/status-icon/readme.md: -------------------------------------------------------------------------------- 1 | # StatusIcon 2 | 3 | The StatusIcon component is used to add an icon that represents one of the 4 | following statuses: 5 | 6 | - success 7 | - info 8 | - warning 9 | - danger 10 | 11 | To use this component: 12 | 13 | 1. Import it into the single file component (SFC) 14 | 2. Add the `` tag 15 | 3. Add the optional status prop and set the value to one of the statuses 16 | 4. Add the translated text to associate with the icon 17 | 18 | ```vue 19 | import StatusIcon from '@/components/Global/StatusIcon' 20 | ``` 21 | 22 | ## Status icon with default status 23 | 24 | ```vue 25 | 26 | ``` 27 | 28 | ![StatusIcon default icon example](./secondary.png) 29 | 30 | ## Status icon with success status 31 | 32 | ```vue 33 | 34 | ``` 35 | 36 | ![StatusIcon success icon example](./success.png) 37 | 38 | ## Status icon with info status 39 | 40 | ```vue 41 | 42 | ``` 43 | 44 | ![StatusIcon info icon example](./info.png) 45 | 46 | ## Status icon with warning status 47 | 48 | ```vue 49 | 50 | ``` 51 | 52 | ![StatusIcon warning icon example](./warning.png) 53 | 54 | ## Status icon with danger status 55 | 56 | ```vue 57 | 58 | ``` 59 | 60 | ![StatusIcon danger icon example](./danger.png) 61 | 62 | ### Example of AppHeader with status icon 63 | 64 | ![AppHeader with status icon example](./appHeaderWithStatusIcon.png) 65 | 66 | ### Example of Event logs with status icon 67 | 68 | ![EventLogs with status icon example](./eventLogsWithSatusIcon.png) 69 | -------------------------------------------------------------------------------- /docs/guide/components/status-icon/secondary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/status-icon/secondary.png -------------------------------------------------------------------------------- /docs/guide/components/status-icon/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/status-icon/success.png -------------------------------------------------------------------------------- /docs/guide/components/status-icon/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/status-icon/warning.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-batch-action-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-batch-action-active.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-batch-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-batch-action.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-empty.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-expand-row.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-expand-row.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-filter-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-filter-active.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-filter.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-pagination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-pagination.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-row-actions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-row-actions.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-search-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-search-active.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-search-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-search-empty.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-search.png -------------------------------------------------------------------------------- /docs/guide/components/table/table-sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table-sort.png -------------------------------------------------------------------------------- /docs/guide/components/table/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/table/table.png -------------------------------------------------------------------------------- /docs/guide/components/toasts/index.md: -------------------------------------------------------------------------------- 1 | # Toasts 2 | 3 | Use a toast message to indicate the status of a user action. For example, a user 4 | saves a form successfully, a toast message with the `success` variant is 5 | displayed. If the user action was not successful, a toast message with the 6 | `danger` variant is displayed. 7 | 8 | There are different transitions for the toast messages. The `success` toast 9 | message will auto-hide after 10 seconds. The user must manually dismiss the 10 | `informational`, `warning`, and `error` toast messages. The `BVToastMixin` 11 | provides a simple API that generates a toast message that meets the transition 12 | guidelines. 13 | 14 | Toast message examples 15 | 16 | ```js{5} 17 | // Sample method from Reboot BMC page 18 | rebootBmc() { 19 | this.$store 20 | .dispatch('controls/rebootBmc') 21 | .then(message => this.successToast(message)) 22 | .catch(({ message }) => this.errorToast(message)); 23 | } 24 | 25 | // Methods used in this example 26 | methods: { 27 | makeSuccessToast() { 28 | this.successToast('This is a success toast and will be dismissed after 10 seconds.'); 29 | }, 30 | makeErrorToast() { 31 | this.errorToast('This is an error toast and must be dismissed by the user.'); 32 | }, 33 | makeWarningToast() { 34 | this.warningToast('This is a warning toast and must be dismissed by the user.'); 35 | }, 36 | makeInfoToast() { 37 | this.infoToast('This is an info toast and must be dismissed by the user.'); 38 | }, 39 | } 40 | ``` 41 | 42 | ## Additional options 43 | 44 | The first argument for each method will be the toast body content. It accepts a 45 | string value or an array of strings for toasts needing to display multiple lines 46 | of content. 47 | 48 | The BVToastMixin also accepts additional options as a second argument. Pass an 49 | object with a `title` property to change the default toast title. Include a 50 | `refreshAction` property, set to true, to include a link that refreshes the 51 | application. Include a `timestamp` property, set to true, to include a timestamp 52 | in the toast. 53 | 54 | Toast message options example 56 | 57 | ```js 58 | 59 | methods: { 60 | makeInfoToast() { 61 | this.infoToast([ 62 | 'This is a toast with multi-lined body content.', 63 | 'Just pass an array of strings!' 64 | ], { 65 | title: 'This is an example', 66 | refreshAction: true, 67 | timestamp: true 68 | }) 69 | } 70 | } 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/guide/components/toasts/toast-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/toasts/toast-options.png -------------------------------------------------------------------------------- /docs/guide/components/toasts/toast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/docs/guide/components/toasts/toast.png -------------------------------------------------------------------------------- /docs/guide/guidelines/colors.md: -------------------------------------------------------------------------------- 1 | # Colors 2 | 3 | This color palette has been agreed upon by the OpenBMC community and differs 4 | from the Bootstrap color patterns. The OpenBMC palette includes custom hex 5 | values, along with additional blue, green, red, and yellow color variables used 6 | as accent colors for components. The `.scss` component files use these accent 7 | colors to override default styles set by the Bootstrap library. 8 | 9 | - [Learn more about theme customization](/customization/theme/) 10 | - [Open an issue in the OpenBMC webui-vue repo](https://github.com/openbmc/webui-vue/issues/new/choose) 11 | to request a change 12 | - [Learn more about Bootstrap colors](https://getbootstrap.com/docs/4.4/getting-started/theming/#theme-colors) 13 | 14 | ## Grays 15 | 16 | 17 | 18 | ## Blues 19 | 20 | 21 | 22 | ## Greens 23 | 24 | 25 | 26 | ## Reds 27 | 28 | 29 | 30 | ## Yellows 31 | 32 | 33 | 34 | ## All Colors 35 | 36 | `All Colors` is the term Bootstrap uses to describe the colors that make up the 37 | `colors` map. These colors and the Grays color variables define the 38 | `theme-colors` map colors. 39 | 40 | [Learn more about the Bootstrap color maps](https://getbootstrap.com/docs/4.0/getting-started/theming/#all-colors). 41 | 42 | 43 | ## Theme Colors 44 | 45 | The theme colors are keys in the `theme-colors` map. Bootstrap-Vue has a variant 46 | prop that accepts any of the `theme-colors` keys to set the theme of a 47 | component. 48 | 49 | [Learn more about the Bootstrap theme-colors maps](https://getbootstrap.com/docs/4.0/getting-started/theming/#theme-colors). 50 | 51 | 52 | -------------------------------------------------------------------------------- /docs/guide/guidelines/internationalization.md: -------------------------------------------------------------------------------- 1 | # Internationalization 2 | 3 | The OpenBMC Web UI implements internationalization and separates the language- 4 | specific parts of the interface from the rest of the code, so they can be easily 5 | replaced. The OpenBMC Web UI uses the following library for 6 | internationalization: 7 | 8 | - [Vue I18n](https://kazupon.github.io/vue-i18n/introduction.html) 9 | 10 | ## Key naming convention 11 | 12 | The OpenBMC Web UI follows the following key naming conventions: 13 | 14 | - Page specific labels should be nested in an object with a key prefixed `page` 15 | followed by the page title. Formatting in this manner provides a systematic 16 | structure and improves readability of the language file. 17 | - e.g. `pageLocalUserManagement.editUser` 18 | - Any 'major' child components should be nested inside page specific objects 19 | (ex. table, modal) 20 | - e.g. `pageEventLogs.table.eventType` 21 | - Avoid any complex linked locale messages. 22 | - Alphabetize object keys. This helps in locating the keys. 23 | - We use the `$t()` function in markup and `this.$t` in scripts (which Vue I18n 24 | provides to our components) for outputting translation messages. 25 | 26 | ## Using the Vue I18n plugin 27 | 28 | - A new `src/i18n.js` file is added and it registers Vue I18n as a plugin to our 29 | Vue instance via the `Vue.use()` function. 30 | - The CLI creates a `src/locales/en-US.json` file, which contains our default 31 | translation messages. 32 | - The keys are placed in the `src/locales/en-US.json` file and then the `$t()` 33 | function is used to output the translation messages. 34 | - After adding or removing calls to `$t` please run this to ensure consistent 35 | English translation (note: using variable expansion in key names is not 36 | handled automatically, you need to manually check them): 37 | 38 | ```bash 39 | node node_modules/vue-i18n-extract/bin/vue-i18n-extract.js -v 'src/**/*.?(js|vue)' -l 'src/locales/en-US.json' 40 | ``` 41 | 42 | - If you're working on updating a translation for another language (e.g. 43 | Russian), run this to see the omissions (as well as cruft) and add the 44 | necessary keys automatically: 45 | 46 | ```bash 47 | node node_modules/vue-i18n-extract/bin/vue-i18n-extract.js -v 'src/**/*.?(js|vue)' -l 'src/locales/ru-RU.json' -a 48 | ``` 49 | 50 | ```json 51 | "pageDumps": { 52 | "dumpsAvailableOnBmc": "Dumps available on BMC" 53 | } 54 | ``` 55 | 56 | ```Vue 57 | 58 | ``` 59 | 60 | - Vue I18n’s `$tc()` function can help with displaying plurals. 61 | [Learn more about pluralization.](https://kazupon.github.io/vue-i18n/guide/pluralization.html) 62 | 63 | ```json 64 | "modal": { 65 | "deleteDump": "Delete dump | Delete dumps" 66 | } 67 | ``` 68 | 69 | ```JS 70 | this.$bvModal 71 | .msgBoxConfirm(this.$tc('pageDumps.modal.deleteDumpConfirmation'), { 72 | title: this.$tc('pageDumps.modal.deleteDump'), 73 | okTitle: this.$tc('pageDumps.modal.deleteDump'), 74 | cancelTitle: this.$t('global.action.cancel'), 75 | autoFocusButton: 'ok', 76 | }) 77 | ``` 78 | -------------------------------------------------------------------------------- /docs/guide/guidelines/motion.md: -------------------------------------------------------------------------------- 1 | # Motion 2 | 3 | The motion guidelines are based on Carbon Design System guidelines. These 4 | guidelines avoid easing curves that are unnatural, distracting, or purely 5 | decorative. 6 | [The documentation below is attributed to Carbon's animation documentation](https://www.carbondesignsystem.com/guidelines/motion/basics/). 7 | 8 | ## Easing 9 | 10 | ### Productive motion 11 | 12 | Productive motion creates a sense of efficiency and responsiveness, while 13 | remaining subtle and out of the way. Productive motion is appropriate for 14 | moments when the user needs to focus on completing tasks. 15 | 16 | ### Expressive motion 17 | 18 | Expressive motion delivers enthusiastic, vibrant, and highly visible movement. 19 | Use expressive motion for significant moments such as opening a new page, 20 | clicking the primary action button, or when the movement itself conveys a 21 | meaning. System alerts and the appearance of notification boxes are great cases 22 | for expressive motion. 23 | 24 | ### Easing tokens 25 | 26 | ``` 27 | $standard-easing--productive: cubic-bezier(0.2, 0, 0.38, 0.9); 28 | $standard-easing--expressive: cubic-bezier(0.4, 0.14, 0.3, 1); 29 | $entrance-easing--productive: cubic-bezier(0, 0, 0.38, 0.9); 30 | $entrance-easing--expressive: cubic-bezier(0, 0, 0.3, 1); 31 | $exit-easing--productive: cubic-bezier(0.2, 0, 1, 0.9); 32 | $exit-easing--expressive: cubic-bezier(0.4, 0.14, 1, 1); 33 | ``` 34 | 35 | ## Duration 36 | 37 | Duration is calculated based on the style and size of the motion. Among the two 38 | motion styles, productive motion is significantly faster than expressive motion. 39 | Motion’s duration should be dynamic based on the size of the animation; the 40 | larger the change in distance (traveled) or size (scaling) of the element, the 41 | longer the animation takes. 42 | 43 | ### Duration tokens 44 | 45 | ``` 46 | $duration--fast-01: 70ms; //Micro-interactions such as button and toggle 47 | $duration--fast-02: 110ms; //Micro-interactions such as fade 48 | $duration--moderate-01: 150ms; //Micro-interactions, small expansion, short distance movements 49 | $duration--moderate-02: 240ms; //Expansion, system communication, toast 50 | $duration--slow-01: 400ms; //Large expansion, important system notifications 51 | $duration--slow-02: 700ms; //Background dimming 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/guide/guidelines/typography.md: -------------------------------------------------------------------------------- 1 | # Typography 2 | -------------------------------------------------------------------------------- /docs/guide/quickstart/page-anatomy.md: -------------------------------------------------------------------------------- 1 | # Page Anatomy 2 | 3 | Single-file components (SFC) consist of a `template`, `script` and `style` 4 | block. 5 | 6 | ## Template block 7 | 8 | When creating a new page, each template consists of at least 3 components: 9 | 10 | - `` 11 | - `` 12 | - `` 13 | 14 | Learn more about the [page title](/guide/components/page-title)and 15 | [page section](/guide/components/page-section) components. 16 | 17 | ```vue 18 | 26 | ``` 27 | 28 | ## Scripts block 29 | 30 | In the scripts section, be sure to import the `PageTitle` and `PageSection` 31 | components and declare them in the `components` property. 32 | 33 | Importing `BContainer` in the [scripts block](#scripts-block) is not required as 34 | it is already registered globally. 35 | 36 | ```vue 37 | 45 | ``` 46 | 47 | ## Style block 48 | 49 | Add the `scoped` attribute to the style block to keep the styles isolated to the 50 | SFC. While the `scoped` attribute is optional, it is preferred and global 51 | changes should be done in global style sheets. 52 | 53 | ```vue 54 | 55 | ``` 56 | 57 | ## Complete single-file component (SFC) 58 | 59 | The final SFC will look like this. 60 | 61 | ```vue 62 | 70 | 78 | 83 | ``` 84 | -------------------------------------------------------------------------------- /docs/guide/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebarDepth: 0 3 | --- 4 | 5 | # Getting Started 6 | 7 | This guide outlines the architecture and technologies used to build the OpenBMC 8 | Web UI. This guide exists to serve the following goals: 9 | 10 | 1. Improve contributor efficiency while maintaining the quality and consistency 11 | of the user interface 12 | 1. Act as a collection of community agreed-upon standards 13 | 14 | ## Coding Standards 15 | 16 | Having an understanding of semantic HTML, CSS, and JavaScript is the critical 17 | knowledge required to contribute to this project. The frameworks and libraries 18 | used to build this UI include: 19 | 20 | - [Vue](https://vuejs.org/) 21 | - [Vuex](https://vuex.vuejs.org/) 22 | - [Vue Router](https://router.vuejs.org/) 23 | - [Axios](https://github.com/axios/axios) 24 | - [Bootstrap-vue](https://bootstrap-vue.js.org/) 25 | - [Vuelidate](https://vuelidate.js.org/) 26 | - [Vue I18n](https://kazupon.github.io/vue-i18n/) 27 | 28 | ::: tip Acquiring an understanding of these technologies will also be necessary. 29 | ::: 30 | 31 | You will find more information about the standards and best practices in the 32 | [Coding Standards section of this guide](/guide/coding-standards/). 33 | 34 | ## Guidelines 35 | 36 | The [guidelines section](/guide/guidelines/colors.md) contains the OpenBMC 37 | community agreed-upon decisions on color, motion, and typography within the 38 | application. How to theme the application to meet company brand guidelines is 39 | documented in [Customization](/customization/). 40 | 41 | ## Components 42 | 43 | The [components section](/guide/components/) is a guide to using both custom Vue 44 | components and components from the Bootstrap-Vue library. 45 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: openbmc-logo.svg 4 | heroText: Style Guide and Coding Conventions 5 | actionText: Getting Started 6 | actionLink: /guide/ 7 | --- 8 | -------------------------------------------------------------------------------- /format-code.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run GUI Linting and Formatting as part of the CI Build process 3 | # 4 | # This is a short term solution. The long term solution to will be to 5 | # add much of this to the build script. 6 | # 7 | 8 | set -e 9 | 10 | # When called from openbmc-build-scripts, the `pwd` could be anywhere, but 11 | # the root of the repo is passed in the first argument. Switch to the repo 12 | # root so npm/git run in the right place. 13 | if [ -n "$1" ]; then 14 | cd "$1" 15 | fi 16 | 17 | npm install 18 | npm run lint 19 | 20 | # CI might be running a different version of NPM than yocto, and we don't 21 | # want to trigger a formatting failure if package-lock.json has changed 22 | # Therefore, revert it back to what it was previously. 23 | git checkout package-lock.json 24 | git --no-pager diff --exit-code 25 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest', 3 | transformIgnorePatterns: ['/node_modules/(?!@carbon)'], 4 | }; 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'postcss-modules': {}, 3 | plugins: { 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | OpenBMC Web UI 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /run-ci: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run GUI Unit Tests as part of the CI Build process# 3 | 4 | set -e 5 | 6 | # When called from openbmc-build-scripts, the `pwd` could be anywhere, but 7 | # the root of the repo is passed in the first argument. Switch to the repo 8 | # root so npm/git run in the right place. 9 | if [ -n "$1" ]; then 10 | cd "$1" 11 | fi 12 | 13 | npm ci 14 | npm run test:unit 15 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 36 | 37 | 40 | -------------------------------------------------------------------------------- /src/assets/styles/_obmc-custom.scss: -------------------------------------------------------------------------------- 1 | // Vendor styles 2 | @import "./bootstrap"; 3 | @import "~bootstrap-vue/src/index"; 4 | 5 | // Custom BMC styles 6 | @import "./bmc/custom"; 7 | -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_alert.scss: -------------------------------------------------------------------------------- 1 | .alert { 2 | display: flex; 3 | padding: $spacer; 4 | border-width: 0 0 0 3px; 5 | color: gray("800"); 6 | margin-bottom: $spacer; 7 | 8 | &.small { 9 | padding: $spacer / 2; 10 | font-size: 1rem; 11 | } 12 | 13 | .close { 14 | font-weight: 300; 15 | opacity: 1; 16 | } 17 | 18 | .alert-icon { 19 | display: inline-flex; 20 | align-items: flex-start; 21 | margin-right: $spacer; 22 | margin-bottom: $spacer; 23 | 24 | @include media-breakpoint-up(sm) { 25 | margin-bottom: 0; 26 | } 27 | } 28 | 29 | .alert-content { 30 | flex: 1 1 auto; 31 | } 32 | 33 | .alert-title { 34 | margin-bottom: $spacer / 2; 35 | } 36 | 37 | .alert-msg { 38 | p + p { 39 | margin-bottom: $spacer; 40 | } 41 | 42 | p:last-of-type { 43 | margin-bottom: 0; 44 | } 45 | } 46 | 47 | &.alert-info { 48 | border-left-color: theme-color("info"); 49 | background-color: theme-color-light("info"); 50 | fill: theme-color("info"); 51 | } 52 | 53 | &.alert-success { 54 | border-left-color: theme-color("success"); 55 | background-color: theme-color-light("success"); 56 | fill: theme-color("success"); 57 | } 58 | 59 | &.alert-danger { 60 | border-left-color: theme-color("danger"); 61 | background-color: theme-color-light("danger"); 62 | fill: theme-color("danger"); 63 | } 64 | 65 | &.alert-warning { 66 | border-left-color: theme-color("warning"); 67 | background-color: theme-color-light("warning"); 68 | fill: theme-color("warning"); 69 | } 70 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_badge.scss: -------------------------------------------------------------------------------- 1 | .badge-pill { 2 | // Need to explicitly set border-radius 3 | // for pill variant because global $enable-rounded 4 | // Bootstrap setting removes rounded pill style 5 | border-radius: 10rem; 6 | fill: currentColor; 7 | font-weight: 400; 8 | line-height: 1.5; 9 | display: inline-flex; 10 | .close { 11 | font-size: 1em; 12 | margin-left: $spacer/2; 13 | font-weight: inherit; 14 | color: inherit; 15 | } 16 | } 17 | 18 | .badge-primary { 19 | background-color: theme-color-light("info"); 20 | color: theme-color("info"); 21 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_base.scss: -------------------------------------------------------------------------------- 1 | dt, 2 | legend, 3 | label { 4 | color: gray("800"); 5 | font-size: 14px; 6 | font-weight: 400; 7 | line-height: 1.4285; 8 | } 9 | 10 | h1, 11 | .h1 { 12 | font-size: 2.625rem; 13 | font-weight: 300; 14 | line-height: 1.238; 15 | } 16 | 17 | h2, 18 | .h2 { 19 | font-size: 2.25rem; 20 | font-weight: 300; 21 | line-height: 1.3333; 22 | } 23 | 24 | h3, 25 | .h3 { 26 | font-size: 1.75rem; 27 | font-weight: 400; 28 | line-height: 1.2857; 29 | } 30 | 31 | h4, 32 | .h4 { 33 | font-size: 1.25rem; 34 | font-weight: 400; 35 | line-height: 1.3; 36 | } 37 | 38 | h5, 39 | .h5 { 40 | font-size: 1rem; 41 | font-weight: 500; 42 | line-height: 1.375; 43 | } 44 | 45 | h6, 46 | .h6 { 47 | font-size: 0.875rem; 48 | font-weight: 500; 49 | line-height: 1.2857; 50 | } 51 | -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_bootstrap-grid.scss: -------------------------------------------------------------------------------- 1 | .container-xl { 2 | // Fluid layout container class sets 100% 3 | // width until xl breakpoint. Once a max-width 4 | // is set, setting the left margin to 0 is needed 5 | // so the content doesn't center align 6 | // https://bootstrap-vue.org/docs/components/layout#fluid-width-container 7 | margin-left: 0; 8 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_buttons.scss: -------------------------------------------------------------------------------- 1 | @import 'bootstrap/dist/css/bootstrap.css'; 2 | 3 | .btn { 4 | padding-top: $spacer / 2; 5 | padding-right: $spacer; 6 | padding-bottom: $spacer / 2; 7 | padding-left: $spacer; 8 | display: inline-flex; 9 | align-items: center; 10 | justify-content: space-around; 11 | svg { 12 | margin-right: $spacer / 4; 13 | } 14 | &:disabled { 15 | color: gray("600"); 16 | fill: currentColor; 17 | box-shadow: none !important; 18 | &:not(.btn-link) { 19 | border-color: gray("400"); 20 | background-color: gray("400"); 21 | } 22 | } 23 | } 24 | 25 | .btn-primary { 26 | fill: currentColor; 27 | &:focus, 28 | &:not(:disabled):not(.disabled):active:focus { 29 | border-color: $white; 30 | box-shadow: inset 0 0 0 3px theme-color('primary'), inset 0 0 0 5px $white; 31 | } 32 | } 33 | 34 | .btn-secondary { 35 | fill: currentColor; 36 | &:focus, 37 | &:not(:disabled):not(.disabled):active:focus { 38 | border-color: $white; 39 | box-shadow: inset 0 0 0 3px theme-color('secondary'), inset 0 0 0 5px $white; 40 | } 41 | } 42 | 43 | // Global style for all button link 44 | .btn-link { 45 | font-weight: $headings-font-weight; 46 | fill: theme-color("primary"); 47 | text-decoration: none !important; 48 | &:hover { 49 | background-color: gray("200"); 50 | color: theme-color("primary"); 51 | } 52 | &:active { 53 | background-color: gray("300"); 54 | } 55 | &:focus { 56 | box-shadow: inset 0 0 0 2px theme-color("primary"); 57 | color: theme-color("primary"); 58 | outline: none; 59 | } 60 | &:disabled { 61 | box-shadow: $btn-focus-box-shadow; 62 | } 63 | } 64 | 65 | // Icon only buttons 66 | .btn-icon-only svg { 67 | margin-right: 0; 68 | } 69 | 70 | // Datepicker, clear search and Password toggle buttons 71 | .input-action-btn, 72 | .btn-datepicker { 73 | position: absolute; 74 | right: 0; 75 | top: 0; 76 | z-index: $zindex-dropdown + 1; 77 | } 78 | 79 | // Contain input buttons within input 80 | .btn-datepicker .dropdown-toggle, 81 | .input-action-btn { 82 | padding: 7px; 83 | margin: 1px; 84 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_calendar.scss: -------------------------------------------------------------------------------- 1 | .b-calendar-nav { 2 | .btn { 3 | &:hover { 4 | background: none; 5 | color: theme-color("dark"); 6 | } 7 | } 8 | } 9 | 10 | .b-calendar-grid .btn { 11 | display: inline-block; 12 | } 13 | 14 | // Date picker focus 15 | .b-calendar .b-calendar-grid { 16 | padding: 6px 12px; 17 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_card.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | .bg-success { 3 | background-color: theme-color-light('success')!important; 4 | } 5 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_dropdown.scss: -------------------------------------------------------------------------------- 1 | // Make calendar visible over the table 2 | .dropdown-menu { 3 | z-index: $zindex-dropdown + 1; 4 | padding: 0; 5 | } 6 | .dropdown-item { 7 | padding-left: $spacer/4; 8 | margin-top: -1 * $spacer/4; 9 | } 10 | .b-dropdown-form { 11 | padding: $spacer/2; 12 | .form-group { 13 | margin-bottom: $spacer/2; 14 | } 15 | } 16 | // Table filter dropdown clear button style 17 | .table-filter { 18 | .dropdown-item { 19 | &:hover { 20 | background-color: gray("200"); 21 | } 22 | &:active { 23 | background-color: gray("300"); 24 | } 25 | &:focus { 26 | outline: none; 27 | background-color: transparent; 28 | box-shadow: inset 0 0 0 2px theme-color("primary"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_index.scss: -------------------------------------------------------------------------------- 1 | // OpenBMC Global Style Overrides of out of the box 2 | // Bootstrap styles 3 | @import "./alert"; 4 | @import "./badge"; 5 | @import "./base"; 6 | @import "./bootstrap-grid"; 7 | @import "./buttons"; 8 | @import "./calendar"; 9 | @import "./card"; 10 | @import "./dropdown"; 11 | @import "./forms"; 12 | @import "./kvm"; 13 | @import "./modal"; 14 | @import "./pagination"; 15 | @import "./section-divider"; 16 | @import "./sol"; 17 | @import "./tables"; 18 | @import "./toasts"; -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_kvm.scss: -------------------------------------------------------------------------------- 1 | #terminal-kvm { 2 | height: calc(100vh - 300px); 3 | display: flex; 4 | &.full-window { 5 | height: calc(100vh - 80px); 6 | } 7 | div:nth-child(1) { 8 | background: transparent !important; 9 | display: block !important; 10 | overflow: hidden !important; 11 | } 12 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_modal.scss: -------------------------------------------------------------------------------- 1 | .modal-header { 2 | .close { 3 | font-weight: normal; 4 | color: theme-color("dark"); 5 | opacity: 1; 6 | } 7 | .modal-title { 8 | font-size: 1.25rem; 9 | font-weight: normal; 10 | line-height: 1.3; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_pagination.scss: -------------------------------------------------------------------------------- 1 | .table-pagination-select { 2 | display: flex; 3 | flex-direction: row-reverse; 4 | justify-content: flex-end; 5 | select { 6 | width: fit-content; 7 | } 8 | label { 9 | margin-left: $spacer; 10 | line-height: $spacer * 2; 11 | } 12 | } 13 | 14 | .b-pagination { 15 | @include media-breakpoint-up(sm) { 16 | justify-content: flex-end; 17 | } 18 | .page-item.active button { 19 | color: theme-color("dark"); 20 | background-color: color("white"); 21 | border-color: $border-color; 22 | box-shadow: inset 0px -3px theme-color("primary"); 23 | } 24 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_section-divider.scss: -------------------------------------------------------------------------------- 1 | .section-divider { 2 | border-bottom: 1px solid gray('400'); 3 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_sol.scss: -------------------------------------------------------------------------------- 1 | #terminal .xterm .xterm-viewport { 2 | overflow: auto; 3 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/custom/_toasts.scss: -------------------------------------------------------------------------------- 1 | .b-toaster { 2 | top: 75px!important; // make sure toasts do not hide top header 3 | } 4 | 5 | // Toast component and status icon style 6 | .toast { 7 | padding: $spacer/2 $spacer/2 $spacer/2 $spacer+2; 8 | border-width: 0 0 0 3px; 9 | box-shadow: $box-shadow; 10 | .close { 11 | font-weight: 300; 12 | opacity: 1; 13 | } 14 | } 15 | 16 | .toast-header { 17 | display: flex; 18 | align-items: flex-start; 19 | background-color: inherit!important; //override specificity 20 | border: none; 21 | color: theme-color("dark")!important; //override specificity 22 | padding-bottom: 0; 23 | } 24 | 25 | .toast-icon { 26 | display: flex; 27 | margin-right: 1rem; 28 | 29 | svg { 30 | margin-left: -2.5rem; 31 | } 32 | 33 | + .close { 34 | line-height: .9; 35 | } 36 | } 37 | 38 | .toast-body { 39 | color: theme-color("dark"); 40 | padding-top: 0; 41 | } 42 | 43 | .b-toast-success .toast { 44 | border-left-color: theme-color("success")!important; 45 | background-color: theme-color-light("success")!important; 46 | } 47 | 48 | .b-toast-info .toast { 49 | border-left-color: theme-color("info")!important; 50 | background-color: theme-color-light("info")!important; 51 | } 52 | 53 | .b-toast-danger .toast { 54 | border-left-color: theme-color("danger")!important; 55 | background-color: theme-color-light("danger")!important; 56 | } 57 | 58 | .b-toast-warning .toast { 59 | border-left-color: theme-color("warning")!important; 60 | background-color: theme-color-light("warning")!important; 61 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/helpers/_colors.scss: -------------------------------------------------------------------------------- 1 | // Sass Color Variables 2 | $black: #000; 3 | $white: #fff; 4 | 5 | $blue-500: #2d60e5; 6 | $green-500: #0a7d06; 7 | $red-500: #da1416; 8 | $yellow-500: #efc100; 9 | 10 | $gray-100: #f4f4f4; 11 | $gray-200: #e6e6e6; 12 | $gray-300: #d8d8d8; 13 | $gray-400: #cccccc; 14 | $gray-500: #b3b3b3; 15 | $gray-600: #999999; 16 | $gray-700: #666666; 17 | $gray-800: #3f3f3f; 18 | $gray-900: #161616; 19 | 20 | // Sass Base Color Variables 21 | $blue: $blue-500; 22 | $green: $green-500; 23 | $red: $red-500; 24 | $yellow: $yellow-500; 25 | 26 | // Sass Theme Color Variables 27 | // Can be used as variants 28 | $danger: $red; 29 | $dark: $gray-900; 30 | $info: $blue; 31 | $light: $gray-100; 32 | $primary: $blue; 33 | $secondary: $gray-800; 34 | $success: $green; 35 | $warning: $yellow; 36 | 37 | $loading-color: $primary; 38 | $navbar-color: $dark; 39 | -------------------------------------------------------------------------------- /src/assets/styles/bmc/helpers/_functions.scss: -------------------------------------------------------------------------------- 1 | // This function is usually used to get a lighter 2 | // theme variant color to use as a background color 3 | @function theme-color-light($variant) { 4 | @return theme-color-level($variant, -11.3); 5 | } 6 | 7 | @function theme-color-dark($variant) { 8 | @return theme-color-level($variant, 2); 9 | } -------------------------------------------------------------------------------- /src/assets/styles/bmc/helpers/_index.scss: -------------------------------------------------------------------------------- 1 | @import "./colors"; 2 | @import "./motion"; 3 | @import "./variables"; 4 | @import "./functions"; -------------------------------------------------------------------------------- /src/assets/styles/bmc/helpers/_motion.scss: -------------------------------------------------------------------------------- 1 | $duration--fast-01: 70ms; //Micro-interactions such as button and toggle 2 | $duration--fast-02: 110ms; //Micro-interactions such as fade 3 | $duration--moderate-01: 150ms; //Micro-interactions, small expansion, short distance movements 4 | $duration--moderate-02: 240ms; //Expansion, system communication, toast 5 | $duration--slow-01: 400ms; //Large expansion, important system notifications 6 | $duration--slow-02: 700ms; //Background dimming 7 | 8 | // https://www.carbondesignsystem.com/guidelines/motion/basics/#easing 9 | $standard-easing--productive: cubic-bezier(0.2, 0, 0.38, 0.9); 10 | $standard-easing--expressive: cubic-bezier(0.4, 0.14, 0.3, 1); 11 | $entrance-easing--productive: cubic-bezier(0, 0, 0.38, 0.9); 12 | $entrance-easing--expressive: cubic-bezier(0, 0, 0.3, 1); 13 | $exit-easing--productive: cubic-bezier(0.2, 0, 1, 0.9); 14 | $exit-easing--expressive: cubic-bezier(0.4, 0.14, 1, 1); -------------------------------------------------------------------------------- /src/assets/styles/bmc/helpers/_variables.scss: -------------------------------------------------------------------------------- 1 | // Override Bootstrap Variables - node_modules/Bootstrap/scss/_variables.scss 2 | $enable-rounded: false; 3 | $enable-validation-icons: false; 4 | $transition-base: all $duration--moderate-02 $standard-easing--productive; 5 | $transition-fade: opacity $duration--moderate-01 $standard-easing--productive; 6 | $transition-collapse: height $duration--slow-01 $standard-easing--expressive; 7 | 8 | // OpenBMC Custom Variables 9 | $responsive-layout-bp: lg; 10 | $header-height: 48px; 11 | $navigation-width: 300px; 12 | $form-label-font-size: .875rem; 13 | $form-line-height: 1.25rem; 14 | $box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.3); 15 | $focus-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, 16 | border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; 17 | -------------------------------------------------------------------------------- /src/assets/styles/bootstrap/_helpers.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/scss/functions"; 2 | @import "~bootstrap/scss/variables"; 3 | @import "~bootstrap/scss/mixins"; -------------------------------------------------------------------------------- /src/assets/styles/bootstrap/_index.scss: -------------------------------------------------------------------------------- 1 | // Base 2 | @import "~bootstrap/scss/root"; 3 | @import "~bootstrap/scss/reboot"; 4 | @import "~bootstrap/scss/transitions"; 5 | @import "~bootstrap/scss/type"; 6 | 7 | // Components 8 | @import "~bootstrap/scss/alert"; 9 | @import "~bootstrap/scss/badge"; 10 | @import "~bootstrap/scss/breadcrumb"; 11 | @import "~bootstrap/scss/button-group"; 12 | @import "~bootstrap/scss/buttons"; 13 | @import "~bootstrap/scss/card"; 14 | @import "~bootstrap/scss/close"; 15 | @import "~bootstrap/scss/code"; 16 | @import "~bootstrap/scss/custom-forms"; 17 | @import "~bootstrap/scss/dropdown"; 18 | @import "~bootstrap/scss/forms"; 19 | @import "~bootstrap/scss/grid"; 20 | @import "~bootstrap/scss/images"; 21 | @import "~bootstrap/scss/input-group"; 22 | @import "~bootstrap/scss/list-group"; 23 | @import "~bootstrap/scss/media"; 24 | @import "~bootstrap/scss/modal"; 25 | @import "~bootstrap/scss/nav"; 26 | @import "~bootstrap/scss/navbar"; 27 | @import "~bootstrap/scss/pagination"; 28 | @import "~bootstrap/scss/popover"; 29 | @import "~bootstrap/scss/progress"; 30 | @import "~bootstrap/scss/spinners"; 31 | @import "~bootstrap/scss/tables"; 32 | @import "~bootstrap/scss/toasts"; 33 | @import "~bootstrap/scss/tooltip"; 34 | 35 | // Utils 36 | @import "~bootstrap/scss/utilities"; 37 | @import "~bootstrap/scss/print"; -------------------------------------------------------------------------------- /src/components/AppHeader/index.js: -------------------------------------------------------------------------------- 1 | import AppHeader from './AppHeader'; 2 | export default AppHeader; 3 | -------------------------------------------------------------------------------- /src/components/AppNavigation/index.js: -------------------------------------------------------------------------------- 1 | import AppNavigation from './AppNavigation'; 2 | export default AppNavigation; 3 | -------------------------------------------------------------------------------- /src/components/Global/Alert.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 48 | -------------------------------------------------------------------------------- /src/components/Global/ButtonBackToTop.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 48 | 49 | 71 | -------------------------------------------------------------------------------- /src/components/Global/InfoTooltip.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 32 | 33 | 42 | -------------------------------------------------------------------------------- /src/components/Global/InputPasswordToggle.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 54 | 55 | 60 | -------------------------------------------------------------------------------- /src/components/Global/LoadingBar.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 75 | 76 | 96 | -------------------------------------------------------------------------------- /src/components/Global/PageContainer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 38 | -------------------------------------------------------------------------------- /src/components/Global/PageSection.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 30 | -------------------------------------------------------------------------------- /src/components/Global/PageTitle.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 39 | 40 | 48 | -------------------------------------------------------------------------------- /src/components/Global/Search.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 74 | 75 | 87 | -------------------------------------------------------------------------------- /src/components/Global/StatusIcon.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 35 | 36 | 62 | -------------------------------------------------------------------------------- /src/components/Global/TableCellCount.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 42 | -------------------------------------------------------------------------------- /src/components/Global/TableToolbarExport.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 43 | -------------------------------------------------------------------------------- /src/components/Mixins/BVPaginationMixin.js: -------------------------------------------------------------------------------- 1 | import i18n from '@/i18n'; 2 | export const currentPage = 1; 3 | export const perPage = 20; 4 | export const itemsPerPageOptions = [ 5 | { 6 | value: 10, 7 | text: '10', 8 | }, 9 | { 10 | value: 20, 11 | text: '20', 12 | }, 13 | { 14 | value: 30, 15 | text: '30', 16 | }, 17 | { 18 | value: 40, 19 | text: '40', 20 | }, 21 | { 22 | value: 0, 23 | text: i18n.global.t('global.table.viewAll'), 24 | }, 25 | ]; 26 | const BVPaginationMixin = { 27 | methods: { 28 | getTotalRowCount(count) { 29 | return this.perPage === 0 ? 0 : count; 30 | }, 31 | }, 32 | }; 33 | 34 | export default BVPaginationMixin; 35 | -------------------------------------------------------------------------------- /src/components/Mixins/BVTableSelectableMixin.js: -------------------------------------------------------------------------------- 1 | export const selectedRows = []; 2 | export const tableHeaderCheckboxModel = false; 3 | export const tableHeaderCheckboxIndeterminate = false; 4 | 5 | const BVTableSelectableMixin = { 6 | methods: { 7 | clearSelectedRows(tableRef) { 8 | if (tableRef) tableRef.clearSelected(); 9 | }, 10 | toggleSelectRow(tableRef, rowIndex) { 11 | if (tableRef && rowIndex !== undefined) { 12 | tableRef.isRowSelected(rowIndex) 13 | ? tableRef.unselectRow(rowIndex) 14 | : tableRef.selectRow(rowIndex); 15 | } 16 | }, 17 | onRowSelected(selectedRows, totalRowsCount) { 18 | if (selectedRows && totalRowsCount !== undefined) { 19 | this.selectedRows = selectedRows; 20 | if (selectedRows.length === 0) { 21 | this.tableHeaderCheckboxIndeterminate = false; 22 | this.tableHeaderCheckboxModel = false; 23 | } else if (selectedRows.length === totalRowsCount) { 24 | this.tableHeaderCheckboxIndeterminate = false; 25 | this.tableHeaderCheckboxModel = true; 26 | } else { 27 | this.tableHeaderCheckboxIndeterminate = true; 28 | this.tableHeaderCheckboxModel = true; 29 | } 30 | } 31 | }, 32 | onChangeHeaderCheckbox(tableRef) { 33 | if (tableRef) { 34 | if (this.tableHeaderCheckboxModel) tableRef.selectAllRows(); 35 | else tableRef.clearSelected(); 36 | } 37 | }, 38 | }, 39 | }; 40 | 41 | export default BVTableSelectableMixin; 42 | -------------------------------------------------------------------------------- /src/components/Mixins/DataFormatterMixin.js: -------------------------------------------------------------------------------- 1 | const DataFormatterMixin = { 2 | methods: { 3 | dataFormatter(value) { 4 | if (value === undefined || value === null || value === '') { 5 | return '--'; 6 | } else if (typeof value === 'number') { 7 | return parseFloat(value.toFixed(3)); 8 | } else { 9 | return value; 10 | } 11 | }, 12 | statusIcon(status) { 13 | switch (status) { 14 | case 'OK': 15 | return 'success'; 16 | case 'Warning': 17 | return 'warning'; 18 | case 'Critical': 19 | return 'danger'; 20 | default: 21 | return ''; 22 | } 23 | }, 24 | dataFormatterArray(value) { 25 | return value.join(', '); 26 | }, 27 | }, 28 | }; 29 | 30 | export default DataFormatterMixin; 31 | -------------------------------------------------------------------------------- /src/components/Mixins/JumpLinkMixin.js: -------------------------------------------------------------------------------- 1 | const JumpLinkMixin = { 2 | methods: { 3 | setFocus(element) { 4 | element.setAttribute('tabindex', '-1'); 5 | element.focus(); 6 | // Reason: https://axesslab.com/skip-links/#update-3-a-comment-from-gov-uk 7 | element.removeAttribute('tabindex'); 8 | }, 9 | scrollToOffset(event) { 10 | // Select element to scroll to 11 | const ref = event.target.getAttribute('data-ref'); 12 | const element = this.$refs[ref].$el; 13 | 14 | // Set focus and tabindex on selected element 15 | this.setFocus(element); 16 | 17 | // Set scroll offset below header 18 | const offset = element.offsetTop - 50; 19 | window.scroll({ 20 | top: offset, 21 | behavior: 'smooth', 22 | }); 23 | }, 24 | }, 25 | }; 26 | 27 | export default JumpLinkMixin; 28 | -------------------------------------------------------------------------------- /src/components/Mixins/LoadingBarMixin.js: -------------------------------------------------------------------------------- 1 | export const loading = true; 2 | 3 | const LoadingBarMixin = { 4 | methods: { 5 | startLoader() { 6 | this.$root.$emit('loader-start'); 7 | this.loading = true; 8 | }, 9 | endLoader() { 10 | this.$root.$emit('loader-end'); 11 | this.loading = false; 12 | }, 13 | hideLoader() { 14 | this.$root.$emit('loader-hide'); 15 | }, 16 | }, 17 | }; 18 | 19 | export default LoadingBarMixin; 20 | -------------------------------------------------------------------------------- /src/components/Mixins/LocalTimezoneLabelMixin.js: -------------------------------------------------------------------------------- 1 | import { format } from 'date-fns-tz'; 2 | 3 | const LocalTimezoneLabelMixin = { 4 | methods: { 5 | localOffset() { 6 | const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; 7 | const shortTz = this.$filters.shortTimeZone(new Date()); 8 | const pattern = `'${shortTz}' O`; 9 | return format(new Date(), pattern, { timezone }).replace('GMT', 'UTC'); 10 | }, 11 | }, 12 | }; 13 | 14 | export default LocalTimezoneLabelMixin; 15 | -------------------------------------------------------------------------------- /src/components/Mixins/SearchFilterMixin.js: -------------------------------------------------------------------------------- 1 | export const searchFilter = null; 2 | 3 | const SearchFilterMixin = { 4 | methods: { 5 | onChangeSearchInput(searchValue) { 6 | this.searchFilter = searchValue; 7 | }, 8 | onClearSearchInput() { 9 | this.searchFilter = null; 10 | }, 11 | }, 12 | }; 13 | 14 | export default SearchFilterMixin; 15 | -------------------------------------------------------------------------------- /src/components/Mixins/TableFilterMixin.js: -------------------------------------------------------------------------------- 1 | const TableFilterMixin = { 2 | methods: { 3 | getFilteredTableData(tableData = [], filters = []) { 4 | const filterItems = filters.reduce((arr, filter) => { 5 | return [...arr, ...filter.values]; 6 | }, []); 7 | // If no filters are active, then return all table data 8 | if (filterItems.length === 0) return tableData; 9 | 10 | const selectedValues = {}; 11 | for (const { key, values } of filters) { 12 | if (values.length > 0) { 13 | selectedValues[key] = values; 14 | } 15 | } 16 | 17 | // Check if row property value is included in list of 18 | // active filters 19 | return tableData.filter((row) => { 20 | for (const [key, values] of Object.entries(selectedValues)) { 21 | const rowProperty = row[key]; 22 | if (rowProperty && !values.includes(rowProperty)) { 23 | return false; 24 | } 25 | } 26 | return true; 27 | }); 28 | }, 29 | getFilteredTableDataByDate( 30 | tableData = [], 31 | startDate, 32 | endDate, 33 | propertyKey = 'date', 34 | ) { 35 | if (!startDate && !endDate) return tableData; 36 | let startDateInMs = startDate ? startDate.getTime() : 0; 37 | let endDateInMs = endDate ? endDate.getTime() : Number.POSITIVE_INFINITY; 38 | 39 | const isUtcDisplay = this.$store.getters['global/isUtcDisplay']; 40 | 41 | //Offset preference selected 42 | if (!isUtcDisplay) { 43 | startDateInMs = startDate 44 | ? startDate.getTime() + startDate.getTimezoneOffset() * 60000 45 | : 0; 46 | endDateInMs = endDate 47 | ? endDate.getTime() + endDate.getTimezoneOffset() * 60000 48 | : Number.POSITIVE_INFINITY; 49 | } 50 | 51 | return tableData.filter((row) => { 52 | const date = row[propertyKey]; 53 | if (!(date instanceof Date)) return; 54 | const dateInMs = date.getTime(); 55 | if (dateInMs >= startDateInMs && dateInMs <= endDateInMs) return row; 56 | }); 57 | }, 58 | }, 59 | }; 60 | 61 | export default TableFilterMixin; 62 | -------------------------------------------------------------------------------- /src/components/Mixins/TableRowExpandMixin.js: -------------------------------------------------------------------------------- 1 | import i18n from '@/i18n'; 2 | export const expandRowLabel = i18n.global.t('global.table.expandTableRow'); 3 | 4 | const TableRowExpandMixin = { 5 | methods: { 6 | toggleRowDetails(row) { 7 | row.toggleDetails(); 8 | row.detailsShowing 9 | ? (this.expandRowLabel = i18n.global.t('global.table.expandTableRow')) 10 | : (this.expandRowLabel = i18n.global.t( 11 | 'global.table.collapseTableRow', 12 | )); 13 | }, 14 | }, 15 | }; 16 | 17 | export default TableRowExpandMixin; 18 | -------------------------------------------------------------------------------- /src/components/Mixins/TableSortMixin.js: -------------------------------------------------------------------------------- 1 | const STATUS = ['OK', 'Warning', 'Critical']; 2 | 3 | const TableSortMixin = { 4 | methods: { 5 | sortStatus(a, b, key) { 6 | return STATUS.indexOf(a[key]) - STATUS.indexOf(b[key]); 7 | }, 8 | }, 9 | }; 10 | 11 | export default TableSortMixin; 12 | -------------------------------------------------------------------------------- /src/components/Mixins/VuelidateMixin.js: -------------------------------------------------------------------------------- 1 | const VuelidateMixin = { 2 | methods: { 3 | getValidationState(model) { 4 | const { $dirty, $error } = model; 5 | return $dirty ? !$error : null; 6 | }, 7 | }, 8 | }; 9 | 10 | export default VuelidateMixin; 11 | -------------------------------------------------------------------------------- /src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-Italic.woff -------------------------------------------------------------------------------- /src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-Light.woff -------------------------------------------------------------------------------- /src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-LightItalic.woff -------------------------------------------------------------------------------- /src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-Regular.woff -------------------------------------------------------------------------------- /src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-SemiBold.woff -------------------------------------------------------------------------------- /src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-SemiBoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/src/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-SemiBoldItalic.woff -------------------------------------------------------------------------------- /src/env/assets/fonts/Intel_Clear/IntelClear-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/src/env/assets/fonts/Intel_Clear/IntelClear-Bold-webfont.woff -------------------------------------------------------------------------------- /src/env/assets/fonts/Intel_Clear/IntelClear-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/src/env/assets/fonts/Intel_Clear/IntelClear-Light-webfont.woff -------------------------------------------------------------------------------- /src/env/assets/fonts/Intel_Clear/IntelClear-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/webui-vue/7fb9bb4f60eef821fb3964888a25cbc4e4e54718/src/env/assets/fonts/Intel_Clear/IntelClear-Regular-webfont.woff -------------------------------------------------------------------------------- /src/env/assets/styles/_default.scss: -------------------------------------------------------------------------------- 1 | // This file shall remain empty. -------------------------------------------------------------------------------- /src/env/assets/styles/_ibm.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Plex'; 3 | src: url('~@/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-Light.woff') format('woff'); 4 | font-weight: 200; 5 | } 6 | @font-face { 7 | font-family: 'Plex'; 8 | src: url('~@/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-LightItalic.woff') format('woff'); 9 | font-weight: 200; 10 | font-style: italic; 11 | } 12 | @font-face { 13 | font-family: 'Plex'; 14 | src: url('~@/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-Regular.woff') format('woff'); 15 | font-weight: 400; 16 | } 17 | @font-face { 18 | font-family: 'Plex'; 19 | src: url('~@/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-Italic.woff') format('woff'); 20 | font-weight: 400; 21 | font-style: italic; 22 | } 23 | @font-face { 24 | font-family: 'Plex'; 25 | src: url('~@/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-SemiBold.woff') format('woff'); 26 | font-weight: 700; 27 | } 28 | @font-face { 29 | font-family: 'Plex'; 30 | src: url('~@/env/assets/fonts/IBM_Plex_Sans/IBMPlexSans-SemiBoldItalic.woff') format('woff'); 31 | font-weight: 700; 32 | font-style: italic; 33 | } 34 | 35 | // IBM Plex with Bootstrap default as fallbacks 36 | // https://getbootstrap.com/docs/4.4/content/reboot/#native-font-stack 37 | 38 | $font-family-base: 'Plex', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 39 | 40 | // IBM theme colors 41 | $blue: #0F62FE; 42 | $red: #DA1E28; 43 | $green: #24A146; 44 | $yellow: #F1C21B; 45 | 46 | $primary: $blue; 47 | $danger: $red; 48 | $success: $green; 49 | $warning: $yellow; 50 | 51 | // Validation icons 52 | $enable-validation-icons: true; 53 | $form-feedback-icon-valid: none; 54 | $form-feedback-icon-invalid: url("data:image/svg+xml,"); 55 | 56 | // Progress loading bar variables 57 | $loading-color-0: #552de5; 58 | $loading-color-50: $primary; 59 | $loading-color-100: #2dbde5; 60 | 61 | // Progress loading bar gradient 62 | .progress { 63 | height: 0.8rem!important; 64 | } 65 | .progress-bar { 66 | background: linear-gradient( 67 | 90deg, 68 | $loading-color-0, 69 | $loading-color-50, 70 | $loading-color-100 71 | ); 72 | background-size: 200% 200%; 73 | animation: gradient 3s $standard-easing--productive infinite; 74 | } 75 | @keyframes gradient { 76 | 0% { 77 | background-position: 0% 50%; 78 | } 79 | 50% { 80 | background-position: 100% 50%; 81 | } 82 | 100% { 83 | background-position: 0% 50%; 84 | } 85 | } -------------------------------------------------------------------------------- /src/env/assets/styles/_intel.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Intel Clear"; 3 | src: url("~@/env/assets/fonts/Intel_Clear/IntelClear-Light-webfont.woff") 4 | format("woff"); 5 | font-weight: 200; 6 | font-style: normal; 7 | } 8 | @font-face { 9 | font-family: "Intel Clear"; 10 | src: url("~@/env/assets/fonts/Intel_Clear/IntelClear-Regular-webfont.woff") 11 | format("woff"); 12 | font-weight: 400; 13 | font-style: normal; 14 | } 15 | @font-face { 16 | font-family: "Intel Clear"; 17 | src: url("~@/env/assets/fonts/Intel_Clear/IntelClear-Bold-webfont.woff") 18 | format("woff"); 19 | font-weight: 700; 20 | font-style: normal; 21 | } 22 | 23 | $font-family-base: "Intel Clear", "Helvetica Neue", Helvetica, Arial, sans-serif; 24 | 25 | $classic-blue: #0068B5; 26 | $energy-blue: #00C7FD; 27 | 28 | $primary: $classic-blue; 29 | 30 | $red: #ed1c24; 31 | $orange: #ffa300; 32 | $yellow: #f3d54e; 33 | $green: #c4d600; 34 | $cyan: $energy-blue; 35 | $gray: #AEAEAE; 36 | $dark-gray: #808080; 37 | $light-gray: #E9E9E9; 38 | $black: #000000; 39 | $white: #ffffff; 40 | 41 | $danger: #C81326; 42 | $success: #8BAE46; 43 | $warning: #FEC91B; 44 | 45 | 46 | $gray-100: $white; 47 | 48 | $loading-color: $warning; 49 | $navbar-color: $classic-blue; 50 | 51 | .status-icon.secondary { 52 | fill: $light-gray !important; 53 | } 54 | 55 | .custom-checkbox .custom-control-input:checked ~ .custom-control-label::after, 56 | .custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before, 57 | .custom-control-input:checked ~ .custom-control-label::before { 58 | background-color: $primary !important; 59 | } 60 | -------------------------------------------------------------------------------- /src/env/store/ibm.js: -------------------------------------------------------------------------------- 1 | import store from '@/store'; 2 | import KeyClearStore from '@/store/modules/Operations/KeyClearStore'; 3 | 4 | store.unregisterModule('virtualMedia'); 5 | 6 | store.registerModule('key-clear', KeyClearStore); 7 | 8 | export default store; 9 | -------------------------------------------------------------------------------- /src/env/store/intel.js: -------------------------------------------------------------------------------- 1 | import store from '@/store'; 2 | 3 | // Use store.registerModule() to register env specific 4 | // store modules 5 | // https://vuex.vuejs.org/api/#registermodule 6 | 7 | store.unregisterModule('ldap'); 8 | 9 | export default store; 10 | -------------------------------------------------------------------------------- /src/eventBus.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | 3 | const eventBus = createApp(); 4 | 5 | export default eventBus; 6 | -------------------------------------------------------------------------------- /src/i18n.js: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n'; 2 | 3 | import en_us from './locales/en-US.json'; 4 | import ru_ru from './locales/ru-RU.json'; 5 | import ka_ge from './locales/ka-GE.json'; 6 | 7 | function loadLocaleMessages() { 8 | const messages = { 9 | 'en-US': en_us, 10 | 'ka-GE': ka_ge, 11 | 'ru-RU': ru_ru, 12 | }; 13 | return messages; 14 | } 15 | 16 | const i18n = createI18n({ 17 | // Get default locale from local storage 18 | locale: window.localStorage.getItem('storedLanguage'), 19 | // Locales that don't exist will fallback to English 20 | fallbackLocale: 'en-US', 21 | // Falling back to fallbackLocale generates two console warnings 22 | // Silent fallback suppresses console warnings when using fallback 23 | silentFallbackWarn: true, 24 | messages: loadLocaleMessages(), 25 | globalInjection: false, 26 | legacy: false, 27 | }); 28 | 29 | export default i18n; 30 | -------------------------------------------------------------------------------- /src/layouts/AppLayout.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 71 | 72 | 105 | -------------------------------------------------------------------------------- /src/layouts/ConsoleLayout.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /src/layouts/LoginLayout.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 48 | 49 | 115 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from 'vue-router'; 2 | 3 | //Do not change store or routes import. 4 | //Exact match alias set to support 5 | //dotenv customizations. 6 | import store from '../store'; 7 | import routes from './routes'; 8 | 9 | const router = createRouter({ 10 | history: createWebHashHistory(), 11 | routes, 12 | linkExactActiveClass: 'nav-link--current', 13 | scrollBehavior() { 14 | return { x: 0, y: 0 }; 15 | }, 16 | }); 17 | 18 | function allowRouterToNavigate(to, next, currentUserRole) { 19 | if (to.matched.some((record) => record.meta.requiresAuth)) { 20 | if (store.getters['authentication/isLoggedIn']) { 21 | if (to.meta.exclusiveToRoles) { 22 | // The privilege for the specific router was verified using the 23 | // exclusiveToRoles roles in the router. 24 | if (to.meta.exclusiveToRoles.includes(currentUserRole)) { 25 | next(); 26 | } else { 27 | next('*'); 28 | } 29 | return; 30 | } 31 | next(); 32 | return; 33 | } 34 | next('/login'); 35 | } else { 36 | next(); 37 | } 38 | } 39 | 40 | router.beforeEach((to, from, next) => { 41 | let currentUserRole = store.getters['global/userPrivilege']; 42 | // condition will get satisfied if user refreshed after login 43 | if (!currentUserRole && store.getters['authentication/isLoggedIn']) { 44 | // invoke API call to get the role ID 45 | store 46 | .dispatch('authentication/getSessionPrivilege') 47 | .then(() => { 48 | let currentUserRole = store.getters['global/userPrivilege']; 49 | allowRouterToNavigate(to, next, currentUserRole); 50 | }) 51 | // our store got out of sync, start afresh 52 | .catch(() => { 53 | console.log('Failed to obtain current Roles, logging out.'); 54 | store.dispatch('authentication/logout'); 55 | }); 56 | } else { 57 | allowRouterToNavigate(to, next, currentUserRole); 58 | } 59 | }); 60 | 61 | export default router; 62 | -------------------------------------------------------------------------------- /src/store/modules/HardwareStatus/AssemblyStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | import i18n from '@/i18n'; 3 | 4 | const AssemblyStore = { 5 | namespaced: true, 6 | state: { 7 | assemblies: null, 8 | }, 9 | getters: { 10 | assemblies: (state) => state.assemblies, 11 | }, 12 | mutations: { 13 | setAssemblyInfo: (state, data) => { 14 | state.assemblies = data.map((assembly) => { 15 | const { 16 | MemberId, 17 | PartNumber, 18 | SerialNumber, 19 | SparePartNumber, 20 | Model, 21 | Name, 22 | Location, 23 | LocationIndicatorActive, 24 | } = assembly; 25 | return { 26 | id: MemberId, 27 | partNumber: PartNumber, 28 | serialNumber: SerialNumber, 29 | sparePartNumber: SparePartNumber, 30 | model: Model, 31 | name: Name, 32 | locationNumber: Location?.PartLocation?.ServiceLabel, 33 | identifyLed: LocationIndicatorActive, 34 | uri: assembly['@odata.id'], 35 | }; 36 | }); 37 | }, 38 | }, 39 | actions: { 40 | async getAssemblyInfo({ commit }) { 41 | return await api 42 | .get('/redfish/v1/Chassis/chassis/Assembly') 43 | .then(({ data }) => commit('setAssemblyInfo', data?.Assemblies)) 44 | .catch((error) => console.log(error)); 45 | }, 46 | async updateIdentifyLedValue({ dispatch }, led) { 47 | const uri = led.uri; 48 | const updatedIdentifyLedValue = { 49 | Assemblies: [ 50 | { 51 | MemberId: led.memberId, 52 | LocationIndicatorActive: led.identifyLed, 53 | }, 54 | ], 55 | }; 56 | 57 | return await api 58 | .patch(uri, updatedIdentifyLedValue) 59 | .then(() => { 60 | if (led.identifyLed) { 61 | return i18n.global.t( 62 | 'pageInventory.toast.successEnableIdentifyLed', 63 | ); 64 | } else { 65 | return i18n.global.t( 66 | 'pageInventory.toast.successDisableIdentifyLed', 67 | ); 68 | } 69 | }) 70 | .catch((error) => { 71 | dispatch('getAssemblyInfo'); 72 | console.log('error', error); 73 | if (led.identifyLed) { 74 | throw new Error( 75 | i18n.global.t('pageInventory.toast.errorEnableIdentifyLed'), 76 | ); 77 | } else { 78 | throw new Error( 79 | i18n.global.t('pageInventory.toast.errorDisableIdentifyLed'), 80 | ); 81 | } 82 | }); 83 | }, 84 | }, 85 | }; 86 | 87 | export default AssemblyStore; 88 | -------------------------------------------------------------------------------- /src/store/modules/HardwareStatus/FanStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | 3 | const FanStore = { 4 | namespaced: true, 5 | state: { 6 | fans: [], 7 | }, 8 | getters: { 9 | fans: (state) => state.fans, 10 | }, 11 | mutations: { 12 | setFanInfo: (state, data) => { 13 | state.fans = data.map((fan) => { 14 | const { 15 | Id, 16 | Name, 17 | PartNumber, 18 | SerialNumber, 19 | SpeedPercent = {}, 20 | Status = {}, 21 | } = fan; 22 | return { 23 | id: Id, 24 | health: Status.Health, 25 | name: Name, 26 | speed: SpeedPercent.Reading, 27 | statusState: Status.State, 28 | healthRollup: Status.HealthRollup, 29 | partNumber: PartNumber, 30 | serialNumber: SerialNumber, 31 | }; 32 | }); 33 | }, 34 | }, 35 | actions: { 36 | async getChassisCollection() { 37 | return await api 38 | .get('/redfish/v1/Chassis') 39 | .then(({ data: { Members } }) => 40 | api.all( 41 | Members.map((member) => 42 | api.get(member['@odata.id']).then((response) => response.data), 43 | ), 44 | ), 45 | ) 46 | .catch((error) => console.log(error)); 47 | }, 48 | async getFanInfo({ dispatch, commit }) { 49 | const collection = await dispatch('getChassisCollection'); 50 | if (!collection || collection.length === 0) return; 51 | return await api 52 | .all(collection.map((chassis) => dispatch('getChassisFans', chassis))) 53 | .then((fansFromChassis) => commit('setFanInfo', fansFromChassis.flat())) 54 | .catch((error) => console.log(error)); 55 | }, 56 | async getChassisFans(_, chassis) { 57 | return await api 58 | .get(chassis.ThermalSubsystem['@odata.id']) 59 | .then((response) => { 60 | return api.get(`${response.data.Fans['@odata.id']}`); 61 | }) 62 | .then(({ data: { Members } }) => { 63 | const promises = Members.map((member) => 64 | api.get(member['@odata.id']), 65 | ); 66 | return api.all(promises); 67 | }) 68 | .then((response) => { 69 | const data = response.map(({ data }) => data); 70 | return data; 71 | }) 72 | .catch((error) => console.log(error)); 73 | }, 74 | }, 75 | }; 76 | 77 | export default FanStore; 78 | -------------------------------------------------------------------------------- /src/store/modules/HardwareStatus/PowerSupplyStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | 3 | const PowerSupplyStore = { 4 | namespaced: true, 5 | state: { 6 | powerSupplies: [], 7 | }, 8 | getters: { 9 | powerSupplies: (state) => state.powerSupplies, 10 | }, 11 | mutations: { 12 | setPowerSupply: (state, data) => { 13 | state.powerSupplies = data.map((powerSupply) => { 14 | const { 15 | EfficiencyRatings = [], 16 | FirmwareVersion, 17 | LocationIndicatorActive, 18 | Id, 19 | Manufacturer, 20 | Model, 21 | Name, 22 | PartNumber, 23 | PowerInputWatts, 24 | SerialNumber, 25 | SparePartNumber, 26 | Location, 27 | Status = {}, 28 | } = powerSupply; 29 | return { 30 | id: Id, 31 | health: Status.Health, 32 | partNumber: PartNumber, 33 | serialNumber: SerialNumber, 34 | efficiencyPercent: EfficiencyRatings[0].EfficiencyPercent, 35 | firmwareVersion: FirmwareVersion, 36 | identifyLed: LocationIndicatorActive, 37 | manufacturer: Manufacturer, 38 | model: Model, 39 | powerInputWatts: PowerInputWatts, 40 | name: Name, 41 | sparePartNumber: SparePartNumber, 42 | locationNumber: Location?.PartLocation?.ServiceLabel, 43 | statusState: Status.State, 44 | }; 45 | }); 46 | }, 47 | }, 48 | actions: { 49 | async getChassisCollection() { 50 | return await api 51 | .get('/redfish/v1/Chassis') 52 | .then(({ data: { Members } }) => 53 | Members.map((member) => member['@odata.id']), 54 | ) 55 | .catch((error) => console.log(error)); 56 | }, 57 | async getAllPowerSupplies({ dispatch, commit }) { 58 | const collection = await dispatch('getChassisCollection'); 59 | if (!collection) return; 60 | return await api 61 | .all(collection.map((chassis) => dispatch('getChassisPower', chassis))) 62 | .then((supplies) => { 63 | let suppliesList = []; 64 | supplies.forEach( 65 | (supply) => (suppliesList = [...suppliesList, ...supply]), 66 | ); 67 | commit('setPowerSupply', suppliesList); 68 | }) 69 | .catch((error) => console.log(error)); 70 | }, 71 | async getChassisPower(_, id) { 72 | return await api 73 | .get(`${id}/PowerSubsystem`) 74 | .then((response) => { 75 | return api.get(`${response.data.PowerSupplies['@odata.id']}`); 76 | }) 77 | .then(({ data: { Members } }) => { 78 | const promises = Members.map((member) => 79 | api.get(member['@odata.id']), 80 | ); 81 | return api.all(promises); 82 | }) 83 | .then((response) => { 84 | const data = response.map(({ data }) => data); 85 | return data; 86 | }) 87 | .catch((error) => console.log(error)); 88 | }, 89 | }, 90 | }; 91 | 92 | export default PowerSupplyStore; 93 | -------------------------------------------------------------------------------- /src/store/modules/HardwareStatus/ServerLedStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | import i18n from '@/i18n'; 3 | 4 | const ServerLedStore = { 5 | namespaced: true, 6 | state: { 7 | indicatorLedActiveState: false, 8 | }, 9 | getters: { 10 | getIndicatorLedActiveState: (state) => state.indicatorLedActiveState, 11 | }, 12 | mutations: { 13 | setIndicatorLedActiveState(state, indicatorLedActiveState) { 14 | state.indicatorLedActiveState = indicatorLedActiveState; 15 | }, 16 | }, 17 | actions: { 18 | async getIndicatorLedActiveState({ commit }) { 19 | return await api 20 | .get(`${await this.dispatch('global/getSystemPath')}`) 21 | .then((response) => { 22 | commit( 23 | 'setIndicatorLedActiveState', 24 | response.data.LocationIndicatorActive, 25 | ); 26 | }) 27 | .catch((error) => console.log(error)); 28 | }, 29 | async saveIndicatorLedActiveState({ commit }, payload) { 30 | commit('setIndicatorLedActiveState', payload); 31 | return await api 32 | .patch(`${await this.dispatch('global/getSystemPath')}`, { 33 | LocationIndicatorActive: payload, 34 | }) 35 | .catch((error) => { 36 | console.log(error); 37 | commit('setIndicatorLedActiveState', !payload); 38 | if (payload) { 39 | throw new Error( 40 | i18n.global.t('pageInventory.toast.errorEnableIdentifyLed'), 41 | ); 42 | } else { 43 | throw new Error( 44 | i18n.global.t('pageInventory.toast.errorDisableIdentifyLed'), 45 | ); 46 | } 47 | }); 48 | }, 49 | }, 50 | }; 51 | 52 | export default ServerLedStore; 53 | -------------------------------------------------------------------------------- /src/store/modules/HardwareStatus/SystemStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | import i18n from '@/i18n'; 3 | 4 | const SystemStore = { 5 | namespaced: true, 6 | state: { 7 | systems: [], 8 | }, 9 | getters: { 10 | systems: (state) => state.systems, 11 | }, 12 | mutations: { 13 | setSystemInfo: (state, data) => { 14 | const system = {}; 15 | system.assetTag = data.AssetTag; 16 | system.description = data.Description; 17 | system.firmwareVersion = data.BiosVersion; 18 | system.hardwareType = data.Name; 19 | system.health = data.Status?.Health; 20 | system.totalSystemMemoryGiB = data.MemorySummary?.TotalSystemMemoryGiB; 21 | system.id = data.Id; 22 | system.locationIndicatorActive = data.LocationIndicatorActive; 23 | system.locationNumber = data.Location?.PartLocation?.ServiceLabel; 24 | system.manufacturer = data.Manufacturer; 25 | system.model = data.Model; 26 | system.processorSummaryCount = data.ProcessorSummary?.Count; 27 | system.processorSummaryCoreCount = data.ProcessorSummary?.CoreCount; 28 | system.powerState = data.PowerState; 29 | system.serialNumber = data.SerialNumber; 30 | system.serialConsoleEnabled = data.SerialConsole.ServiceEnabled; 31 | system.serialConsoleMaxSessions = 32 | data.SerialConsole.MaxConcurrentSessions; 33 | system.healthRollup = data.Status?.HealthRollup; 34 | system.subModel = data.SubModel; 35 | system.statusState = data.Status?.State; 36 | system.systemType = data.SystemType; 37 | state.systems = [system]; 38 | }, 39 | }, 40 | actions: { 41 | async getSystem({ commit }) { 42 | return await api 43 | .get(`${await this.dispatch('global/getSystemPath')}`) 44 | .then(({ data }) => commit('setSystemInfo', data)) 45 | .catch((error) => console.log(error)); 46 | }, 47 | async changeIdentifyLedState({ commit }, ledState) { 48 | return await api 49 | .patch(`${await this.dispatch('global/getSystemPath')}`, { 50 | LocationIndicatorActive: ledState, 51 | }) 52 | .then(() => { 53 | if (ledState) { 54 | return i18n.global.t( 55 | 'pageInventory.toast.successEnableIdentifyLed', 56 | ); 57 | } else { 58 | return i18n.global.t( 59 | 'pageInventory.toast.successDisableIdentifyLed', 60 | ); 61 | } 62 | }) 63 | .catch((error) => { 64 | commit('setSystemInfo', this.state.system.systems[0]); 65 | console.log('error', error); 66 | if (ledState) { 67 | throw new Error( 68 | i18n.global.t('pageInventory.toast.errorEnableIdentifyLed'), 69 | ); 70 | } else { 71 | throw new Error( 72 | i18n.global.t('pageInventory.toast.errorDisableIdentifyLed'), 73 | ); 74 | } 75 | }); 76 | }, 77 | }, 78 | }; 79 | 80 | export default SystemStore; 81 | -------------------------------------------------------------------------------- /src/store/modules/Logs/PostCodeLogsStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | import i18n from '@/i18n'; 3 | 4 | const PostCodeLogsStore = { 5 | namespaced: true, 6 | state: { 7 | allPostCodes: [], 8 | }, 9 | getters: { 10 | allPostCodes: (state) => state.allPostCodes, 11 | }, 12 | mutations: { 13 | setAllPostCodes: (state, allPostCodes) => 14 | (state.allPostCodes = allPostCodes), 15 | }, 16 | actions: { 17 | async getPostCodesLogData({ commit }) { 18 | return await api 19 | .get( 20 | `${await this.dispatch('global/getSystemPath')}/LogServices/PostCodes/Entries`, 21 | ) 22 | .then(({ data: { Members = [] } = {} }) => { 23 | const postCodeLogs = Members.map((log) => { 24 | const { Created, MessageArgs, AdditionalDataURI } = log; 25 | return { 26 | date: new Date(Created), 27 | bootCount: MessageArgs[0], 28 | timeStampOffset: MessageArgs[1], 29 | postCode: MessageArgs[2], 30 | uri: AdditionalDataURI, 31 | }; 32 | }); 33 | commit('setAllPostCodes', postCodeLogs); 34 | }) 35 | .catch((error) => { 36 | console.log('POST Codes Log Data:', error); 37 | }); 38 | }, 39 | async deleteAllPostCodeLogs({ dispatch }, data) { 40 | return await api 41 | .post( 42 | `${await this.dispatch('global/getSystemPath')}/LogServices/PostCodes/Actions/LogService.ClearLog`, 43 | ) 44 | .then(() => dispatch('getPostCodesLogData')) 45 | .then(() => 46 | i18n.global.t('pagePostCodeLogs.toast.successDelete', data.length), 47 | ) 48 | .catch((error) => { 49 | console.log(error); 50 | throw new Error( 51 | i18n.global.t('pagePostCodeLogs.toast.errorDelete', data.length), 52 | ); 53 | }); 54 | }, 55 | }, 56 | }; 57 | 58 | export default PostCodeLogsStore; 59 | -------------------------------------------------------------------------------- /src/store/modules/Operations/FactoryResetStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | import i18n from '@/i18n'; 3 | 4 | const FactoryResetStore = { 5 | namespaced: true, 6 | actions: { 7 | async resetToDefaults() { 8 | return await api 9 | .post( 10 | `${await this.dispatch('global/getBmcPath')}/Actions/Manager.ResetToDefaults`, 11 | { 12 | ResetType: 'ResetAll', 13 | }, 14 | ) 15 | .then(() => 16 | i18n.global.t('pageFactoryReset.toast.resetToDefaultsSuccess'), 17 | ) 18 | .catch((error) => { 19 | console.log('Factory Reset: ', error); 20 | throw new Error( 21 | i18n.global.t('pageFactoryReset.toast.resetToDefaultsError'), 22 | ); 23 | }); 24 | }, 25 | async resetBios() { 26 | return await api 27 | .post( 28 | `${await this.dispatch('global/getSystemPath')}/Bios/Actions/Bios.ResetBios`, 29 | ) 30 | .then(() => i18n.global.t('pageFactoryReset.toast.resetBiosSuccess')) 31 | .catch((error) => { 32 | console.log('Factory Reset: ', error); 33 | throw new Error( 34 | i18n.global.t('pageFactoryReset.toast.resetBiosError'), 35 | ); 36 | }); 37 | }, 38 | }, 39 | }; 40 | 41 | export default FactoryResetStore; 42 | -------------------------------------------------------------------------------- /src/store/modules/Operations/KeyClearStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | import i18n from '@/i18n'; 3 | 4 | const KeyClearStore = { 5 | namespaced: true, 6 | actions: { 7 | async clearEncryptionKeys(_, selectedKey) { 8 | const selectedKeyForClearing = { 9 | Attributes: { hb_key_clear_request: selectedKey }, 10 | }; 11 | return await api 12 | .patch( 13 | `${await this.dispatch('global/getSystemPath')}/Bios/Settings`, 14 | selectedKeyForClearing, 15 | ) 16 | .then(() => 17 | i18n.global.t('pageKeyClear.toast.selectedKeyClearedSuccess'), 18 | ) 19 | .catch((error) => { 20 | console.log('Key clear', error); 21 | throw new Error( 22 | i18n.global.t('pageKeyClear.toast.selectedKeyClearedError'), 23 | ); 24 | }); 25 | }, 26 | }, 27 | }; 28 | 29 | export default KeyClearStore; 30 | -------------------------------------------------------------------------------- /src/store/modules/ResourceManagement/PowerControlStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | import i18n from '@/i18n'; 3 | 4 | const PowerControlStore = { 5 | namespaced: true, 6 | state: { 7 | powerCapValue: null, 8 | powerCapUri: '', 9 | powerConsumptionValue: null, 10 | }, 11 | getters: { 12 | powerCapValue: (state) => state.powerCapValue, 13 | powerCapUri: (state) => state.powerCapUri, 14 | powerConsumptionValue: (state) => state.powerConsumptionValue, 15 | }, 16 | mutations: { 17 | setPowerCapValue: (state, powerCapValue) => 18 | (state.powerCapValue = powerCapValue), 19 | setPowerCapUri: (state, powerCapUri) => (state.powerCapUri = powerCapUri), 20 | setPowerConsumptionValue: (state, powerConsumptionValue) => 21 | (state.powerConsumptionValue = powerConsumptionValue), 22 | }, 23 | actions: { 24 | setPowerCapUpdatedValue({ commit }, value) { 25 | commit('setPowerCapValue', value); 26 | }, 27 | async getChassisCollection() { 28 | return await api 29 | .get('/redfish/v1/') 30 | .then((response) => api.get(response.data.Chassis['@odata.id'])) 31 | .then(({ data: { Members } }) => 32 | Members.map((member) => member['@odata.id']), 33 | ) 34 | .catch((error) => console.log(error)); 35 | }, 36 | async getPowerControl({ dispatch, commit }) { 37 | const collection = await dispatch('getChassisCollection'); 38 | if (!collection || collection.length === 0) return; 39 | return await api 40 | .get(`${collection[0]}`) 41 | .then((response) => api.get(response.data.Power['@odata.id'])) 42 | .then((response) => { 43 | const powerControl = response.data.PowerControl; 44 | if (!powerControl || powerControl.length === 0) return; 45 | const powerCapUri = response.data['@odata.id']; 46 | const powerCap = powerControl[0].PowerLimit.LimitInWatts; 47 | // If system is powered off, power consumption does not exist in the PowerControl 48 | const powerConsumption = powerControl[0].PowerConsumedWatts || null; 49 | commit('setPowerCapUri', powerCapUri); 50 | commit('setPowerCapValue', powerCap); 51 | commit('setPowerConsumptionValue', powerConsumption); 52 | }) 53 | .catch((error) => { 54 | console.log('Power control', error); 55 | }); 56 | }, 57 | async setPowerControl({ state }, powerCapValue) { 58 | const data = { 59 | PowerControl: [{ PowerLimit: { LimitInWatts: powerCapValue } }], 60 | }; 61 | return await api 62 | .patch(state.powerCapUri, data) 63 | .then(() => 64 | i18n.global.t('pageServerPowerOperations.toast.successSaveSettings'), 65 | ) 66 | .catch((error) => { 67 | console.log(error); 68 | throw new Error( 69 | i18n.global.t('pageServerPowerOperations.toast.errorSaveSettings'), 70 | ); 71 | }); 72 | }, 73 | }, 74 | }; 75 | 76 | export default PowerControlStore; 77 | -------------------------------------------------------------------------------- /src/store/modules/SecurityAndAccess/SessionsStore.js: -------------------------------------------------------------------------------- 1 | import api, { getResponseCount } from '@/store/api'; 2 | import i18n from '@/i18n'; 3 | 4 | const SessionsStore = { 5 | namespaced: true, 6 | state: { 7 | allConnections: [], 8 | }, 9 | getters: { 10 | allConnections: (state) => state.allConnections, 11 | }, 12 | mutations: { 13 | setAllConnections: (state, allConnections) => 14 | (state.allConnections = allConnections), 15 | }, 16 | actions: { 17 | async getSessionsData({ commit }) { 18 | return await api 19 | .get('/redfish/v1/SessionService/Sessions') 20 | .then((response) => 21 | response.data.Members.map((sessionLogs) => sessionLogs['@odata.id']), 22 | ) 23 | .then((sessionUris) => 24 | api.all(sessionUris.map((sessionUri) => api.get(sessionUri))), 25 | ) 26 | .then((sessionUris) => { 27 | const allConnectionsData = sessionUris.map((sessionUri) => { 28 | return { 29 | sessionID: sessionUri.data?.Id, 30 | context: sessionUri.data?.Context 31 | ? sessionUri.data?.Context 32 | : '-', 33 | username: sessionUri.data?.UserName, 34 | ipAddress: sessionUri.data?.ClientOriginIPAddress, 35 | uri: sessionUri.data['@odata.id'], 36 | }; 37 | }); 38 | commit('setAllConnections', allConnectionsData); 39 | }) 40 | .catch((error) => { 41 | console.log('Client Session Data:', error); 42 | }); 43 | }, 44 | async disconnectSessions({ dispatch }, uris = []) { 45 | const promises = uris.map((uri) => 46 | api.delete(uri).catch((error) => { 47 | console.log(error); 48 | return error; 49 | }), 50 | ); 51 | return await api 52 | .all(promises) 53 | .then((response) => { 54 | dispatch('getSessionsData'); 55 | return response; 56 | }) 57 | .then( 58 | api.spread((...responses) => { 59 | const { successCount, errorCount } = getResponseCount(responses); 60 | const toastMessages = []; 61 | 62 | if (successCount) { 63 | const message = i18n.global.t( 64 | 'pageSessions.toast.successDelete', 65 | successCount, 66 | ); 67 | toastMessages.push({ type: 'success', message }); 68 | } 69 | 70 | if (errorCount) { 71 | const message = i18n.global.t( 72 | 'pageSessions.toast.errorDelete', 73 | errorCount, 74 | ); 75 | toastMessages.push({ type: 'error', message }); 76 | } 77 | return toastMessages; 78 | }), 79 | ); 80 | }, 81 | }, 82 | }; 83 | export default SessionsStore; 84 | -------------------------------------------------------------------------------- /src/store/modules/Settings/PowerPolicyStore.js: -------------------------------------------------------------------------------- 1 | import api from '@/store/api'; 2 | import i18n from '@/i18n'; 3 | 4 | const PowerPolicyStore = { 5 | namespaced: true, 6 | state: { 7 | powerRestoreCurrentPolicy: null, 8 | powerRestorePolicies: [], 9 | }, 10 | getters: { 11 | powerRestoreCurrentPolicy: (state) => state.powerRestoreCurrentPolicy, 12 | powerRestorePolicies: (state) => state.powerRestorePolicies, 13 | }, 14 | mutations: { 15 | setPowerRestoreCurrentPolicy: (state, powerRestoreCurrentPolicy) => 16 | (state.powerRestoreCurrentPolicy = powerRestoreCurrentPolicy), 17 | setPowerRestorePolicies: (state, powerRestorePolicies) => 18 | (state.powerRestorePolicies = powerRestorePolicies), 19 | }, 20 | actions: { 21 | async getPowerRestorePolicies({ commit }) { 22 | return await api 23 | .get('/redfish/v1/JsonSchemas/ComputerSystem') 24 | .then(async (response) => { 25 | if ( 26 | response.data?.Location.length > 0 && 27 | response.data?.Location[0].Uri 28 | ) { 29 | return await api.get(response.data?.Location[0].Uri).then( 30 | ({ 31 | data: { 32 | definitions: { PowerRestorePolicyTypes = {} }, 33 | }, 34 | }) => { 35 | let powerPoliciesData = PowerRestorePolicyTypes.enum.map( 36 | (powerState) => { 37 | let desc = `${i18n.global.t( 38 | `pagePowerRestorePolicy.policies.${powerState}`, 39 | )} - ${ 40 | PowerRestorePolicyTypes.enumDescriptions[powerState] 41 | }`; 42 | return { 43 | state: powerState, 44 | desc, 45 | }; 46 | }, 47 | ); 48 | commit('setPowerRestorePolicies', powerPoliciesData); 49 | }, 50 | ); 51 | } 52 | }); 53 | }, 54 | async getPowerRestoreCurrentPolicy({ commit }) { 55 | return await api 56 | .get(`${await this.dispatch('global/getSystemPath')}`) 57 | .then(({ data: { PowerRestorePolicy } }) => { 58 | commit('setPowerRestoreCurrentPolicy', PowerRestorePolicy); 59 | }) 60 | .catch((error) => console.log(error)); 61 | }, 62 | async setPowerRestorePolicy({ dispatch }, powerPolicy) { 63 | const data = { PowerRestorePolicy: powerPolicy }; 64 | 65 | return await api 66 | .patch(`${await this.dispatch('global/getSystemPath')}`, data) 67 | .then(() => { 68 | dispatch('getPowerRestoreCurrentPolicy'); 69 | return i18n.global.t( 70 | 'pagePowerRestorePolicy.toast.successSaveSettings', 71 | ); 72 | }) 73 | .catch((error) => { 74 | console.log(error); 75 | throw new Error( 76 | i18n.global.t('pagePowerRestorePolicy.toast.errorSaveSettings'), 77 | ); 78 | }); 79 | }, 80 | }, 81 | }; 82 | 83 | export default PowerPolicyStore; 84 | -------------------------------------------------------------------------------- /src/views/ChangePassword/index.js: -------------------------------------------------------------------------------- 1 | import ChangePassword from './ChangePassword.vue'; 2 | export default ChangePassword; 3 | -------------------------------------------------------------------------------- /src/views/HardwareStatus/Inventory/InventoryServiceIndicator.vue: -------------------------------------------------------------------------------- 1 | 40 | 84 | -------------------------------------------------------------------------------- /src/views/HardwareStatus/Inventory/index.js: -------------------------------------------------------------------------------- 1 | import Inventory from './Inventory.vue'; 2 | export default Inventory; 3 | -------------------------------------------------------------------------------- /src/views/HardwareStatus/Sensors/index.js: -------------------------------------------------------------------------------- 1 | import Sensors from './Sensors.vue'; 2 | export default Sensors; 3 | -------------------------------------------------------------------------------- /src/views/Login/index.js: -------------------------------------------------------------------------------- 1 | import Login from './Login.vue'; 2 | export default Login; 3 | -------------------------------------------------------------------------------- /src/views/Logs/Dumps/DumpsModalConfirmation.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 84 | -------------------------------------------------------------------------------- /src/views/Logs/Dumps/index.js: -------------------------------------------------------------------------------- 1 | import Dumps from './Dumps.vue'; 2 | export default Dumps; 3 | -------------------------------------------------------------------------------- /src/views/Logs/EventLogs/index.js: -------------------------------------------------------------------------------- 1 | import EventLogs from './EventLogs.vue'; 2 | export default EventLogs; 3 | -------------------------------------------------------------------------------- /src/views/Logs/PostCodeLogs/index.js: -------------------------------------------------------------------------------- 1 | import PostCodeLogs from './PostCodeLogs.vue'; 2 | export default PostCodeLogs; 3 | -------------------------------------------------------------------------------- /src/views/Operations/FactoryReset/index.js: -------------------------------------------------------------------------------- 1 | import FactoryReset from './FactoryReset.vue'; 2 | export default FactoryReset; 3 | -------------------------------------------------------------------------------- /src/views/Operations/Firmware/Firmware.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 99 | -------------------------------------------------------------------------------- /src/views/Operations/Firmware/FirmwareAlertServerPower.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 58 | -------------------------------------------------------------------------------- /src/views/Operations/Firmware/FirmwareCardsBios.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 74 | 75 | 80 | -------------------------------------------------------------------------------- /src/views/Operations/Firmware/FirmwareModalSwitchToRunning.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 38 | -------------------------------------------------------------------------------- /src/views/Operations/Firmware/FirmwareModalUpdateFirmware.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 51 | -------------------------------------------------------------------------------- /src/views/Operations/Firmware/index.js: -------------------------------------------------------------------------------- 1 | import Firmware from './Firmware.vue'; 2 | export default Firmware; 3 | -------------------------------------------------------------------------------- /src/views/Operations/KeyClear/index.js: -------------------------------------------------------------------------------- 1 | import KeyClear from './KeyClear.vue'; 2 | export default KeyClear; 3 | -------------------------------------------------------------------------------- /src/views/Operations/Kvm/Kvm.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /src/views/Operations/Kvm/index.js: -------------------------------------------------------------------------------- 1 | import Kvm from './Kvm.vue'; 2 | export default Kvm; 3 | -------------------------------------------------------------------------------- /src/views/Operations/RebootBmc/RebootBmc.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/views/Operations/RebootBmc/index.js: -------------------------------------------------------------------------------- 1 | import RebootBmc from './RebootBmc.vue'; 2 | export default RebootBmc; 3 | -------------------------------------------------------------------------------- /src/views/Operations/SerialOverLan/SerialOverLan.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 31 | -------------------------------------------------------------------------------- /src/views/Operations/SerialOverLan/index.js: -------------------------------------------------------------------------------- 1 | import SerialOverLan from './SerialOverLan.vue'; 2 | export default SerialOverLan; 3 | -------------------------------------------------------------------------------- /src/views/Operations/ServerPowerOperations/index.js: -------------------------------------------------------------------------------- 1 | import ServerPowerOperations from './ServerPowerOperations.vue'; 2 | export default ServerPowerOperations; 3 | -------------------------------------------------------------------------------- /src/views/Operations/VirtualMedia/index.js: -------------------------------------------------------------------------------- 1 | import VirtualMedia from './VirtualMedia.vue'; 2 | export default VirtualMedia; 3 | -------------------------------------------------------------------------------- /src/views/Overview/OverviewCard.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 78 | 79 | 88 | -------------------------------------------------------------------------------- /src/views/Overview/OverviewDumps.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 61 | -------------------------------------------------------------------------------- /src/views/Overview/OverviewEvents.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 92 | 93 | 98 | -------------------------------------------------------------------------------- /src/views/Overview/OverviewFirmware.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 69 | -------------------------------------------------------------------------------- /src/views/Overview/OverviewInventory.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 67 | -------------------------------------------------------------------------------- /src/views/Overview/OverviewNetwork.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 78 | -------------------------------------------------------------------------------- /src/views/Overview/OverviewPower.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 55 | -------------------------------------------------------------------------------- /src/views/Overview/OverviewQuickLinks.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 57 | 58 | 64 | -------------------------------------------------------------------------------- /src/views/Overview/OverviewServer.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 63 | -------------------------------------------------------------------------------- /src/views/Overview/index.js: -------------------------------------------------------------------------------- 1 | import Overview from './Overview.vue'; 2 | export default Overview; 3 | -------------------------------------------------------------------------------- /src/views/PageNotFound/PageNotFound.vue: -------------------------------------------------------------------------------- 1 | 6 | 20 | -------------------------------------------------------------------------------- /src/views/PageNotFound/index.js: -------------------------------------------------------------------------------- 1 | import PageNotFound from './PageNotFound.vue'; 2 | export default PageNotFound; 3 | -------------------------------------------------------------------------------- /src/views/ProfileSettings/index.js: -------------------------------------------------------------------------------- 1 | import ProfileSettings from './ProfileSettings.vue'; 2 | export default ProfileSettings; 3 | -------------------------------------------------------------------------------- /src/views/ResourceManagement/index.js: -------------------------------------------------------------------------------- 1 | import Power from './Power.vue'; 2 | export default Power; 3 | -------------------------------------------------------------------------------- /src/views/SecurityAndAccess/Certificates/index.js: -------------------------------------------------------------------------------- 1 | import Certificates from './Certificates.vue'; 2 | export default Certificates; 3 | -------------------------------------------------------------------------------- /src/views/SecurityAndAccess/Ldap/index.js: -------------------------------------------------------------------------------- 1 | import Ldap from './Ldap.vue'; 2 | export default Ldap; 3 | -------------------------------------------------------------------------------- /src/views/SecurityAndAccess/Policies/index.js: -------------------------------------------------------------------------------- 1 | import Policies from './Policies.vue'; 2 | export default Policies; 3 | -------------------------------------------------------------------------------- /src/views/SecurityAndAccess/Sessions/index.js: -------------------------------------------------------------------------------- 1 | import Sessions from './Sessions.vue'; 2 | export default Sessions; 3 | -------------------------------------------------------------------------------- /src/views/SecurityAndAccess/UserManagement/index.js: -------------------------------------------------------------------------------- 1 | import UserManagement from './UserManagement.vue'; 2 | export default UserManagement; 3 | -------------------------------------------------------------------------------- /src/views/Settings/DateTime/index.js: -------------------------------------------------------------------------------- 1 | import DateTime from './DateTime.vue'; 2 | export default DateTime; 3 | -------------------------------------------------------------------------------- /src/views/Settings/Network/ModalDns.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 102 | -------------------------------------------------------------------------------- /src/views/Settings/Network/index.js: -------------------------------------------------------------------------------- 1 | import Network from './Network.vue'; 2 | export default Network; 3 | -------------------------------------------------------------------------------- /src/views/Settings/PowerRestorePolicy/index.js: -------------------------------------------------------------------------------- 1 | import PowerRestorePolicy from './PowerRestorePolicy.vue'; 2 | export default PowerRestorePolicy; 3 | -------------------------------------------------------------------------------- /src/views/Settings/SnmpAlerts/index.js: -------------------------------------------------------------------------------- 1 | import SnmpAlerts from './SnmpAlerts.vue'; 2 | export default SnmpAlerts; 3 | -------------------------------------------------------------------------------- /tests/unit/AppHeader.spec.js: -------------------------------------------------------------------------------- 1 | import { mount, createWrapper } from '@vue/test-utils'; 2 | import { createStore } from 'vuex'; 3 | import AppHeader from '@/components/AppHeader'; 4 | 5 | describe('AppHeader.vue', () => { 6 | const actions = { 7 | 'global/getServerStatus': jest.fn(), 8 | 'eventLog/getEventLogData': jest.fn(), 9 | 'authentication/resetStoreState': jest.fn(), 10 | 'global/getSystemInfo': jest.fn(), 11 | }; 12 | 13 | // VueX requires that all modules be present, even if they aren't used 14 | // in the test, so invent a Fake auth module and install it. 15 | const modules = { 16 | authentication: { 17 | namespaced: true, 18 | }, 19 | }; 20 | 21 | const store = createStore({ actions, modules }); 22 | const wrapper = mount(AppHeader, { 23 | store, 24 | mocks: { 25 | $t: (key) => key, 26 | }, 27 | }); 28 | 29 | // Reset dispatch between tests so that multiple 30 | // actions are not dispatched for each test 31 | beforeEach(() => { 32 | store.dispatch = jest.fn(); 33 | }); 34 | 35 | it('should exist', () => { 36 | expect(wrapper.exists()).toBe(true); 37 | }); 38 | 39 | it('should render correctly', () => { 40 | expect(wrapper.element).toMatchSnapshot(); 41 | }); 42 | 43 | it('refresh button click should emit refresh event', async () => { 44 | wrapper.get('#app-header-refresh').trigger('click'); 45 | await wrapper.vm.$nextTick(); 46 | expect(wrapper.emitted('refresh')).toBeTruthy(); 47 | }); 48 | 49 | it('nav-trigger button click should emit toggle-navigation event', async () => { 50 | const rootWrapper = createWrapper(wrapper.vm.$root); 51 | wrapper.get('#app-header-trigger').trigger('click'); 52 | await wrapper.vm.$nextTick(); 53 | expect(rootWrapper.emitted('toggle-navigation')).toBeTruthy(); 54 | }); 55 | 56 | it('logout button should dispatch authentication/logout', async () => { 57 | wrapper.get('[data-test-id="appHeader-link-logout"]').trigger('click'); 58 | await wrapper.vm.$nextTick(); 59 | expect(store.dispatch).toHaveBeenCalledTimes(1); 60 | }); 61 | 62 | it('change:isNavigationOpen event should set isNavigationOpen prop to false', async () => { 63 | const rootWrapper = createWrapper(wrapper.vm.$root); 64 | rootWrapper.vm.$emit('change-is-navigation-open', false); 65 | await rootWrapper.vm.$nextTick(); 66 | expect(wrapper.vm.isNavigationOpen).toEqual(false); 67 | }); 68 | 69 | describe('Created lifecycle hook', () => { 70 | it('getSystemInfo should dispatch global/getSystemInfo', () => { 71 | wrapper.vm.getSystemInfo(); 72 | expect(store.dispatch).toHaveBeenCalledTimes(1); 73 | }); 74 | 75 | it('getEvents should dispatch eventLog/getEventLogData', () => { 76 | wrapper.vm.getEvents(); 77 | expect(store.dispatch).toHaveBeenCalledTimes(1); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /tests/unit/AppNavigation.spec.js: -------------------------------------------------------------------------------- 1 | import { mount, createWrapper } from '@vue/test-utils'; 2 | import AppNavigation from '@/components/AppNavigation'; 3 | import { createApp } from 'vue'; 4 | import Vuex from 'vuex'; 5 | import VueRouter from 'vue-router'; 6 | //import { BootstrapVue } from 'bootstrap-vue'; 7 | 8 | describe('AppNavigation.vue', () => { 9 | let wrapper; 10 | const router = new VueRouter(); 11 | const actions = { 12 | 'global/userPrivilege': jest.fn(), 13 | }; 14 | const store = new Vuex.Store({ actions }); 15 | const app = createApp(); 16 | //app.use(BootstrapVue); 17 | app.use(VueRouter); 18 | 19 | wrapper = mount(AppNavigation, { 20 | store, 21 | router, 22 | mocks: { 23 | $t: (key) => key, 24 | }, 25 | }); 26 | 27 | it('should exist', async () => { 28 | expect(wrapper.exists()).toBe(true); 29 | }); 30 | 31 | it('should render correctly', () => { 32 | expect(wrapper.element).toMatchSnapshot(); 33 | }); 34 | 35 | it('should render with nav-container open', () => { 36 | wrapper.vm.isNavigationOpen = true; 37 | expect(wrapper.element).toMatchSnapshot(); 38 | }); 39 | 40 | it('Nav Overlay click should emit change-is-navigation-open event', async () => { 41 | const rootWrapper = createWrapper(wrapper.vm.$root); 42 | const navOverlay = wrapper.find('#nav-overlay'); 43 | navOverlay.trigger('click'); 44 | await wrapper.vm.$nextTick(); 45 | expect(rootWrapper.emitted('change-is-navigation-open')).toBeTruthy(); 46 | }); 47 | 48 | it('toggle-navigation event should toggle isNavigation data prop value', async () => { 49 | const rootWrapper = createWrapper(wrapper.vm.$root); 50 | wrapper.vm.isNavigationOpen = false; 51 | rootWrapper.vm.$emit('toggle-navigation'); 52 | expect(wrapper.vm.isNavigationOpen).toBe(true); 53 | rootWrapper.vm.$emit('toggle-navigation'); 54 | expect(wrapper.vm.isNavigationOpen).toBe(false); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /tests/unit/Global/InfoTooltip.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import InfoTooltip from '@/components/Global/InfoTooltip'; 3 | 4 | process.env.BOOTSTRAP_VUE_NO_WARN = true; 5 | 6 | describe('InfoTooltip.vue', () => { 7 | const wrapper = mount(InfoTooltip, { 8 | propsData: { 9 | title: 'A tooltip test title', 10 | }, 11 | mocks: { 12 | $t: (key) => key, 13 | }, 14 | }); 15 | it('should exist', () => { 16 | expect(wrapper.exists()).toBe(true); 17 | }); 18 | it('should render correctly', () => { 19 | expect(wrapper.element).toMatchSnapshot(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /tests/unit/Global/InputPasswordToggle.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import InputPasswordToggle from '@/components/Global/InputPasswordToggle'; 3 | 4 | describe('InputPasswordToggle.vue', () => { 5 | const wrapper = mount(InputPasswordToggle, { 6 | data() { 7 | return { 8 | isVisible: false, 9 | }; 10 | }, 11 | mocks: { 12 | $t: (key) => key, 13 | }, 14 | }); 15 | it('should exist', () => { 16 | expect(wrapper.exists()).toBe(true); 17 | }); 18 | it('should not render isVisible class', () => { 19 | expect(wrapper.find('.isVisible').exists()).toBe(false); 20 | }); 21 | it('should render isVisible class when button is clicked', async () => { 22 | await wrapper.find('button').trigger('click'); 23 | expect(wrapper.find('.isVisible').exists()).toBe(true); 24 | }); 25 | it('should render correctly', () => { 26 | expect(wrapper.element).toMatchSnapshot(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /tests/unit/Global/LoadingBar.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import LoadingBar from '@/components/Global/LoadingBar'; 3 | 4 | describe('LoadingBar.vue', () => { 5 | const wrapper = mount(LoadingBar, { 6 | data() { 7 | return { 8 | loadingIndicatorValue: 0, 9 | isLoadingComplete: false, 10 | }; 11 | }, 12 | mocks: { 13 | $t: (key) => key, 14 | }, 15 | }); 16 | it('should exist', () => { 17 | expect(wrapper.exists()).toBe(true); 18 | }); 19 | it('should show loading bar element', async () => { 20 | await wrapper.setData({ 21 | isLoadingComplete: false, 22 | loadingIndicatorValue: 100, 23 | }); 24 | expect(wrapper.vm.isLoadingComplete).toBe(false); 25 | expect(wrapper.find('.progress').exists()).toBe(true); 26 | }); 27 | it('should hide loading bar element', async () => { 28 | await wrapper.setData({ 29 | isLoadingComplete: true, 30 | loadingIndicatorValue: 0, 31 | }); 32 | expect(wrapper.vm.isLoadingComplete).toBe(true); 33 | expect(wrapper.find('.progress').exists()).toBe(false); 34 | }); 35 | it('should render correctly', () => { 36 | expect(wrapper.element).toMatchSnapshot(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /tests/unit/Global/PageContainer.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import PageContainer from '@/components/Global/PageContainer'; 3 | 4 | describe('PageContainer.vue', () => { 5 | const wrapper = mount(PageContainer, { 6 | mocks: { 7 | $t: (key) => key, 8 | }, 9 | }); 10 | it('should exist', () => { 11 | expect(wrapper.exists()).toBe(true); 12 | }); 13 | it('should render main element', () => { 14 | expect(wrapper.find('main').exists()).toBe(true); 15 | }); 16 | it('should render correctly', () => { 17 | expect(wrapper.element).toMatchSnapshot(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/unit/Global/PageSection.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import PageSection from '@/components/Global/PageSection'; 3 | 4 | describe('PageSection.vue', () => { 5 | const wrapper = mount(PageSection, { 6 | propsData: { 7 | sectionTitle: 'PageSection test title', 8 | }, 9 | mocks: { 10 | $t: (key) => key, 11 | }, 12 | }); 13 | it('should exist', () => { 14 | expect(wrapper.exists()).toBe(true); 15 | }); 16 | it('should render h2 element', () => { 17 | expect(wrapper.find('h2').exists()).toBe(true); 18 | }); 19 | it('should render correctly', () => { 20 | expect(wrapper.element).toMatchSnapshot(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/unit/Global/PageTitle.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import PageTitle from '@/components/Global/PageTitle'; 3 | 4 | describe('PageTitle.vue', () => { 5 | const wrapper = mount(PageTitle, { 6 | propsData: { 7 | description: 'A page title test description', 8 | }, 9 | mocks: { 10 | $t: (key) => key, 11 | $route: { 12 | meta: { 13 | title: 'Page Title', 14 | }, 15 | }, 16 | }, 17 | }); 18 | it('should exist', () => { 19 | expect(wrapper.exists()).toBe(true); 20 | }); 21 | it('should render h1 element', () => { 22 | expect(wrapper.find('h1').exists()).toBe(true); 23 | }); 24 | it('should render p element', () => { 25 | expect(wrapper.find('p').exists()).toBe(true); 26 | }); 27 | it('should render correctly', () => { 28 | expect(wrapper.element).toMatchSnapshot(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /tests/unit/Global/Search.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import Search from '@/components/Global/Search'; 3 | 4 | describe('Search.vue', () => { 5 | const wrapper = mount(Search, { 6 | mocks: { 7 | $t: (key) => key, 8 | }, 9 | }); 10 | it('should exist', () => { 11 | expect(wrapper.exists()).toBe(true); 12 | }); 13 | it('should emit change-search on triggering onChangeInput', () => { 14 | wrapper.find('input').trigger('input'); 15 | expect(wrapper.emitted('change-search')).toHaveLength(1); 16 | }); 17 | it('should emit clear-search on triggering onClearSearch', async () => { 18 | await wrapper.setData({ filter: 'true' }); 19 | wrapper.find('button').trigger('click'); 20 | expect(wrapper.emitted('clear-search')).toHaveLength(1); 21 | }); 22 | it('should render correctly', () => { 23 | expect(wrapper.element).toMatchSnapshot(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/unit/Global/StatusIcon.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import StatusIcon from '@/components/Global/StatusIcon'; 3 | 4 | describe('StatusIcon.vue', () => { 5 | const wrapper = mount(StatusIcon, { 6 | propsData: { 7 | status: 'info', 8 | }, 9 | }); 10 | it('should exist', () => { 11 | expect(wrapper.exists()).toBe(true); 12 | }); 13 | it('should render icon-info element', () => { 14 | expect(wrapper.find('.info').exists()).toBe(true); 15 | }); 16 | it('should render icon-success element', async () => { 17 | await wrapper.setProps({ status: 'success' }); 18 | expect(wrapper.find('.success').exists()).toBe(true); 19 | }); 20 | it('should render icon-warning element', async () => { 21 | await wrapper.setProps({ status: 'warning' }); 22 | expect(wrapper.find('.warning').exists()).toBe(true); 23 | }); 24 | it('should render icon-danger element', async () => { 25 | await wrapper.setProps({ status: 'danger' }); 26 | expect(wrapper.find('.danger').exists()).toBe(true); 27 | }); 28 | it('should render icon-secondary element', async () => { 29 | await wrapper.setProps({ status: 'secondary' }); 30 | expect(wrapper.find('.status-icon').exists()).toBe(true); 31 | }); 32 | it('should render correctly', () => { 33 | expect(wrapper.element).toMatchSnapshot(); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/unit/Global/TableCellCount.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import TableCellCount from '@/components/Global/TableCellCount'; 3 | 4 | describe('TableCellCount.vue', () => { 5 | const wrapper = mount(TableCellCount, { 6 | propsData: { 7 | filteredItemsCount: 5, 8 | totalNumberOfCells: 100, 9 | }, 10 | mocks: { 11 | $t: (key) => key, 12 | }, 13 | }); 14 | it('should exist', () => { 15 | expect(wrapper.exists()).toBe(true); 16 | }); 17 | it('should render filtered and totalnumber of items', () => { 18 | expect(wrapper.text()).toContain('global.table.selectedItems'); 19 | }); 20 | it('should render only totalnumber of items', async () => { 21 | await wrapper.setProps({ filteredItemsCount: 5, totalNumberOfCells: 5 }); 22 | expect(wrapper.text()).toContain('global.table.items'); 23 | }); 24 | it('should render correctly', () => { 25 | expect(wrapper.element).toMatchSnapshot(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /tests/unit/Global/TableToolbar.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import TableToolbar from '@/components/Global/TableToolbar'; 3 | 4 | describe('TableToolbar.vue', () => { 5 | const wrapper = mount(TableToolbar, { 6 | propsData: { 7 | selectedItemsCount: 0, 8 | }, 9 | mocks: { 10 | $t: (key) => key, 11 | }, 12 | }); 13 | it('should exist', () => { 14 | expect(wrapper.exists()).toBe(true); 15 | }); 16 | it('should render class toolbar-container when selectedItemsCount is greater than 0', async () => { 17 | await wrapper.setProps({ selectedItemsCount: 12 }); 18 | expect(wrapper.find('.toolbar-container').exists()).toBe(true); 19 | }); 20 | it('should render correctly', () => { 21 | expect(wrapper.element).toMatchSnapshot(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/InfoTooltip.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`InfoTooltip.vue should render correctly 1`] = ` 4 | 33 | `; 34 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/InputPasswordToggle.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`InputPasswordToggle.vue should render correctly 1`] = ` 4 |
7 | 8 | 9 | 14 | 31 | 34 | Show password as plain text. Note: this will visually expose your password on the screen. 35 | 36 | 37 |
38 | `; 39 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/LoadingBar.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`LoadingBar.vue should render correctly 1`] = ` 4 | 7 | 8 | 9 | `; 10 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/PageContainer.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`PageContainer.vue should render correctly 1`] = ` 4 |
8 | `; 9 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/PageSection.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`PageSection.vue should render correctly 1`] = ` 4 |
7 |

8 | PageSection test title 9 |

10 | 11 | 12 |
13 | `; 14 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/PageTitle.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`PageTitle.vue should render correctly 1`] = ` 4 |
7 |

8 | Page Title 9 |

10 | 11 |

12 | A page title test description 13 |

14 |
15 | `; 16 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/Search.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Search.vue should render correctly 1`] = ` 4 |
7 |
12 | 19 |
20 |
24 | 25 |
28 | 43 |
44 | 45 | 52 | 53 | 54 | 55 |
56 | 57 | 58 | 59 |
60 |
61 |
62 | `; 63 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/StatusIcon.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`StatusIcon.vue should render correctly 1`] = ` 4 | 7 | 26 | 27 | `; 28 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/TableCellCount.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`TableCellCount.vue should render correctly 1`] = ` 4 |
7 |

8 | 9 | global.table.items 10 | 11 |

12 |
13 | `; 14 | -------------------------------------------------------------------------------- /tests/unit/Global/__snapshots__/TableToolbar.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`TableToolbar.vue should render correctly 1`] = ` 4 | 7 |
10 |
13 |

16 | 17 | 12 global.action.selected 18 | 19 |

20 | 21 |
24 | 25 | 33 |
34 |
35 |
36 |
37 | `; 38 | --------------------------------------------------------------------------------