├── .eslintrc.js ├── .github ├── CODEOWNERS ├── pull_request_template.md └── workflows │ ├── check-versions.yml │ ├── master.yml │ ├── publish-gh-registry.yml │ └── publish.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── .yarnrc ├── CHANGELOG.md ├── LICENSE ├── MIGRATION.md ├── README.md ├── app-config.yaml ├── backstage.json ├── config.d.ts ├── custom.d.ts ├── dev └── index.tsx ├── docs ├── homepage.png ├── screen1.png ├── screen2.png └── screen3.png ├── package.json ├── rollup.config.js ├── scripts ├── add_publish_to_github_config.sh ├── diff.patch ├── download_backstage.sh └── run_branch.sh ├── src ├── api │ ├── CortexApi.ts │ ├── CortexClient.test.ts │ ├── CortexClient.ts │ ├── ExtensionApi.ts │ ├── NoopExtensionClient.ts │ ├── index.ts │ ├── types.ts │ └── userInsightTypes.ts ├── assets │ └── cortex.icon.svg ├── components │ ├── Common │ │ ├── CopyButton.tsx │ │ ├── CortexInfoCard.tsx │ │ ├── HoverTimestamp.tsx │ │ ├── LinearProgressWithLabel.tsx │ │ ├── PollingLinearGauge.tsx │ │ ├── ScorecardLadderLevelBadge.tsx │ │ ├── SortDropdown.tsx │ │ ├── Stats.tsx │ │ ├── StatsItem.tsx │ │ ├── Truncated.tsx │ │ └── useLinearProgressWithLabelStyles.ts │ ├── CortexGroupActionItemsWidget │ │ ├── CortexGroupActionItemsWidget.test.tsx │ │ ├── CortexGroupActionItemsWidget.tsx │ │ ├── GroupComponentActionItemsRow.tsx │ │ ├── GroupComponentRuleInitiativeInfo.tsx │ │ ├── GroupComponentRuleInitiativesRow.tsx │ │ └── index.ts │ ├── CortexLayout │ │ ├── CortexLayout.test.tsx │ │ ├── CortexLayout.tsx │ │ └── index.ts │ ├── CortexPage │ │ ├── CortexPage.tsx │ │ └── index.ts │ ├── CortexScorecardWidget │ │ ├── CortexScorecardWidget.test.tsx │ │ ├── CortexScorecardWidget.tsx │ │ └── index.ts │ ├── DefaultEntityLink │ │ ├── DefaultEntityRefLink.tsx │ │ └── index.ts │ ├── Entitlements │ │ ├── ExpirationBanner.tsx │ │ ├── ExpirationUtils.test.ts │ │ └── ExpirationUtils.ts │ ├── EntityPage │ │ ├── EntityInitiativesCard.tsx │ │ ├── EntityPage.test.tsx │ │ ├── EntityPage.tsx │ │ ├── EntityScorecardDetails │ │ │ ├── EntityScorecardDetails.tsx │ │ │ └── index.tsx │ │ ├── EntityScorecardOverview │ │ │ ├── EntityScorecardOverview.test.tsx │ │ │ ├── EntityScorecardOverview.tsx │ │ │ └── index.ts │ │ ├── EntityScorecardRules │ │ │ ├── EntityScorecardRules.test.tsx │ │ │ ├── EntityScorecardRules.tsx │ │ │ └── index.ts │ │ ├── EntityScorecardsCard.tsx │ │ ├── EntityScorecardsCardRow.tsx │ │ └── index.ts │ ├── FilterCard │ │ ├── FilterCard.tsx │ │ ├── Filters.tsx │ │ ├── index.ts │ │ └── useFilter.tsx │ ├── Gauge │ │ ├── Gauge.tsx │ │ └── index.ts │ ├── HelpPage │ │ ├── HelpLinksContent.tsx │ │ ├── HelpPage.tsx │ │ └── index.ts │ ├── Homepage │ │ ├── Homepage.tsx │ │ ├── HomepageInsightCard.tsx │ │ ├── HomepageInsights.tsx │ │ ├── HomepageOncallCard.tsx │ │ ├── HomepageUtils.tsx │ │ ├── InsightCard.tsx │ │ └── index.ts │ ├── Initiatives │ │ ├── InitiativeCard │ │ │ ├── InitiativeCard.tsx │ │ │ └── index.ts │ │ ├── InitiativeDetailsPage │ │ │ ├── InitativeFilterDialog │ │ │ │ ├── InitiativeFilterDialog.tsx │ │ │ │ ├── InitiativeFilterDialogUtils.test.ts │ │ │ │ ├── InitiativeFilterDialogUtils.ts │ │ │ │ └── index.ts │ │ │ ├── InitiativeDetailsPage.test.tsx │ │ │ ├── InitiativeDetailsPage.tsx │ │ │ ├── InitiativeFailingTab │ │ │ │ ├── InitiativeFailingTab.tsx │ │ │ │ ├── InitiativeFailingTabConfig.ts │ │ │ │ ├── ServiceNameAndRulesColumn.tsx │ │ │ │ └── index.ts │ │ │ ├── InitiativeFilterForm │ │ │ │ └── InitiativeFilterForm.tsx │ │ │ ├── InitiativeLevelsTab │ │ │ │ ├── InitiativeLevelsTab.tsx │ │ │ │ └── index.ts │ │ │ ├── InitiativeMetadataCard │ │ │ │ ├── InitiativeMetadataCard.tsx │ │ │ │ ├── InitiativeMetadataCardUtils.ts │ │ │ │ ├── InitiativeMetadataFilter.tsx │ │ │ │ └── index.ts │ │ │ ├── InitiativePassingTab │ │ │ │ ├── InitiativePassingTab.tsx │ │ │ │ ├── InitiativePassingTabConfig.ts │ │ │ │ ├── ServiceNameColumn.tsx │ │ │ │ └── index.ts │ │ │ ├── InitiativeRulesTab │ │ │ │ ├── InitiativeRulesTab.tsx │ │ │ │ └── index.ts │ │ │ ├── InitiativeStatsCard │ │ │ │ ├── InitiativeStatsCard.test.tsx │ │ │ │ ├── InitiativeStatsCard.tsx │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── InitiativeStatsCard.test.tsx.snap │ │ │ │ └── index.tsx │ │ │ └── index.ts │ │ ├── InitiativesPage.test.tsx │ │ └── InitiativesPage │ │ │ ├── InitiativesList.tsx │ │ │ ├── InitiativesPage.tsx │ │ │ └── index.ts │ ├── ItemCardHeader │ │ ├── ItemCardHeader.tsx │ │ └── index.ts │ ├── ListCard │ │ ├── ListCard.tsx │ │ └── index.ts │ ├── MetadataItem │ │ ├── MetadataItem.tsx │ │ └── index.ts │ ├── ReportsPage │ │ ├── AllScorecardsPage │ │ │ ├── AllScorecardsHeatmap.tsx │ │ │ ├── AllScorecardsPage.tsx │ │ │ ├── HeatmapCell.tsx │ │ │ ├── HeatmapFilters.tsx │ │ │ ├── HeatmapFiltersModal.tsx │ │ │ ├── HeatmapFiltersSelect.tsx │ │ │ ├── HeatmapUtils.ts │ │ │ ├── LevelsInfoCell.tsx │ │ │ └── Tables │ │ │ │ ├── AllScorecardHeatmapTable.tsx │ │ │ │ ├── HeatmapTableByGroup.tsx │ │ │ │ ├── HeatmapTableByLevels.tsx │ │ │ │ ├── HeatmapTableByService.tsx │ │ │ │ ├── HeatmapTableHeader.tsx │ │ │ │ ├── HierarchyBreadcrumbs.tsx │ │ │ │ ├── LevelsDrivenTable.tsx │ │ │ │ ├── SingleScorecardHeatmapTable.tsx │ │ │ │ └── colorClasses.ts │ │ ├── Common │ │ │ ├── GroupByDropdown.tsx │ │ │ ├── HeaderTypeDropdown.tsx │ │ │ └── LookbackDropdown.tsx │ │ ├── HeatmapPage │ │ │ ├── HeatmapFiltersModal.tsx │ │ │ ├── HeatmapFiltersSelect.tsx │ │ │ ├── HeatmapPage.test.tsx │ │ │ ├── HeatmapPage.tsx │ │ │ ├── HeatmapSettings.tsx │ │ │ ├── HeatmapTable.tsx │ │ │ ├── HeatmapUtils.test.ts │ │ │ ├── HeatmapUtils.tsx │ │ │ ├── colorClasses.ts │ │ │ └── index.ts │ │ ├── ProgressPage │ │ │ ├── AggregatedScorecardProgress.tsx │ │ │ ├── ProgressPage.tsx │ │ │ ├── SerieFilter.tsx │ │ │ └── index.ts │ │ ├── ReportsPage.tsx │ │ ├── ScorecardSelector.tsx │ │ └── index.ts │ ├── ScorecardRefLink │ │ ├── ScorecardRefLink.tsx │ │ └── index.ts │ ├── ScorecardServiceRefLink │ │ ├── ScorecardServiceRefLink.tsx │ │ └── index.ts │ ├── Scorecards │ │ ├── ScorecardCard │ │ │ ├── ScorecardCard.tsx │ │ │ └── index.ts │ │ ├── ScorecardDetailsPage │ │ │ ├── CopyCsvButton.tsx │ │ │ ├── ScorecardDetails.tsx │ │ │ ├── ScorecardDetailsPage.test.tsx │ │ │ ├── ScorecardDetailsPage.tsx │ │ │ ├── ScorecardFilterDialog │ │ │ │ ├── ScorecardFilterDialog.tsx │ │ │ │ ├── ScorecardFilterDialogUtils.tsx │ │ │ │ └── index.ts │ │ │ ├── ScorecardMetadataCard │ │ │ │ ├── ScorecardMetadataCard.test.tsx │ │ │ │ ├── ScorecardMetadataCard.tsx │ │ │ │ ├── ScorecardMetadataFilter.tsx │ │ │ │ ├── ScorecardMetadataUtils.tsx │ │ │ │ └── index.ts │ │ │ ├── ScorecardReportsTab │ │ │ │ ├── ScorecardReportsTab.tsx │ │ │ │ └── index.ts │ │ │ ├── ScorecardRuleExemptionsTab │ │ │ │ ├── RuleExemptionStatus.tsx │ │ │ │ ├── ScorecardRuleExemptionTooltip.tsx │ │ │ │ ├── ScorecardRuleExemptionsTab.tsx │ │ │ │ ├── ScorecardRuleExemptionsTabUtils.ts │ │ │ │ └── index.ts │ │ │ ├── ScorecardRulesTab │ │ │ │ ├── ScorecardLadderRulesCard.tsx │ │ │ │ ├── ScorecardRuleRow.tsx │ │ │ │ ├── ScorecardScoresRulesCard.tsx │ │ │ │ └── index.ts │ │ │ ├── ScorecardStatsCard │ │ │ │ ├── ScorecardLadderStatsCard.tsx │ │ │ │ ├── ScorecardScoresStatsCard.tsx │ │ │ │ └── index.ts │ │ │ ├── ScorecardsScoresTab │ │ │ │ ├── ScorecardsScoresTable.tsx │ │ │ │ ├── ScorecardsScoresTableConfig.tsx │ │ │ │ └── index.ts │ │ │ ├── ScorecardsTableCard │ │ │ │ ├── RuleResultDetails.tsx │ │ │ │ ├── ScorecardResultDetails.test.tsx │ │ │ │ ├── ScorecardResultDetails.tsx │ │ │ │ └── __snapshots__ │ │ │ │ │ └── ScorecardResultDetails.test.tsx.snap │ │ │ └── index.ts │ │ ├── ScorecardsPage │ │ │ ├── ScorecardList.tsx │ │ │ ├── ScorecardsPage.test.tsx │ │ │ ├── ScorecardsPage.tsx │ │ │ └── index.ts │ │ └── ScorecardsServicePage │ │ │ ├── ScorecardsServiceHeader.tsx │ │ │ ├── ScorecardsServiceNextRules.tsx │ │ │ ├── ScorecardsServiceOverallScoreProgress.tsx │ │ │ ├── ScorecardsServicePage.tsx │ │ │ ├── ScorecardsServiceProgress.tsx │ │ │ ├── ScorecardsServiceRuleProgress.tsx │ │ │ ├── ScorecardsServiceRuleProgressUtils.ts │ │ │ ├── ScorecardsServiceRuleRow.tsx │ │ │ ├── ScorecardsServiceRulesDetails.tsx │ │ │ ├── ScorecardsServiceRulesFilter.tsx │ │ │ ├── ScorecardsServiceStatistics.tsx │ │ │ ├── ScorecardsServiceTooltipRuleRow.tsx │ │ │ ├── index.ts │ │ │ └── useScorecardServiceRuleRowStyles.ts │ ├── SettingsPage │ │ ├── SettingsPage.test.tsx │ │ ├── SettingsPage.tsx │ │ ├── SyncCard.tsx │ │ ├── SyncJobsTable.test.tsx │ │ ├── SyncJobsTable.tsx │ │ └── index.ts │ ├── StatsCard │ │ ├── StatsCard.tsx │ │ └── index.ts │ ├── SystemPage │ │ ├── SystemPage.test.tsx │ │ ├── SystemPage.tsx │ │ ├── SystemPageRow.tsx │ │ └── SystemPageRowDetails.tsx │ └── Timeseries │ │ ├── Timeseries.tsx │ │ └── index.ts ├── extensions.tsx ├── filters.ts ├── index.ts ├── plugin.test.ts ├── plugin.ts ├── routes.ts ├── setupTests.ts ├── styles │ └── styles.ts └── utils │ ├── ComponentUtils.test.ts │ ├── ComponentUtils.ts │ ├── HookUtils.ts │ ├── MathUtils.ts │ ├── MomentUtils.ts │ ├── NumberUtils.test.ts │ ├── NumberUtils.ts │ ├── ScorecardFilterUtils.ts │ ├── ScorecardLadderUtils.ts │ ├── ScorecardRules.ts │ ├── SearchUtils.ts │ ├── TestUtils.tsx │ ├── URLUtils.test.ts │ ├── URLUtils.ts │ ├── WindowUtils.ts │ ├── collections.ts │ ├── dates.ts │ ├── hooks.ts │ ├── lookback.ts │ ├── pureHooks.ts │ ├── strings.ts │ ├── types.test.ts │ └── types.ts ├── tsconfig.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | module.exports = { 17 | extends: ['@cortexapps/eslint-config-oss'], 18 | ignorePatterns: ['dist/*', 'dist-types/*', 'node_modules/*'], 19 | rules: { 20 | "eol-last": ["error", "always"], 21 | semi: ["error", "always"] 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # These owners will be the default owners for everything in 5 | # the repo. Unless a later match takes precedence, 6 | # @global-owner1 and @global-owner2 will be requested for 7 | # review when someone opens a pull request. 8 | * @cristinabuenahora @FoodProduct @browniefed @igorRoog @PavelZurek 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Change description 2 | 3 | > Description here 4 | 5 | ## Type of change 6 | 7 | - [ ] Bug fix (fixes an issue) 8 | - [ ] New feature (adds functionality) 9 | - [ ] Improvement (improves codebase without changing functionality) 10 | 11 | ## Checklists 12 | 13 | ### Development 14 | 15 | - [ ] The changelog has been updated as appropriate 16 | - [ ] Lint rules pass locally 17 | - [ ] Application changes have been tested thoroughly 18 | - [ ] Automated tests covering modified code pass 19 | 20 | ### Security 21 | 22 | - [ ] Security impact of change has been considered 23 | - [ ] Code follows company security practices and guidelines 24 | 25 | ### Code review 26 | 27 | - [ ] Pull request has a descriptive title and context useful to a reviewer. Screenshots or screencasts are attached as necessary 28 | - [ ] "Ready for review" label attached and reviewers assigned 29 | - [ ] Changes have been reviewed by at least one other contributor 30 | - [ ] Pull request linked to task tracker where applicable 31 | -------------------------------------------------------------------------------- /.github/workflows/check-versions.yml: -------------------------------------------------------------------------------- 1 | name: Backstage Version Check 2 | 3 | on: 4 | schedule: 5 | - cron: '30 17 * * 1-5' 6 | 7 | jobs: 8 | build: 9 | name: backstage-version-check 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Use Node.js ${{ matrix.node-version }} 14 | uses: actions/setup-node@v3 15 | with: 16 | node-version: 20.10.0 17 | - run: yarn install 18 | - name: Check Backstage versions 19 | id: check-versions 20 | run: | 21 | yarn backstage-cli versions:check | awk 'NR > 3 { print last} {last=$0}' | sed -e 's/ @/ • @/g' | awk '{printf "%s\\n", $0}' | tee version_check.txt 22 | RC=( "${PIPESTATUS[@]}" ) 23 | if [ "${RC[0]}" -ne "0" ]; then 24 | echo "::set-output name=failure::true" 25 | echo "::set-output name=failure_message::$(cat version_check.txt)" 26 | else 27 | echo "::set-output name=failure::false" 28 | fi 29 | - run: cat version_check.txt 30 | - if: steps.check-versions.outputs.failure == 'true' 31 | name: Alert on Slack 32 | uses: slackapi/slack-github-action@v1.17.0 33 | with: 34 | channel-id: '#project-backstage' 35 | payload: | 36 | { 37 | "text": "Detected out of date Backstage dependencies", 38 | "blocks": [ 39 | { 40 | "type": "section", 41 | "text": { 42 | "type": "mrkdwn", 43 | "text": "${{ steps.check-versions.outputs.failure_message }}" 44 | } 45 | } 46 | ] 47 | } 48 | env: 49 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_ERROR_REPORTING_BOT_TOKEN }} 50 | -------------------------------------------------------------------------------- /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | env: 10 | PACKAGES_REGISTRY_GITHUB_AUTH_TOKEN: ${{ secrets.READ_PACKAGES_GITHUB_TOKEN }} 11 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [20.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: yarn --frozen-lockfile 28 | - run: yarn tsc 29 | - run: yarn lint 30 | - run: yarn test 31 | - run: yarn build 32 | env: 33 | CI: true 34 | -------------------------------------------------------------------------------- /.github/workflows/publish-gh-registry.yml: -------------------------------------------------------------------------------- 1 | name: publish to github registry 2 | env: 3 | GITHUB_PASSWORD: ${{secrets.GITHUB_TOKEN}} 4 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 5 | 6 | on: 7 | pull_request: 8 | types: [labeled, opened, synchronize, reopened] 9 | 10 | jobs: 11 | publish: 12 | if: contains(github.event.pull_request.labels.*.name, 'internal-build') 13 | name: publish 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@main 18 | - name: Set up Node.js 19 | uses: actions/setup-node@main 20 | with: 21 | node-version: 20.10.0 22 | registry-url: 'https://registry.npmjs.org' 23 | - name: Configure Git 24 | run: | 25 | git config --global user.name "github-actions[bot]" 26 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 27 | - run: yarn --frozen-lockfile 28 | - run: yarn tsc 29 | - run: yarn build 30 | - run: ./scripts/add_publish_to_github_config.sh 31 | - run: | 32 | yarn version --new-version $(jq -r .version < package.json)-${GITHUB_SHA} 33 | yarn publish --non-interactive 34 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | env: 3 | PACKAGES_REGISTRY_GITHUB_AUTH_TOKEN: ${{ secrets.READ_PACKAGES_GITHUB_TOKEN }} 4 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 5 | on: 6 | push: 7 | branches: 8 | - master 9 | jobs: 10 | publish: 11 | name: publish 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@main 16 | - name: Set up Node.js 17 | uses: actions/setup-node@main 18 | with: 19 | node-version: 20.10.0 20 | registry-url: 'https://registry.npmjs.org' 21 | - run: yarn --frozen-lockfile 22 | - run: yarn tsc 23 | - run: yarn build 24 | - name: Disable GitHub registry 25 | run: | 26 | mv .npmrc _.npmrc 27 | cat _.npmrc | sed -E 's/(@cortexapps:registry=.*)/# \1/' >> .npmrc 28 | rm _.npmrc 29 | - run: yarn publish 30 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmjs.org/ 2 | engine-strict=true -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | registry "http://registry.npmjs.org/" 2 | network-timeout 300000 3 | -------------------------------------------------------------------------------- /MIGRATION.md: -------------------------------------------------------------------------------- 1 | # Migration Guide 2 | 3 | ## Migrating from 1.x.x to 2.x.x 4 | 5 | `allowedHeaders: ['x-cortex-email', 'x-cortex-name']` is now required in the proxy config: 6 | 7 | Update [app-config.yaml](https://github.com/backstage/backstage/blob/master/app-config.yaml#L54) to add a new config under 8 | the `proxy` section: 9 | 10 | ```yaml 11 | '/cortex': 12 | target: ${CORTEX_BACKEND_HOST_URL} 13 | headers: 14 | Authorization: Bearer ${CORTEX_TOKEN} 15 | allowedHeaders: ['x-cortex-email', 'x-cortex-name'] 16 | ``` 17 | -------------------------------------------------------------------------------- /app-config.yaml: -------------------------------------------------------------------------------- 1 | # Stub app config needed to run with yarn start 2 | app: 3 | title: backstage example app 4 | baseUrl: http://localhost:3000 5 | -------------------------------------------------------------------------------- /backstage.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.27.7" 3 | } 4 | -------------------------------------------------------------------------------- /custom.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | declare module '*.svg' { 17 | const content: any; 18 | export default content; 19 | } 20 | -------------------------------------------------------------------------------- /dev/index.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { createDevApp } from '@backstage/dev-utils'; 17 | import { cortexPlugin } from '../src/plugin'; 18 | import React from 'react'; 19 | import { CortexPage } from '../src/components/CortexPage'; 20 | 21 | createDevApp() 22 | .registerPlugin(cortexPlugin as any) 23 | .addPage({ 24 | element: , 25 | title: 'Cortex', 26 | }) 27 | .render(); 28 | -------------------------------------------------------------------------------- /docs/homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cortexapps/backstage-plugin/49b0290dae995baf64a9c150e04236bf0bf12be0/docs/homepage.png -------------------------------------------------------------------------------- /docs/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cortexapps/backstage-plugin/49b0290dae995baf64a9c150e04236bf0bf12be0/docs/screen1.png -------------------------------------------------------------------------------- /docs/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cortexapps/backstage-plugin/49b0290dae995baf64a9c150e04236bf0bf12be0/docs/screen2.png -------------------------------------------------------------------------------- /docs/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cortexapps/backstage-plugin/49b0290dae995baf64a9c150e04236bf0bf12be0/docs/screen3.png -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* eslint-disable import/no-extraneous-dependencies */ 17 | import resolve from '@rollup/plugin-node-resolve'; 18 | import esbuild from 'rollup-plugin-esbuild'; 19 | import peerDepsExternal from 'rollup-plugin-peer-deps-external'; 20 | import commonjs from '@rollup/plugin-commonjs'; 21 | import dts from 'rollup-plugin-dts'; 22 | 23 | // eslint-disable-next-line import/no-anonymous-default-export 24 | export default [ 25 | { 26 | input: 'src/index.ts', 27 | output: { 28 | dir: 'dist', 29 | entryFileNames: 'index.esm.js', 30 | chunkFileNames: 'esm/[name]-[hash].js', 31 | format: 'module', 32 | }, 33 | preserveEntrySignatures: 'strict', 34 | external: require('module').builtinModules, 35 | 36 | plugins: [ 37 | peerDepsExternal({ 38 | includeDependencies: true, 39 | }), 40 | commonjs({ 41 | include: ['node_modules/**', '../../node_modules/**'], 42 | exclude: ['**/*.stories.*', '**/*.test.*'], 43 | }), 44 | esbuild({ 45 | target: 'es2019', 46 | }), 47 | resolve({ 48 | mainFields: ['browser', 'module', 'main'], 49 | }), 50 | ], 51 | }, 52 | 53 | { 54 | input: 'dist-types/src/index.d.ts', 55 | output: { 56 | file: 'dist/index.d.ts', 57 | format: 'es', 58 | }, 59 | plugins: [dts()], 60 | }, 61 | ]; 62 | -------------------------------------------------------------------------------- /scripts/add_publish_to_github_config.sh: -------------------------------------------------------------------------------- 1 | echo " 2 | //npm.pkg.github.com/:_authToken=${GITHUB_PASSWORD} 3 | @cortexapps:registry=https://npm.pkg.github.com 4 | engine-strict=true 5 | " > .npmrc 6 | -------------------------------------------------------------------------------- /scripts/download_backstage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | 3 | set timeout -1 4 | 5 | spawn npx @backstage/create-app 6 | 7 | expect "Enter a name for the app" 8 | send -- "backstage-cortex\r" 9 | 10 | expect "Select database for the backend" 11 | send -- "\r" 12 | 13 | expect eof 14 | -------------------------------------------------------------------------------- /scripts/run_branch.sh: -------------------------------------------------------------------------------- 1 | command -v expect > /dev/null || { echo 'Script requires "expect" to run.'; exit 1; } 2 | command -v npx > /dev/null || { echo 'Script requires "npx" to run.'; exit 1; } 3 | 4 | if [ ! -d '/usr/local/opt/nvm' ] ; then 5 | echo 'Script requires "nvm" to run.' 6 | exit 1 7 | else 8 | source '/usr/local/opt/nvm/nvm.sh' 9 | fi 10 | 11 | script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 12 | plugin_dir=$(echo $script_dir | sed -e 's/\/scripts//g') 13 | backstage_dir="${script_dir}/backstage-cortex" 14 | 15 | cd $plugin_dir 16 | plugin_version=$(cat package.json | python -c "import sys; import json; value = json.loads(sys.stdin.read()); print(value[\"version\"])") 17 | echo "Building and publishing backstage-plugin@${plugin_version}" 18 | 19 | if ! (nvm install 20.10.0 && yarn install && yarn tsc && yarn build && yalc publish --push) ; then 20 | echo 'Failed to build plugin.' 21 | exit 1 22 | fi 23 | 24 | if [ ! -d "${backstage_dir}" ] ; then 25 | echo 'Downloading Backstage' 26 | cd $script_dir 27 | ./download_backstage.sh 28 | cd backstage-cortex 29 | echo "export { cortexPlugin } from '@cortexapps/backstage-plugin';" > packages/app/src/plugins.ts 30 | patch -p1 < ../diff.patch 31 | yalc add "@cortexapps/backstage-plugin@${plugin_version}" --pure 32 | nvm install 20.10.0 33 | yarn install 34 | fi 35 | -------------------------------------------------------------------------------- /src/api/CortexClient.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { CortexClient } from './CortexClient'; 18 | import { IdentityApi } from '@backstage/core-plugin-api'; 19 | import { mock } from 'jest-mock-extended'; 20 | 21 | const mockFetch = jest.fn().mockResolvedValue({ 22 | status: 200, 23 | json: () => Promise.resolve({}), 24 | }); 25 | global.fetch = mockFetch; 26 | 27 | describe('CortexClient', () => { 28 | const getCortexClient = ( 29 | email: string = 'e@mail.com', 30 | displayName: string = 'Na Me', 31 | ) => 32 | new CortexClient({ 33 | discoveryApi: { 34 | getBaseUrl: () => Promise.resolve('http://localhost:3000'), 35 | }, 36 | identityApi: mock({ 37 | getCredentials: () => 38 | Promise.resolve({ 39 | token: 'toKen', 40 | }), 41 | getProfileInfo: () => 42 | Promise.resolve({ 43 | email, 44 | displayName, 45 | }), 46 | }), 47 | }); 48 | 49 | beforeEach(() => { 50 | jest.clearAllMocks(); 51 | }); 52 | 53 | [ 54 | { 55 | email: 'e@mail.com', 56 | displayName: 'NaMe', 57 | }, 58 | { 59 | email: 'tony@cortex.io', 60 | displayName: 'Tony Vespa', 61 | }, 62 | { 63 | email: 'foreign.exchange.student@cortex.io', 64 | displayName: 'Fez Ąğłóżź', 65 | }, 66 | ].forEach(({ email, displayName }) => { 67 | it(`fetch is called with x-cortex headers [${email}] [${displayName}]`, async () => { 68 | await getCortexClient(email, displayName).cancelEntitySync(); 69 | expect(fetch).toBeCalledWith( 70 | expect.anything(), 71 | expect.objectContaining({ 72 | headers: expect.objectContaining({ 73 | 'x-cortex-email': encodeURIComponent(email), 74 | 'x-cortex-name': encodeURIComponent(displayName), 75 | }), 76 | }), 77 | ); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /src/api/ExtensionApi.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { createApiRef } from '@backstage/core-plugin-api'; 17 | import { ExtensionApi } from '@cortexapps/backstage-plugin-extensions'; 18 | 19 | export const extensionApiRef = createApiRef({ 20 | id: 'plugin.cortex.extension', 21 | }); 22 | -------------------------------------------------------------------------------- /src/api/NoopExtensionClient.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { 17 | CustomMapping, 18 | EntityFilterGroup, 19 | ExtensionApi, 20 | } from '@cortexapps/backstage-plugin-extensions'; 21 | 22 | export class NoopExtensionClient implements ExtensionApi { 23 | async getAdditionalFilters(): Promise { 24 | return []; 25 | } 26 | 27 | async getCustomMappings(): Promise { 28 | return []; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { CortexClient, cortexApiRef } from './CortexClient'; 17 | -------------------------------------------------------------------------------- /src/components/Common/CopyButton.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { ReactNode, useCallback } from 'react'; 17 | import { Button } from '@material-ui/core'; 18 | import { copyText } from '../../utils/WindowUtils'; 19 | 20 | interface CopyButtonProps { 21 | textToCopy: string | (() => string); 22 | children: ReactNode; 23 | 'aria-label'?: string; 24 | onSuccess?: () => void; 25 | onFail?: () => void; 26 | } 27 | 28 | export const CopyButton: React.FC = ({ 29 | textToCopy, 30 | children, 31 | onSuccess, 32 | onFail, 33 | 'aria-label': ariaLabel, 34 | }) => { 35 | const onCopyClicked = useCallback(async () => { 36 | const text = typeof textToCopy === 'function' ? textToCopy() : textToCopy; 37 | 38 | return copyText(text, onSuccess, onFail); 39 | }, [onFail, onSuccess, textToCopy]); 40 | 41 | return ( 42 | 50 | ); 51 | }; 52 | -------------------------------------------------------------------------------- /src/components/Common/CortexInfoCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { PropsWithChildren } from 'react'; 17 | import { InfoCard } from '@backstage/core-components'; 18 | import { makeStyles } from '@material-ui/core'; 19 | import classnames from 'classnames'; 20 | 21 | interface CortexInfoCardProps extends PropsWithChildren { 22 | title?: React.ReactNode; 23 | padding?: 'default' | 'dense'; 24 | className?: string; 25 | } 26 | 27 | export const useCortexInfoCardStyles = makeStyles(theme => ({ 28 | root: { 29 | marginBottom: theme.spacing(1), 30 | }, 31 | cardRoot: { 32 | padding: theme.spacing(2), 33 | }, 34 | cardDense: { 35 | padding: theme.spacing(0), 36 | }, 37 | cardHeader: { 38 | padding: theme.spacing(2), 39 | }, 40 | cardHeaderTitle: { 41 | fontWeight: 700, 42 | margin: 0, 43 | }, 44 | })); 45 | 46 | export const CortexInfoCard: React.FC = ({ 47 | children, 48 | title, 49 | padding = 'default', 50 | className, 51 | }) => { 52 | const classes = useCortexInfoCardStyles(); 53 | 54 | return ( 55 | 70 | {children} 71 | 72 | ); 73 | }; 74 | -------------------------------------------------------------------------------- /src/components/Common/HoverTimestamp.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { isNil } from 'lodash'; 17 | import { Moment } from 'moment'; 18 | import React, { PropsWithChildren, useMemo } from 'react'; 19 | 20 | import { Tooltip } from '@material-ui/core'; 21 | 22 | interface HoverTimestampProps extends PropsWithChildren { 23 | id?: string; // Unique id for tooltip target 24 | ts: Moment; 25 | } 26 | 27 | export const HoverTimestamp: React.FC = ({ 28 | children, 29 | id, 30 | ts, 31 | }) => { 32 | const local = useMemo(() => ts.local(), [ts]); 33 | 34 | const hoverText = useMemo( 35 | () => local.format('dddd, MM/DD/YYYY, HH:mm:ss'), 36 | [local], 37 | ); 38 | 39 | return ( 40 | 41 | {isNil(children) ? local.fromNow() : children} 42 | 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/Common/LinearProgressWithLabel.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useMemo } from 'react'; 17 | import { LinearProgress, Typography } from '@material-ui/core'; 18 | import Box from '@material-ui/core/Box'; 19 | import { isNil } from 'lodash'; 20 | import { ClassNameMap } from '@material-ui/core/styles/withStyles'; 21 | 22 | interface LinearProgressWithLabelProps { 23 | classes: ClassNameMap; 24 | value: number; 25 | colorByValue?: boolean; 26 | label?: string; 27 | } 28 | 29 | export function colorForNum(value: number): 'success' | 'warning' | 'danger' { 30 | if (value > 90) { 31 | return 'success'; 32 | } else if (value > 49) { 33 | return 'warning'; 34 | } else { 35 | return 'danger'; 36 | } 37 | } 38 | 39 | export const LinearProgressWithLabel: React.FC = 40 | ({ value, classes, colorByValue, label }) => { 41 | const className = useMemo(() => { 42 | const color = colorByValue ? undefined : colorForNum(value); 43 | 44 | return isNil(color) 45 | ? undefined 46 | : color === 'success' 47 | ? classes.barColorSuccess 48 | : color === 'warning' 49 | ? classes.barColorWarning 50 | : classes.barColorDanger; 51 | }, [classes, colorByValue, value]); 52 | 53 | return ( 54 | 55 | 56 | 61 | 62 | 63 | 64 | {label ?? `${Math.round(value)}%`} 65 | 66 | 67 | 68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /src/components/Common/PollingLinearGauge.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { LinearGauge } from '@backstage/core-components'; 18 | import React, { useState } from 'react'; 19 | import { ExponentialBackoff } from '../../utils/MathUtils'; 20 | import { useInterval } from '../../utils/HookUtils'; 21 | 22 | interface PollingLinearGaugeProps { 23 | backoffMultiplier?: number; 24 | done?: boolean; 25 | initialDelay?: number; 26 | maxDelay?: number; 27 | poll: () => Promise; 28 | value: number; 29 | } 30 | 31 | const PollingLinearGauge: React.FC = ({ 32 | backoffMultiplier = 1.2, 33 | done = false, 34 | initialDelay = 1000, 35 | maxDelay = 1000, 36 | poll, 37 | value, 38 | }) => { 39 | const [exponentialBackoff, setExponentialBackoff] = useState( 40 | () => 41 | new ExponentialBackoff({ 42 | initialDelay: initialDelay, 43 | multiplier: backoffMultiplier, 44 | maxDelay: maxDelay, 45 | }), 46 | ); 47 | 48 | useInterval( 49 | async () => { 50 | await poll(); 51 | setExponentialBackoff(backoff => backoff.advance()); 52 | }, 53 | done ? null : exponentialBackoff.delay(), 54 | ); 55 | 56 | return ; 57 | }; 58 | 59 | export default PollingLinearGauge; 60 | -------------------------------------------------------------------------------- /src/components/Common/ScorecardLadderLevelBadge.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { Box, Tooltip, Typography } from '@material-ui/core'; 18 | import StarIcon from '@material-ui/icons/Star'; 19 | import { fallbackPalette } from '../../styles/styles'; 20 | 21 | interface ScorecardLadderLevelBadgeProps { 22 | name?: string; 23 | color?: string; 24 | showName?: boolean; 25 | } 26 | 27 | export const ScorecardLadderLevelBadge = ({ 28 | name = 'No Level', 29 | color, 30 | showName, 31 | }: ScorecardLadderLevelBadgeProps) => ( 32 | 33 | 34 | 40 | {showName && {name}} 41 | 42 | 43 | ); 44 | -------------------------------------------------------------------------------- /src/components/Common/SortDropdown.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { FormControl, InputLabel, MenuItem, Select } from '@material-ui/core'; 18 | 19 | export interface SortMethods { 20 | [title: string]: (a: T, b: T) => number; 21 | } 22 | 23 | interface SortDropdownProps { 24 | label?: string; 25 | selected: string | undefined; 26 | items: string[]; 27 | select: (event: React.ChangeEvent<{ value: unknown }>) => void; 28 | } 29 | 30 | export const SortDropdown = ({ 31 | label, 32 | selected, 33 | items, 34 | select, 35 | }: SortDropdownProps) => { 36 | return ( 37 | 38 | {label && {label}} 39 | 46 | 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /src/components/Common/Stats.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { alpha, makeStyles } from '@material-ui/core'; 17 | import classnames from 'classnames'; 18 | import { identity } from 'lodash'; 19 | import React, { PropsWithChildren } from 'react'; 20 | import { fallbackPalette } from '../../styles/styles'; 21 | 22 | interface StatsProps extends PropsWithChildren { 23 | column?: boolean; 24 | stretch?: boolean; 25 | } 26 | 27 | const useStyles = makeStyles({ 28 | root: { 29 | display: 'flex', 30 | flexDirection: 'column', 31 | listStyle: 'none', 32 | margin: 0, 33 | padding: 0, 34 | '& li': { 35 | flex: 1, 36 | maxWidth: 300, 37 | '&:last-child': { 38 | borderRight: 'none', 39 | }, 40 | }, 41 | }, 42 | flexRow: { 43 | flexDirection: 'row', 44 | }, 45 | flex1: { 46 | flex: 1, 47 | }, 48 | rowItem: { 49 | borderRight: `1px solid ${alpha(fallbackPalette.common.white, 0.12)}`, 50 | padding: 20, 51 | }, 52 | columnItem: { 53 | padding: 20, 54 | }, 55 | }); 56 | 57 | const Stats: React.FC = ({ children, column = false, stretch }) => { 58 | const classes = useStyles(); 59 | // filter falsey (e.g. null) children out 60 | const childrenToRender = React.Children.toArray(children).filter(identity); 61 | 62 | return ( 63 |
    69 | {React.Children.map(childrenToRender, (child, idx) => { 70 | return ( 71 |
  1. 78 | {child} 79 |
  2. 80 | ); 81 | })} 82 |
83 | ); 84 | }; 85 | 86 | export default Stats; 87 | -------------------------------------------------------------------------------- /src/components/Common/StatsItem.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useMemo } from 'react'; 17 | import { percentify } from '../../utils/NumberUtils'; 18 | import { ordinal } from '../../utils/strings'; 19 | import { Box, Typography, alpha, withStyles } from '@material-ui/core'; 20 | 21 | export type StatNumberType = 'NTH' | 'PERCENTAGE' | 'NONE'; 22 | 23 | export interface StatsItemProps { 24 | caption: string; 25 | percentage?: boolean; 26 | type?: StatNumberType; 27 | value: number; 28 | } 29 | 30 | export const CaptionTypography = withStyles(theme => ({ 31 | root: { 32 | color: alpha(theme.palette.text.primary, 0.7), 33 | }, 34 | }))(Typography); 35 | 36 | const StatsItem: React.FC = ({ 37 | caption, 38 | percentage, 39 | type, 40 | value, 41 | }) => { 42 | const calculatedValue = useMemo( 43 | () => (percentage ? percentify(value) : value), 44 | [value, percentage], 45 | ); 46 | 47 | const suffix = useMemo(() => { 48 | switch (type) { 49 | case 'NTH': 50 | return ordinal(calculatedValue); 51 | case 'PERCENTAGE': 52 | return '%'; 53 | case 'NONE': 54 | default: 55 | return undefined; 56 | } 57 | }, [calculatedValue, type]); 58 | 59 | return ( 60 | 61 | {caption} 62 | 63 | {calculatedValue} 64 | {suffix} 65 | 66 | 67 | ); 68 | }; 69 | 70 | export default StatsItem; 71 | -------------------------------------------------------------------------------- /src/components/Common/Truncated.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useMemo, useState } from "react"; 17 | import { BackstageTheme } from "@backstage/theme"; 18 | import { makeStyles } from "@material-ui/core"; 19 | 20 | const useStyles = makeStyles(styles => ({ 21 | linkButton: { 22 | backgroundColor: 'transparent', 23 | border: 'none', 24 | color: styles.palette.primary.main, 25 | cursor: 'pointer', 26 | fontSize: styles.typography.body2.fontSize, 27 | padding: 0, 28 | }, 29 | })); 30 | 31 | interface TruncatedProps { 32 | text?: string; 33 | truncateToLines: number; 34 | renderText?: (text: string) => React.ReactElement 35 | } 36 | 37 | export const Truncated: React.FC = ({ text = '', truncateToLines, renderText }) => { 38 | const classes = useStyles(); 39 | 40 | const [isExpanded, setIsExpanded] = useState(false); 41 | 42 | const allEndOfLines = useMemo(() => { 43 | return Array.from(text.matchAll(/\r\n|\r|\n/g)).map(a => a.index); 44 | }, [text]); 45 | 46 | const displayedText = useMemo(() => { 47 | if (isExpanded || allEndOfLines.length < truncateToLines) { 48 | return text; 49 | } 50 | 51 | return text.substring(0, allEndOfLines[truncateToLines]) + '...'; 52 | }, [text, truncateToLines, isExpanded, allEndOfLines]); 53 | 54 | return ( 55 | <> 56 | {renderText ? renderText(displayedText) : displayedText} 57 | {allEndOfLines.length > truncateToLines && ( 58 | 64 | )} 65 | 66 | ); 67 | }; 68 | -------------------------------------------------------------------------------- /src/components/Common/useLinearProgressWithLabelStyles.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { makeStyles } from '@material-ui/core'; 17 | import { fallbackPalette } from '../../styles/styles'; 18 | 19 | const useLinearProgressWithLabelStyles = makeStyles(() => ({ 20 | barColorSuccess: { 21 | backgroundColor: fallbackPalette.status.ok, 22 | }, 23 | barColorWarning: { 24 | backgroundColor: fallbackPalette.status.warning, 25 | }, 26 | barColorDanger: { 27 | backgroundColor: fallbackPalette.status.error, 28 | }, 29 | })); 30 | 31 | export default useLinearProgressWithLabelStyles; 32 | -------------------------------------------------------------------------------- /src/components/CortexGroupActionItemsWidget/GroupComponentActionItemsRow.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { DefaultEntityRefLink } from '../DefaultEntityLink'; 18 | import { MetadataItem } from '../MetadataItem'; 19 | import { CompoundEntityRef } from '@backstage/catalog-model'; 20 | import { InitiativeActionItem } from '../../api/types'; 21 | import { GroupComponentRuleInitiativesRow } from './GroupComponentRuleInitiativesRow'; 22 | import { makeStyles } from '@material-ui/core'; 23 | import { BackstageTheme } from '@backstage/theme'; 24 | 25 | const useStyles = makeStyles(_ => ({ 26 | componentLink: { 27 | marginBottom: '0.35rem', 28 | }, 29 | })); 30 | 31 | interface GroupComponentActionItemsRowProps { 32 | componentRef: CompoundEntityRef; 33 | ruleToInitiativeActionItem: Record; 34 | } 35 | 36 | export const GroupComponentActionItemsRow = ({ 37 | componentRef, 38 | ruleToInitiativeActionItem, 39 | }: GroupComponentActionItemsRowProps) => { 40 | const classes = useStyles(); 41 | 42 | return ( 43 | 44 |
45 | 46 | 47 | {componentRef.name} 48 | 49 | 50 |
51 | {Object.keys(ruleToInitiativeActionItem).map(rule => ( 52 | 58 | ))} 59 |
60 | ); 61 | }; 62 | -------------------------------------------------------------------------------- /src/components/CortexGroupActionItemsWidget/GroupComponentRuleInitiativeInfo.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { InitiativeActionItem } from '../../api/types'; 18 | import { Grid, makeStyles } from '@material-ui/core'; 19 | import { BackstageTheme } from '@backstage/theme'; 20 | import { useRouteRef } from '@backstage/core-plugin-api'; 21 | import { initiativeRouteRef } from '../../routes'; 22 | import { MetadataItem } from '../MetadataItem'; 23 | import { Link } from '@backstage/core-components'; 24 | import React from 'react'; 25 | import { useInitiativesCustomName } from '../../utils/hooks'; 26 | import { capitalize } from 'lodash'; 27 | 28 | const useStyles = makeStyles(_ => ({ 29 | initiativeInfo: { 30 | marginLeft: '2rem', 31 | marginBottom: '0.25rem', 32 | }, 33 | })); 34 | 35 | interface GroupComponentRuleInitiativeInfoProps { 36 | initiativeActionItem: InitiativeActionItem; 37 | } 38 | 39 | export const GroupComponentRuleInitiativeInfo = ({ 40 | initiativeActionItem, 41 | }: GroupComponentRuleInitiativeInfoProps) => { 42 | const initiativeRef = useRouteRef(initiativeRouteRef); 43 | const classes = useStyles(); 44 | 45 | const { singular: initiativeNameLabel } = useInitiativesCustomName(); 46 | 47 | return ( 48 | 53 | 54 | 55 | {initiativeActionItem.initiative.name} 56 | 57 | 58 | {initiativeActionItem.initiative.targetDate} 59 | 60 | 61 | 62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /src/components/CortexGroupActionItemsWidget/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { CortexGroupActionItemsWidget } from './CortexGroupActionItemsWidget'; 17 | -------------------------------------------------------------------------------- /src/components/CortexLayout/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | export { CortexLayout } from './CortexLayout'; 18 | -------------------------------------------------------------------------------- /src/components/CortexPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { CortexPage } from './CortexPage'; 17 | -------------------------------------------------------------------------------- /src/components/CortexScorecardWidget/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { CortexScorecardWidget } from './CortexScorecardWidget'; 17 | -------------------------------------------------------------------------------- /src/components/DefaultEntityLink/DefaultEntityRefLink.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { EntityRefLink } from '@backstage/plugin-catalog-react'; 18 | import { defaultComponentRefContext } from '../../utils/ComponentUtils'; 19 | import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; 20 | 21 | interface DefaultEntityRefLinkProps { 22 | children?: React.ReactNode; 23 | entityRef: Entity | CompoundEntityRef; 24 | title?: string; 25 | } 26 | 27 | export const DefaultEntityRefLink = ({ 28 | children, 29 | entityRef, 30 | title, 31 | }: DefaultEntityRefLinkProps) => { 32 | return ( 33 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/DefaultEntityLink/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { DefaultEntityRefLink } from './DefaultEntityRefLink'; 17 | -------------------------------------------------------------------------------- /src/components/Entitlements/ExpirationBanner.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { ExpirationResponse } from '../../api/types'; 18 | import { WarningPanel } from '@backstage/core-components'; 19 | import { isNil } from 'lodash'; 20 | import { getExpirationMessage } from './ExpirationUtils'; 21 | import { daysUntil } from '../../utils/dates'; 22 | 23 | interface ExpirationBannerProps extends ExpirationResponse { 24 | } 25 | 26 | export const ExpirationBanner = ({ contractType, expirationDate, shutdownDate }: ExpirationBannerProps) => { 27 | const daysUntilExpiration = daysUntil(expirationDate); 28 | if (isNil(daysUntilExpiration)) { 29 | return null; 30 | } 31 | 32 | const daysUntilShutdown = daysUntil(shutdownDate); 33 | const isTrial = contractType === 'TRIAL'; 34 | const isExpired = daysUntilExpiration < 0; 35 | 36 | return ( 37 | 41 | ); 42 | }; 43 | 44 | -------------------------------------------------------------------------------- /src/components/EntityPage/EntityScorecardDetails/EntityScorecardDetails.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { ServiceScorecardScore } from '../../../api/types'; 18 | import { EntityScorecardOverview } from '../EntityScorecardOverview'; 19 | import { EntityScorecardRules } from '../EntityScorecardRules'; 20 | import { useCortexApi } from '../../../utils/hooks'; 21 | import { Progress } from '@backstage/core-components'; 22 | import { ScorecardsServiceNextRules } from '../../Scorecards/ScorecardsServicePage/ScorecardsServiceNextRules'; 23 | import { AnyEntityRef } from '../../../utils/types'; 24 | 25 | interface EntityScorecardDetailsProps { 26 | entityRef: AnyEntityRef; 27 | scorecardId: number; 28 | score: ServiceScorecardScore; 29 | } 30 | 31 | export const EntityScorecardDetails = ({ 32 | entityRef, 33 | scorecardId, 34 | score, 35 | }: EntityScorecardDetailsProps) => { 36 | const { value: ladders, loading: loadingLadders } = useCortexApi( 37 | api => api.getScorecardLadders(scorecardId), 38 | [scorecardId], 39 | ); 40 | 41 | // currently we only support 1 ladder per Scorecard 42 | const ladder = ladders?.[0]; 43 | 44 | if (loadingLadders) { 45 | return ; 46 | } 47 | 48 | return ( 49 | <> 50 | {ladder === undefined ? ( 51 | 52 | ) : ( 53 | 57 | )} 58 | 59 | 60 | ); 61 | }; 62 | -------------------------------------------------------------------------------- /src/components/EntityPage/EntityScorecardDetails/index.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { EntityScorecardDetails } from './EntityScorecardDetails'; 17 | -------------------------------------------------------------------------------- /src/components/EntityPage/EntityScorecardOverview/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { EntityScorecardOverview } from './EntityScorecardOverview'; 17 | -------------------------------------------------------------------------------- /src/components/EntityPage/EntityScorecardRules/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { EntityScorecardRules } from './EntityScorecardRules'; 17 | -------------------------------------------------------------------------------- /src/components/EntityPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { EntityPage } from './EntityPage'; 17 | -------------------------------------------------------------------------------- /src/components/FilterCard/FilterCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { FilterDefinition, Filters } from './Filters'; 18 | import { CortexInfoCard } from '../Common/CortexInfoCard'; 19 | import { Box, makeStyles } from '@material-ui/core'; 20 | 21 | interface FilterCardProps { 22 | filterDefinitions: FilterDefinition[]; 23 | title?: string; 24 | } 25 | 26 | const useStyles = makeStyles(() => ({ 27 | filterCardRoot: { 28 | width: 550, 29 | minWidth: 550, 30 | }, 31 | })); 32 | 33 | export const FilterCard: React.FC = ({ 34 | filterDefinitions, 35 | title = 'Filter By', 36 | }) => { 37 | const classes = useStyles(); 38 | 39 | return ( 40 | 41 | 42 | {filterDefinitions.map((filterDefinition, idx) => { 43 | return ( 44 | 48 | ); 49 | })} 50 | 51 | 52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/components/FilterCard/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { FilterCard } from './FilterCard'; 17 | -------------------------------------------------------------------------------- /src/components/FilterCard/useFilter.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React, { PropsWithChildren, useContext, useState } from 'react'; 18 | 19 | interface FilterContextState { 20 | checkedFilters: Record; 21 | setCheckedFilters: React.Dispatch< 22 | React.SetStateAction> 23 | >; 24 | oneOf: Record; 25 | setOneOf: React.Dispatch>>; 26 | } 27 | 28 | const FilterContext = React.createContext( 29 | undefined, 30 | ); 31 | 32 | interface FilterProviderProps extends PropsWithChildren { 33 | initialCheckedFilters?: Record; 34 | initialOneOf?: Record; 35 | } 36 | 37 | export const FilterProvider: React.FC = ({ 38 | children, 39 | initialCheckedFilters = {}, 40 | initialOneOf = {}, 41 | }) => { 42 | const [checkedFilters, setCheckedFilters] = useState>( 43 | initialCheckedFilters, 44 | ); 45 | const [oneOf, setOneOf] = useState>(initialOneOf); 46 | 47 | return ( 48 | 56 | {children} 57 | 58 | ); 59 | }; 60 | 61 | export const useFilter = () => { 62 | const context = useContext(FilterContext); 63 | 64 | if (!context) { 65 | throw new Error('useFilter was used outside of its Provider'); 66 | } 67 | 68 | return context; 69 | }; 70 | -------------------------------------------------------------------------------- /src/components/Gauge/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { Gauge } from './Gauge'; 17 | -------------------------------------------------------------------------------- /src/components/HelpPage/HelpLinksContent.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { HelpPageLink } from '@cortexapps/backstage-plugin-extensions'; 17 | import { 18 | Content, 19 | ContentHeader, 20 | EmptyState, 21 | ItemCardGrid, 22 | } from '@backstage/core-components'; 23 | import React from 'react'; 24 | import { isEmpty } from 'lodash'; 25 | import { ListCard } from '../ListCard'; 26 | 27 | export interface HelpLinksContentProps { 28 | links: HelpPageLink[]; 29 | } 30 | 31 | export const HelpLinksContent = ({ links }: HelpLinksContentProps) => { 32 | return ( 33 | 34 | 35 | {isEmpty(links) && } 36 | 37 | {links.map((link, index) => ( 38 | 44 | ))} 45 | 46 | 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /src/components/HelpPage/HelpPage.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { HelpPageDisplayOptions } from '@cortexapps/backstage-plugin-extensions'; 18 | import { isUndefined } from 'lodash'; 19 | import { HelpLinksContent } from './HelpLinksContent'; 20 | 21 | export interface HelpPageProps { 22 | helpPage: HelpPageDisplayOptions; 23 | } 24 | 25 | export const HelpPage = ({ helpPage }: HelpPageProps) => { 26 | return ( 27 | <> 28 | {!isUndefined(helpPage.links) && ( 29 | 30 | )} 31 | 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/components/HelpPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { HelpPage } from './HelpPage'; 17 | -------------------------------------------------------------------------------- /src/components/Homepage/Homepage.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useMemo } from 'react'; 17 | import { 18 | Content, 19 | ContentHeader, 20 | ItemCardGrid, 21 | } from '@backstage/core-components'; 22 | import moment from 'moment'; 23 | import { HomepageOncallCard } from './HomepageOncallCard'; 24 | import { HomepageInsights } from './HomepageInsights'; 25 | import { identityApiRef, useApi } from '@backstage/core-plugin-api'; 26 | import { useAsync } from 'react-use'; 27 | 28 | export const Homepage = () => { 29 | const identityApi = useApi(identityApiRef); 30 | 31 | const currTime = useMemo(() => moment(), []); 32 | 33 | const name = useAsync(async () => { 34 | const profileInfo = await identityApi.getProfileInfo(); 35 | return profileInfo.displayName; 36 | }); 37 | 38 | const helloMessage = useMemo(() => { 39 | const nameSuffix = name.loading ? '' : `, ${name.value}`; 40 | 41 | const currHour = currTime.hour(); 42 | 43 | if (currHour < 12) { 44 | return `☀️ Good morning${nameSuffix}`; 45 | } else if (currHour < 18) { 46 | return `☀️ Good afternoon${nameSuffix}`; 47 | } else { 48 | return `🌅 Good evening${nameSuffix}`; 49 | } 50 | }, [currTime, name]); 51 | 52 | return ( 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ); 61 | }; 62 | -------------------------------------------------------------------------------- /src/components/Homepage/HomepageUtils.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import moment from 'moment'; 17 | import { Oncall } from '../../api/types'; 18 | import { joinWithSpecialLastJoin, maybePluralize } from '../../utils/strings'; 19 | 20 | export const getDisplayTimeUntilNextOncall = (oncallStartDate: string) => { 21 | const now = moment.now(); 22 | 23 | const hoursUntilOncall = moment(oncallStartDate).diff(now, 'hours'); 24 | const daysUntilOncall = moment(oncallStartDate).diff(now, 'days'); 25 | 26 | return hoursUntilOncall < 18 27 | ? `${hoursUntilOncall} hours` 28 | : `${maybePluralize(daysUntilOncall, 'day')}`; 29 | }; 30 | 31 | export const getOncallSchedulesDisplayName = (oncalls: Oncall[]) => { 32 | const oncallNames = oncalls?.map(oncall => oncall.name) ?? []; 33 | return oncallNames.length === 1 34 | ? oncallNames[0] 35 | : joinWithSpecialLastJoin(oncallNames, { 36 | joiner: ', ', 37 | lastJoiner: ', and ', 38 | lastJoinerWhenTwoItems: ' and ', 39 | }); 40 | }; 41 | -------------------------------------------------------------------------------- /src/components/Homepage/InsightCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { InfoCard } from '@backstage/core-components'; 18 | import { Typography } from '@material-ui/core'; 19 | import { useHomepageInsightsStyles } from '../../styles/styles'; 20 | 21 | interface InsightCardProps { 22 | children: React.ReactNode; 23 | } 24 | 25 | const InsightCard: React.FC = ({ children }) => { 26 | const classes = useHomepageInsightsStyles(); 27 | 28 | return ( 29 | 30 | {children} 31 | 32 | ); 33 | }; 34 | 35 | export default InsightCard; 36 | -------------------------------------------------------------------------------- /src/components/Homepage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { Homepage } from './Homepage'; 17 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeCard/InitiativeCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { Initiative } from '../../../api/types'; 18 | import { useRouteRef } from '@backstage/core-plugin-api'; 19 | import { initiativeRouteRef } from '../../../routes'; 20 | import { ListCard } from '../../ListCard'; 21 | import moment from 'moment'; 22 | 23 | type InitiativeCardProps = { 24 | initiative: Initiative; 25 | }; 26 | 27 | export const InitiativeCard = ({ initiative }: InitiativeCardProps) => { 28 | const initiativeRef = useRouteRef(initiativeRouteRef); 29 | 30 | return ( 31 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeCard/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativeCard } from './InitiativeCard'; 17 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitativeFilterDialog/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativeFilterDialog } from './InitiativeFilterDialog'; 17 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeFailingTab/InitiativeFailingTabConfig.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { BackstageTheme, darkTheme } from '@backstage/theme'; 18 | import { makeStyles } from '@material-ui/core'; 19 | import { InitiativeActionItem } from '../../../../api/types'; 20 | 21 | export interface InitiativeFailingTabRowProps { 22 | actionItems: InitiativeActionItem[]; 23 | componentRef: string; 24 | tag: string; 25 | name: string; 26 | description?: string; 27 | score: number; 28 | } 29 | 30 | export const useInitiativeFailingTabStyle = makeStyles(() => ({ 31 | tag: { 32 | fontFamily: 'Monospace', 33 | fontSize: 12, 34 | }, 35 | })); 36 | 37 | export const failingTabTheme: BackstageTheme = { 38 | ...darkTheme, 39 | overrides: { 40 | ...darkTheme.overrides, 41 | MuiTableCell: { 42 | ...darkTheme.overrides?.MuiTableCell, 43 | root: { 44 | ...darkTheme.overrides?.MuiTableCell?.root, 45 | verticalAlign: 'top', 46 | }, 47 | }, 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeFailingTab/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativeFailingTab } from './InitiativeFailingTab'; 17 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeLevelsTab/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativeLevelsTab } from './InitiativeLevelsTab'; 17 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeMetadataCard/InitiativeMetadataCardUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { isNil } from 'lodash'; 18 | import { Initiative } from '../../../../api/types'; 19 | import { daysUntil } from '../../../../utils/MomentUtils'; 20 | import moment from 'moment'; 21 | 22 | export const getTargetDateMessage = (initiative: Initiative) => { 23 | const remainingDays = daysUntil(initiative.targetDate); 24 | 25 | return !isNil(remainingDays) && remainingDays < 0 26 | ? `Expired ${moment.utc(initiative.targetDate).fromNow()}` 27 | : `Due by ${moment.utc(initiative.targetDate) 28 | .format('MMMM Do, YYYY')}`; 29 | }; 30 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeMetadataCard/InitiativeMetadataFilter.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useMemo } from 'react'; 17 | import { Initiative } from '../../../../api/types'; 18 | import { Typography } from '@material-ui/core'; 19 | import { isEmpty } from 'lodash'; 20 | import { 21 | getEntityCategoryFromFilter, 22 | getEntityGroupsFromFilter, 23 | } from '../../../Scorecards/ScorecardDetailsPage/ScorecardMetadataCard/ScorecardMetadataUtils'; 24 | import { joinWithAnds } from '../../../../utils/strings'; 25 | 26 | interface InitiativeMetadataFilterProps { 27 | initiative: Initiative; 28 | } 29 | 30 | const InitiativeMetadataFilter: React.FC = ({ 31 | initiative, 32 | }) => { 33 | const entityCategory = useMemo( 34 | () => getEntityCategoryFromFilter(initiative.scorecard?.filter), 35 | [initiative], 36 | ); 37 | 38 | const { entityGroups, excludeEntityGroups } = useMemo( 39 | () => getEntityGroupsFromFilter(initiative.filter), 40 | [initiative.filter], 41 | ); 42 | 43 | const hasIncludes = !isEmpty(entityGroups); 44 | const hasExcludes = !isEmpty(excludeEntityGroups); 45 | 46 | return ( 47 | 48 | Applies to {!hasIncludes && !hasExcludes ? 'all' : ''}{' '} 49 | {entityCategory?.toLowerCase() || 'entitie'}s 50 | {(hasIncludes || hasExcludes) && ( 51 | <> 52 | {' '} 53 | in {!hasIncludes && ' all '} group 54 | {!hasIncludes || entityGroups.length > 1 ? 's' : ''} 55 | {hasIncludes && <> {joinWithAnds(entityGroups)}} 56 | {hasExcludes && <>, excluding {joinWithAnds(excludeEntityGroups)}} 57 | 58 | )} 59 | 60 | ); 61 | }; 62 | 63 | export default InitiativeMetadataFilter; 64 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeMetadataCard/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativeMetadataCard } from './InitiativeMetadataCard'; 17 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativePassingTab/InitiativePassingTabConfig.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { BackstageTheme, darkTheme } from '@backstage/theme'; 18 | import { makeStyles } from '@material-ui/core'; 19 | 20 | export interface InitiativePassingTabRowProps { 21 | componentRef: string; 22 | tag: string; 23 | name: string; 24 | description?: string; 25 | } 26 | 27 | export const useInitiativePassingTabStyle = makeStyles(() => ({ 28 | tag: { 29 | fontFamily: 'Monospace', 30 | fontSize: 12, 31 | }, 32 | })); 33 | 34 | export const passingTabTheme: BackstageTheme = { 35 | ...darkTheme, 36 | overrides: { 37 | ...darkTheme.overrides, 38 | MuiTableCell: { 39 | ...darkTheme.overrides?.MuiTableCell, 40 | root: { 41 | ...darkTheme.overrides?.MuiTableCell?.root, 42 | verticalAlign: 'top', 43 | }, 44 | }, 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativePassingTab/ServiceNameColumn.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { Typography } from '@material-ui/core'; 18 | import Box from '@material-ui/core/Box'; 19 | import { ScorecardServiceRefLink } from '../../../ScorecardServiceRefLink'; 20 | import { InitiativePassingTabRowProps } from './InitiativePassingTabConfig'; 21 | 22 | export interface ServiceNameColumnProps extends InitiativePassingTabRowProps { 23 | scorecardId: number; 24 | } 25 | 26 | export const ServiceNameColumn: React.FC = ({ 27 | componentRef, 28 | scorecardId, 29 | name, 30 | description, 31 | }) => { 32 | return ( 33 | 34 | 40 | 41 | 45 | {name} 46 | 47 | {description && ( 48 | {description} 49 | )} 50 | 51 | 52 | 53 | ); 54 | }; 55 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativePassingTab/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativePassingTab } from './InitiativePassingTab'; 17 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeRulesTab/InitiativeRulesTab.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { InitiativeRule } from '../../../../api/types'; 18 | import { Box, alpha, makeStyles } from '@material-ui/core'; 19 | import { fallbackPalette } from '../../../../styles/styles'; 20 | import { CortexInfoCard } from '../../../Common/CortexInfoCard'; 21 | import { ScorecardRuleRow } from '../../../Scorecards/ScorecardDetailsPage/ScorecardRulesTab/ScorecardRuleRow'; 22 | 23 | interface InitiativeRulesTabProps { 24 | rules: InitiativeRule[]; 25 | } 26 | 27 | export const useInitiativeLevelsTabStyles = makeStyles(() => ({ 28 | levelBlock: { 29 | padding: 16, 30 | borderBottom: `1px solid ${alpha(fallbackPalette.common.white, 0.12)}`, 31 | '&:last-child': { 32 | borderBottom: 'none', 33 | }, 34 | }, 35 | })); 36 | 37 | export const InitiativeRulesTab: React.FC = ({ 38 | rules, 39 | }) => { 40 | return ( 41 | 42 | 43 | {rules.map((rule, idx) => ( 44 | 45 | ))} 46 | 47 | 48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeRulesTab/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativeRulesTab } from './InitiativeRulesTab'; 17 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeStatsCard/InitiativeStatsCard.test.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { shallow } from 'enzyme'; 17 | import React from 'react'; 18 | import { 19 | InitiativeStatsCard, 20 | InitiativeStatsCardProps, 21 | } from './InitiativeStatsCard'; 22 | 23 | describe('InitiativeStatsCard', () => { 24 | const getInitiativeStatsCard = (props: InitiativeStatsCardProps) => 25 | shallow(); 26 | 27 | it('should handle divide by 0', async () => { 28 | const card = getInitiativeStatsCard({ 29 | scores: [], 30 | actionItems: [], 31 | filter: () => () => true, 32 | }); 33 | 34 | expect(card).toMatchSnapshot(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeStatsCard/__snapshots__/InitiativeStatsCard.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`InitiativeStatsCard should handle divide by 0 1`] = ` 4 | 12 | 13 | 19 | 24 | 29 | 30 | 31 | `; 32 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/InitiativeStatsCard/index.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativeStatsCard } from './InitiativeStatsCard'; 17 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativeDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativeDetailsPage } from './InitiativeDetailsPage'; 17 | export { default } from './InitiativeDetailsPage'; 18 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativesPage/InitiativesPage.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { cortexApiRef } from '../../../api'; 18 | import { useAsync } from 'react-use'; 19 | import { EmptyState, Progress, WarningPanel } from '@backstage/core-components'; 20 | import { useApi } from '@backstage/core-plugin-api'; 21 | import { Route, Routes } from 'react-router-dom'; 22 | import InitiativeDetailsPage from '../InitiativeDetailsPage'; 23 | import { InitiativesList } from './InitiativesList'; 24 | import { useInitiativesCustomName } from '../../../utils/hooks'; 25 | 26 | export const InitiativesPage = () => { 27 | const cortexApi = useApi(cortexApiRef); 28 | 29 | const { 30 | value: scorecards, 31 | loading, 32 | error, 33 | } = useAsync(async () => { 34 | return await cortexApi.getScorecards(); 35 | }, []); 36 | 37 | const { plural: initiativesName } = useInitiativesCustomName(); 38 | 39 | if (loading) { 40 | return ; 41 | } 42 | 43 | if (error) { 44 | return ( 45 | 49 | {error.message} 50 | 51 | ); 52 | } 53 | 54 | if (!scorecards?.length) { 55 | return ( 56 | 61 | ); 62 | } 63 | 64 | return ( 65 | 66 | } /> 67 | } /> 68 | 69 | ); 70 | }; 71 | -------------------------------------------------------------------------------- /src/components/Initiatives/InitiativesPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { InitiativesPage } from './InitiativesPage'; 17 | -------------------------------------------------------------------------------- /src/components/ItemCardHeader/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ItemCardHeader } from './ItemCardHeader'; 17 | -------------------------------------------------------------------------------- /src/components/ListCard/ListCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { 18 | Card, 19 | CardActions, 20 | CardContent, 21 | CardMedia, 22 | Chip, 23 | } from '@material-ui/core'; 24 | import { 25 | Button, 26 | ItemCardHeader, 27 | MarkdownContent, 28 | } from '@backstage/core-components'; 29 | import { Truncated } from '../Common/Truncated'; 30 | 31 | interface ListCardProps { 32 | badges?: string[]; 33 | description?: string; 34 | name: string; 35 | truncateToLines?: number; 36 | url: string; 37 | } 38 | 39 | export const ListCard = ({ 40 | badges, 41 | description, 42 | name, 43 | truncateToLines, 44 | url, 45 | }: ListCardProps) => { 46 | return ( 47 | 48 | 49 | 50 | 51 | 52 | {description && ( 53 | <> 54 | {truncateToLines ? ( 55 | } 59 | /> 60 | ) : ( 61 | 62 | )} 63 | 64 | )} 65 | {badges?.map(badge => ( 66 | 67 | ))} 68 | 69 | 70 | 73 | 74 | 75 | ); 76 | }; 77 | -------------------------------------------------------------------------------- /src/components/ListCard/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ListCard } from './ListCard'; 17 | -------------------------------------------------------------------------------- /src/components/MetadataItem/MetadataItem.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { Grid, makeStyles, Typography } from '@material-ui/core'; 18 | import { fallbackPalette } from '../../styles/styles'; 19 | import { isUndefined } from 'lodash'; 20 | 21 | const useStyles = makeStyles(theme => ({ 22 | label: { 23 | color: theme.palette?.text?.secondary ?? fallbackPalette.text.secondary, 24 | textTransform: 'uppercase', 25 | fontSize: '10px', 26 | fontWeight: 'bold', 27 | letterSpacing: 0.5, 28 | overflow: 'hidden', 29 | whiteSpace: 'nowrap', 30 | }, 31 | value: { 32 | overflow: 'hidden', 33 | lineHeight: '24px', 34 | wordBreak: 'break-word', 35 | }, 36 | })); 37 | 38 | interface MetadataItemProps { 39 | label?: string; 40 | children: string | React.ReactNode; 41 | gridSizes?: Record; 42 | } 43 | 44 | export const MetadataItem = ({ 45 | label, 46 | children, 47 | gridSizes, 48 | }: MetadataItemProps) => { 49 | const classes = useStyles(); 50 | 51 | return ( 52 | 53 | {!isUndefined(label) && ( 54 | 55 | {label} 56 | 57 | )} 58 | {typeof children === 'string' ? ( 59 | 60 | {children} 61 | 62 | ) : ( 63 | children 64 | )} 65 | 66 | ); 67 | }; 68 | -------------------------------------------------------------------------------- /src/components/MetadataItem/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { MetadataItem } from './MetadataItem'; 17 | -------------------------------------------------------------------------------- /src/components/ReportsPage/AllScorecardsPage/AllScorecardsPage.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useCallback, useState } from 'react'; 17 | import { Content, ContentHeader } from '@backstage/core-components'; 18 | import { Grid } from '@material-ui/core'; 19 | import { AllScorecardsHeatmap } from './AllScorecardsHeatmap'; 20 | import { GroupByOption } from '../../../api/types'; 21 | import { GroupByDropdown } from '../Common/GroupByDropdown'; 22 | import { CopyButton } from '../../Common/CopyButton'; 23 | import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'; 24 | import { buildUrl } from '../../../utils/URLUtils'; 25 | import { stringifyUrl } from 'query-string'; 26 | 27 | export const AllScorecardsPage = () => { 28 | const location = useLocation(); 29 | const navigate = useNavigate(); 30 | const [searchParams] = useSearchParams(); 31 | 32 | const [groupBy, setGroupBy] = useState( 33 | (searchParams.get('groupBy') as GroupByOption) ?? GroupByOption.ENTITY, 34 | ); 35 | const setGroupByAndNavigate = useCallback((groupBy: GroupByOption) => { 36 | setGroupBy(groupBy); 37 | 38 | const targetUrl = stringifyUrl({ url: location.pathname, query: { 39 | groupBy: groupBy !== GroupByOption.ENTITY ? groupBy as string : undefined, 40 | } }); 41 | 42 | navigate(targetUrl, { replace: true }); 43 | }, [location.pathname, navigate]); 44 | 45 | const getShareableLink = useCallback( 46 | () => buildUrl({ groupBy }, location.pathname) 47 | , [location, groupBy]); 48 | 49 | return ( 50 | 51 | 52 | 53 | Share link 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /src/components/ReportsPage/AllScorecardsPage/HeatmapCell.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { TableCell, Typography } from '@material-ui/core'; 18 | import { percentify } from '../../../utils/NumberUtils'; 19 | import { ClassNameMap } from '@material-ui/core/styles/withStyles'; 20 | 21 | function getCellClassname(value?: number): string | undefined { 22 | if (value === undefined) return undefined; 23 | 24 | if (value > 0.9) { 25 | return 'success1'; 26 | } else if (value > 0.8) { 27 | return 'success2'; 28 | } else if (value > 0.7) { 29 | return 'success3'; 30 | } else if (value > 0.6) { 31 | return 'warning1'; 32 | } else if (value > 0.5) { 33 | return 'warning2'; 34 | } else if (value > 0.4) { 35 | return 'warning3'; 36 | } else if (value > 0.3) { 37 | return 'danger1'; 38 | } else if (value > 0.2) { 39 | return 'danger2'; 40 | } 41 | 42 | return 'danger3'; 43 | } 44 | 45 | interface HeatmapCellProps { 46 | score?: number; 47 | text?: string; 48 | colorClasses: ClassNameMap; 49 | } 50 | 51 | export const HeatmapCell = ({ 52 | score, 53 | text, 54 | colorClasses, 55 | }: HeatmapCellProps) => { 56 | const className = getCellClassname(score); 57 | 58 | return ( 59 | 64 | 65 | {text ?? (score !== undefined ? `${percentify(score)}%` : '')} 66 | 67 | 68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /src/components/ReportsPage/AllScorecardsPage/HeatmapFiltersSelect.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { Box, Button, FormControl, InputLabel, MenuItem, Select } from '@material-ui/core'; 17 | import { Clear } from '@material-ui/icons'; 18 | import React from 'react'; 19 | 20 | export interface ModalSelectItem { 21 | value: string | number; 22 | label: string; 23 | } 24 | 25 | interface ModalSelectProps { 26 | name: string, 27 | value: T[], 28 | onChange: (value: T[]) => void, 29 | onReset: () => void, 30 | options: ModalSelectItem[] 31 | } 32 | 33 | export const ModalSelect = ({ name, value, onChange, onReset, options }: ModalSelectProps) => { 34 | return ( 35 | 36 | 37 | {name} 38 | 52 | 53 | {value.length > 0 && ( 54 | 62 | )} 63 | 64 | ); 65 | }; 66 | -------------------------------------------------------------------------------- /src/components/ReportsPage/AllScorecardsPage/Tables/colorClasses.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { BackstageTheme } from '@backstage/theme'; 17 | import { makeStyles } from '@material-ui/core'; 18 | 19 | export const useColorCellStyles = makeStyles(theme => ({ 20 | root: { 21 | border: 'none', 22 | textAlign: 'center', 23 | minWidth: '100px', 24 | }, 25 | success1: { 26 | background: '#00ca9b', 27 | color: theme.palette.common.black, 28 | }, 29 | success2: { 30 | background: '#60e6c7', 31 | color: theme.palette.common.black, 32 | }, 33 | success3: { 34 | background: '#b0ecde', 35 | color: theme.palette.common.black, 36 | }, 37 | warning1: { 38 | background: '#ffecb3', 39 | color: theme.palette.common.black, 40 | }, 41 | warning2: { 42 | background: '#ffca28', 43 | color: theme.palette.common.black, 44 | }, 45 | warning3: { 46 | background: '#ffa001', 47 | color: theme.palette.common.black, 48 | }, 49 | danger1: { 50 | background: '#ffd6dd', 51 | color: theme.palette.common.black, 52 | }, 53 | danger2: { 54 | background: '#fe899e', 55 | color: theme.palette.common.black, 56 | }, 57 | danger3: { 58 | background: '#fd496a', 59 | color: theme.palette.common.black, 60 | }, 61 | })); 62 | -------------------------------------------------------------------------------- /src/components/ReportsPage/Common/GroupByDropdown.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { ChangeEvent } from 'react'; 17 | import { FormControl, InputLabel, MenuItem, Select } from '@material-ui/core'; 18 | import { GroupByOption } from '../../../api/types'; 19 | 20 | interface GroupByDropdownProps { 21 | groupBy: GroupByOption | undefined; 22 | setGroupBy: (groupBy: GroupByOption) => void; 23 | excluded?: GroupByOption[]; 24 | } 25 | 26 | const GroupByLabels = { 27 | [GroupByOption.ENTITY]: 'Entity', 28 | [GroupByOption.TEAM]: GroupByOption.TEAM, 29 | [GroupByOption.SERVICE_GROUP]: 'Group', 30 | [GroupByOption.LEVEL]: GroupByOption.LEVEL, 31 | [GroupByOption.DOMAIN]: GroupByOption.DOMAIN, 32 | }; 33 | 34 | export const GroupByDropdown = ({ 35 | groupBy, 36 | setGroupBy, 37 | excluded = [], 38 | }: GroupByDropdownProps) => { 39 | const options = Object.values(GroupByOption).filter( 40 | (option) => !excluded.includes(option) 41 | ); 42 | 43 | const onGroupByChange = (event: ChangeEvent<{ value: unknown }>) => { 44 | setGroupBy(event.target.value as GroupByOption); 45 | }; 46 | 47 | return ( 48 | 49 | Group By 50 | 57 | 58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /src/components/ReportsPage/Common/HeaderTypeDropdown.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { FormControl, InputLabel, MenuItem, Select } from '@material-ui/core'; 18 | import { enumKeys } from '../../../utils/types'; 19 | import { HeaderType } from '../../../api/types'; 20 | 21 | interface HeaderTypeDropdownProps { 22 | headerType: HeaderType | undefined; 23 | setHeaderType: (headerType: HeaderType) => void; 24 | } 25 | 26 | export const HeaderTypeDropdown = ({ 27 | headerType, 28 | setHeaderType, 29 | }: HeaderTypeDropdownProps) => { 30 | const onHeaderTypeChange = (event: React.ChangeEvent<{ value: unknown }>) => { 31 | setHeaderType(event.target.value as HeaderType); 32 | }; 33 | 34 | return ( 35 | 36 | Driven By 37 | 44 | 45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /src/components/ReportsPage/Common/LookbackDropdown.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { FormControl, MenuItem, Select } from '@material-ui/core'; 18 | import { enumKeys } from '../../../utils/types'; 19 | import { Lookback, lookbackLabels } from '../../../utils/lookback'; 20 | 21 | interface LookbackDropdownProps { 22 | lookback: Lookback | undefined; 23 | setLookback: (event: React.ChangeEvent<{ value: unknown }>) => void; 24 | } 25 | 26 | export const LookbackDropdown = ({ 27 | lookback, 28 | setLookback, 29 | }: LookbackDropdownProps) => { 30 | return ( 31 | 32 | 39 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/ReportsPage/HeatmapPage/HeatmapPage.test.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { render } from '@testing-library/react'; 18 | import { TestApiProvider, wrapInTestApp } from '@backstage/test-utils'; 19 | import { CortexApi } from '../../../api/CortexApi'; 20 | import { cortexApiRef } from '../../../api'; 21 | import { HeatmapPage } from './HeatmapPage'; 22 | import { rootRouteRef } from '../../../routes'; 23 | import { Scorecard } from '../../../api/types'; 24 | 25 | describe('HeatmapPage', () => { 26 | const cortexApi: Partial = { 27 | getScorecards: () => 28 | Promise.resolve([ 29 | { 30 | creator: { name: 'Billy Bob', email: 'billybob@cortex.io' }, 31 | id: 1, 32 | name: 'My Scorecard', 33 | description: 'Some description', 34 | rules: [], 35 | nextUpdated: '2021-08-25T04:00:00', 36 | tag: '', 37 | filter: null, 38 | exemptions: { 39 | autoApprove: false, 40 | enabled: false, 41 | }, 42 | isDraft: false, 43 | notifications: { 44 | enabled: false, 45 | }, 46 | } as Scorecard, 47 | ]), 48 | }; 49 | 50 | const renderWrapped = (children: React.ReactNode) => 51 | render( 52 | wrapInTestApp( 53 | 54 | {children} 55 | , 56 | { 57 | mountedRoutes: { 58 | '/': rootRouteRef as any, 59 | }, 60 | }, 61 | ), 62 | ); 63 | 64 | it('should render', async () => { 65 | const { findByText, queryAllByText } = renderWrapped(); 66 | 67 | expect(await findByText(/Bird's Eye/)).toBeVisible(); 68 | expect(await queryAllByText(/Select a Scorecard/)).toHaveLength(2); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/components/ReportsPage/HeatmapPage/colorClasses.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { BackstageTheme } from '@backstage/theme'; 17 | import { makeStyles } from '@material-ui/core'; 18 | 19 | export const useColorCellStyles = makeStyles(theme => ({ 20 | root: { 21 | border: 'none', 22 | textAlign: 'center', 23 | minWidth: '100px', 24 | borderRadius: 0, 25 | }, 26 | success1: { 27 | background: '#00ca9b', 28 | color: theme.palette.common.black, 29 | }, 30 | success2: { 31 | background: '#60e6c7', 32 | color: theme.palette.common.black, 33 | }, 34 | success3: { 35 | background: '#b0ecde', 36 | color: theme.palette.common.black, 37 | }, 38 | warning1: { 39 | background: '#ffecb3', 40 | color: theme.palette.common.black, 41 | }, 42 | warning2: { 43 | background: '#ffca28', 44 | color: theme.palette.common.black, 45 | }, 46 | warning3: { 47 | background: '#ffa001', 48 | color: theme.palette.common.black, 49 | }, 50 | danger1: { 51 | background: '#ffd6dd', 52 | color: theme.palette.common.black, 53 | }, 54 | danger2: { 55 | background: '#fe899e', 56 | color: theme.palette.common.black, 57 | }, 58 | danger3: { 59 | background: '#fd496a', 60 | color: theme.palette.common.black, 61 | }, 62 | })); 63 | -------------------------------------------------------------------------------- /src/components/ReportsPage/HeatmapPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { HeatmapPage } from './HeatmapPage'; 17 | -------------------------------------------------------------------------------- /src/components/ReportsPage/ProgressPage/SerieFilter.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { Autocomplete } from '@material-ui/lab'; 18 | import { Checkbox, TextField } from '@material-ui/core'; 19 | 20 | interface SerieFilterProps { 21 | options: string[]; 22 | onSelect: (filters: string[]) => void; 23 | } 24 | 25 | export const SerieFilter = ({ options, onSelect }: SerieFilterProps) => { 26 | return ( 27 | { 31 | onSelect(values as string[]); 32 | }} 33 | renderOption={(option, { selected }) => ( 34 | 35 | 36 | {option} 37 | 38 | )} 39 | renderInput={params => ( 40 | 41 | )} 42 | /> 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/ReportsPage/ProgressPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ProgressPage } from './ProgressPage'; 17 | -------------------------------------------------------------------------------- /src/components/ReportsPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ReportsPage } from './ReportsPage'; 17 | -------------------------------------------------------------------------------- /src/components/ScorecardRefLink/ScorecardRefLink.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { useRouteRef } from '@backstage/core-plugin-api'; 18 | import { scorecardRouteRef } from '../../routes'; 19 | import { Link } from '@backstage/core-components'; 20 | 21 | interface ScorecardRefLinkProps { 22 | scorecardId: number; 23 | children?: React.ReactNode; 24 | className?: string; 25 | } 26 | 27 | export const ScorecardRefLink = ({ 28 | scorecardId, 29 | children, 30 | className, 31 | }: ScorecardRefLinkProps) => { 32 | const scorecardRef = useRouteRef(scorecardRouteRef); 33 | 34 | return ( 35 | 36 | {children} 37 | 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /src/components/ScorecardRefLink/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardRefLink } from './ScorecardRefLink'; 17 | -------------------------------------------------------------------------------- /src/components/ScorecardServiceRefLink/ScorecardServiceRefLink.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { useRouteRef } from '@backstage/core-plugin-api'; 18 | import { scorecardServiceDetailsRouteRef } from '../../routes'; 19 | import { Link } from '@backstage/core-components'; 20 | import { parseEntityRef } from '@backstage/catalog-model'; 21 | import { defaultComponentRefContext } from '../../utils/ComponentUtils'; 22 | 23 | interface ScorecardRefLinkProps { 24 | scorecardId: number; 25 | children?: React.ReactNode; 26 | componentRef: string; 27 | } 28 | 29 | export const ScorecardServiceRefLink = ({ 30 | componentRef, 31 | scorecardId, 32 | children, 33 | }: ScorecardRefLinkProps) => { 34 | const scorecardServiceDetailsRef = useRouteRef( 35 | scorecardServiceDetailsRouteRef, 36 | ); 37 | 38 | const entityName = parseEntityRef(componentRef, defaultComponentRefContext); 39 | 40 | return ( 41 | 47 | {children} 48 | 49 | ); 50 | }; 51 | -------------------------------------------------------------------------------- /src/components/ScorecardServiceRefLink/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardServiceRefLink } from './ScorecardServiceRefLink'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardCard/ScorecardCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { Scorecard } from '../../../api/types'; 18 | import { useRouteRef } from '@backstage/core-plugin-api'; 19 | import { scorecardRouteRef } from '../../../routes'; 20 | import { ListCard } from '../../ListCard'; 21 | import { useUiExtensions } from '../../../utils/hooks'; 22 | 23 | type ScorecardCardProps = { 24 | scorecard: Scorecard; 25 | }; 26 | 27 | export const ScorecardCard = ({ scorecard }: ScorecardCardProps) => { 28 | const scorecardRef = useRouteRef(scorecardRouteRef); 29 | 30 | const { uiExtensions } = useUiExtensions(); 31 | const customCardDisplayOptions = uiExtensions?.scorecards?.cardDisplayOptions; 32 | 33 | return ( 34 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardCard/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardCard } from './ScorecardCard'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardFilterDialog/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardFilterDialogWrapper as ScorecardFilterDialog } from './ScorecardFilterDialog'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardMetadataCard/ScorecardMetadataCard.test.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { Fixtures, renderWrapped } from '../../../../utils/TestUtils'; 18 | import { ScorecardMetadataCard } from './ScorecardMetadataCard'; 19 | import { FilterType } from '../../../../api/types'; 20 | 21 | describe('ScorecardMetadataCard', () => { 22 | it('should render metadata', async () => { 23 | const scorecard = Fixtures.scorecard({ 24 | creator: { name: 'Bob Jones', email: 'bobjones@cortex.io' }, 25 | description: 'My Description', 26 | filter: { 27 | type: FilterType.SERVICE_FILTER, 28 | entityGroupFilter: { 29 | entityGroups: ['tag1'], 30 | excludedEntityGroups: ['tag2'], 31 | }, 32 | }, 33 | }); 34 | const { findByText } = renderWrapped( 35 | , 36 | ); 37 | 38 | expect(await findByText('My Description')).toBeVisible(); 39 | expect(await findByText('My Scorecard')).toBeVisible(); 40 | expect(await findByText('my-scorecard')).toBeVisible(); 41 | expect(await findByText(/tag1/)).toBeVisible(); 42 | expect(await findByText(/tag2/)).toBeVisible(); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardMetadataCard/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardMetadataCard } from './ScorecardMetadataCard'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardReportsTab/ScorecardReportsTab.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { Scorecard } from '../../../../api/types'; 18 | import { ItemCardGrid } from '@backstage/core-components'; 19 | import { ReportsPageCard } from '../../../ReportsPage/ReportsPage'; 20 | 21 | interface ScorecardReportsTabProps { 22 | scorecard: Scorecard; 23 | } 24 | 25 | export const ScorecardReportsTab = ({ 26 | scorecard, 27 | }: ScorecardReportsTabProps) => { 28 | return ( 29 | 30 | 35 | 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardReportsTab/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardReportsTab } from './ScorecardReportsTab'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardRuleExemptionsTab/RuleExemptionStatus.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { makeStyles, Box, Typography } from '@material-ui/core'; 18 | import classnames from 'classnames'; 19 | import React from 'react'; 20 | import { ExemptionStatusResponseType } from '../../../../api/types'; 21 | import { fallbackPalette } from '../../../../styles/styles'; 22 | 23 | export interface RuleExemptionStatusProps { 24 | type: ExemptionStatusResponseType; 25 | } 26 | 27 | const useRuleExemptionStyles = makeStyles(theme => ({ 28 | root: { 29 | display: 'block', 30 | width: 100, 31 | padding: theme.spacing(0.5, 1), 32 | marginRight: theme.spacing(1), 33 | }, 34 | pending: { 35 | backgroundColor: fallbackPalette.common.grayLight, 36 | }, 37 | approved: { 38 | backgroundColor: fallbackPalette.common.orange[100], 39 | }, 40 | text: { 41 | color: fallbackPalette.common.black, 42 | }, 43 | })); 44 | 45 | const typeToNameMap: Record = { 46 | [ExemptionStatusResponseType.APPROVED]: 'Exempt', 47 | [ExemptionStatusResponseType.PENDING]: 'Requested', 48 | [ExemptionStatusResponseType.REJECTED]: 'Rejected', 49 | }; 50 | 51 | export const RuleExemptionStatus: React.FC = ({ 52 | type, 53 | }) => { 54 | const classes = useRuleExemptionStyles(); 55 | 56 | return ( 57 | 63 | 64 | {typeToNameMap[type]} 65 | 66 | 67 | ); 68 | }; 69 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardRuleExemptionsTab/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardRuleExemptionsTab } from './ScorecardRuleExemptionsTab'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardRulesTab/ScorecardScoresRulesCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useMemo } from 'react'; 17 | import { Scorecard } from '../../../../api/types'; 18 | import { Box } from '@material-ui/core'; 19 | import { sortRules } from '../../../../utils/ScorecardRules'; 20 | import { CortexInfoCard } from '../../../Common/CortexInfoCard'; 21 | import { ScorecardRuleRow } from './ScorecardRuleRow'; 22 | 23 | interface SScorecardScoresRulesCardProps { 24 | scorecard: Scorecard; 25 | } 26 | 27 | export const ScorecardScoresRulesCard = ({ 28 | scorecard, 29 | }: SScorecardScoresRulesCardProps) => { 30 | const sortedRules = useMemo(() => sortRules(scorecard.rules), [scorecard]); 31 | 32 | return ( 33 | 34 | 35 | {sortedRules.map(rule => ( 36 | 40 | ))} 41 | 42 | 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardRulesTab/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardScoresRulesCard } from './ScorecardScoresRulesCard'; 17 | export { ScorecardLadderRulesCard } from './ScorecardLadderRulesCard'; 18 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardStatsCard/ScorecardScoresStatsCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useMemo } from 'react'; 17 | import { ScorecardServiceScore } from '../../../../api/types'; 18 | import { Paper } from '@material-ui/core'; 19 | import { isEmpty, mean, round } from 'lodash'; 20 | import { median, quantileSorted } from 'simple-statistics'; 21 | import Stats from '../../../Common/Stats'; 22 | import StatsItem from '../../../Common/StatsItem'; 23 | 24 | interface ScorecardScoresStatsCardProps { 25 | entityCategory: string; 26 | scores: ScorecardServiceScore[]; 27 | } 28 | 29 | export const ScorecardScoresStatsCard = ({ 30 | entityCategory, 31 | scores, 32 | }: ScorecardScoresStatsCardProps) => { 33 | const type = 'PERCENTAGE'; 34 | const percentages = useMemo( 35 | () => (isEmpty(scores) ? [0] : scores.map(score => score.scorePercentage)), 36 | [scores], 37 | ); 38 | 39 | const avgScore = useMemo(() => round(mean(percentages), 2), [percentages]); 40 | const percentile = useMemo( 41 | () => quantileSorted([...percentages].reverse(), 0.8), 42 | [percentages], 43 | ); 44 | const medianScore = useMemo(() => median(percentages), [percentages]); 45 | 46 | return ( 47 | 48 | 49 | 55 | 61 | 67 | 72 | 73 | 74 | ); 75 | }; 76 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardStatsCard/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardLadderStatsCard } from './ScorecardLadderStatsCard'; 17 | export { ScorecardScoresStatsCard } from './ScorecardScoresStatsCard'; 18 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardsScoresTab/ScorecardsScoresTableConfig.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { TableColumn } from '@backstage/core-components'; 17 | import { ScorecardScoreLadderLevel } from '../../../../api/types'; 18 | import { ScorecardLadderLevelBadge } from '../../../Common/ScorecardLadderLevelBadge'; 19 | import React from 'react'; 20 | import { LinearProgressWithLabel } from '../../../Common/LinearProgressWithLabel'; 21 | import { percentify } from '../../../../utils/NumberUtils'; 22 | import { ClassNameMap } from '@material-ui/core/styles/withStyles'; 23 | 24 | export const PAGE_SIZE = 25; 25 | 26 | export const levelSort = ( 27 | data1: { level?: ScorecardScoreLadderLevel }, 28 | data2: { level?: ScorecardScoreLadderLevel }, 29 | ) => { 30 | const level1 = data1?.level?.rank ?? -1; 31 | const level2 = data2?.level?.rank ?? -1; 32 | 33 | return level2 - level1; 34 | }; 35 | 36 | export const scorePercentageSort = ( 37 | data1: { scorePercentage?: number }, 38 | data2: { scorePercentage?: number }, 39 | ) => { 40 | const score1 = data1?.scorePercentage ?? 0; 41 | const score2 = data2?.scorePercentage ?? 0; 42 | 43 | return score2 - score1; 44 | }; 45 | 46 | export const levelColumn: TableColumn = { 47 | field: 'level', 48 | title: 'Level', 49 | width: '20%', 50 | customSort: levelSort, 51 | render: ({ level }: { level?: ScorecardScoreLadderLevel }) => 52 | level ? ( 53 | 58 | ) : null, 59 | }; 60 | 61 | export const scoreColumn: TableColumn = { 62 | field: 'scorePercentage', 63 | sorting: true, 64 | title: 'Score', 65 | width: '10%', 66 | render: (data: { 67 | linearProgressClasses?: ClassNameMap; 68 | scorePercentage?: number; 69 | }) => { 70 | return ( 71 | 75 | ); 76 | }, 77 | customSort: scorePercentageSort, 78 | }; 79 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardsScoresTab/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardsScoresTable } from './ScorecardsScoresTable'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardsTableCard/ScorecardResultDetails.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useMemo } from 'react'; 17 | import { List, ListItem, Typography } from '@material-ui/core'; 18 | import { RuleResultDetails } from './RuleResultDetails'; 19 | import { ruleName, RuleOutcome } from '../../../../api/types'; 20 | import { isApplicableRuleOutcome } from '../../../../utils/ScorecardRules'; 21 | 22 | interface ScorecardResultDetailsProps { 23 | ruleOutcomes: RuleOutcome[]; 24 | hideWeights?: boolean; 25 | } 26 | 27 | export const ScorecardResultDetails = ({ 28 | ruleOutcomes, 29 | hideWeights, 30 | }: ScorecardResultDetailsProps) => { 31 | const sortedRules = useMemo( 32 | () => 33 | [...ruleOutcomes].sort((a, b) => { 34 | // Make applicable rule outcomes "smaller" so they'll show up first 35 | if (isApplicableRuleOutcome(a) && !isApplicableRuleOutcome(b)) { 36 | return -1; 37 | } 38 | if (!isApplicableRuleOutcome(a) && isApplicableRuleOutcome(b)) { 39 | return 1; 40 | } 41 | if (isApplicableRuleOutcome(a) && isApplicableRuleOutcome(b)) { 42 | if (a.score === b.score) { 43 | return ruleName(a.rule).localeCompare(ruleName(b.rule)); 44 | } else { 45 | return a.score - b.score; 46 | } 47 | } 48 | return ruleName(a.rule).localeCompare(ruleName(b.rule)); 49 | }), 50 | [ruleOutcomes], 51 | ); 52 | 53 | return ( 54 | 55 | {sortedRules.length === 0 && ( 56 | 57 | No rules. 58 | 59 | )} 60 | {sortedRules.map(rule => ( 61 | 66 | ))} 67 | 68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/ScorecardsTableCard/__snapshots__/ScorecardResultDetails.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ScorecardResultDetails check order of applicable scorecard service rules being displayed 1`] = ` 4 | 5 | 19 | 34 | 48 | 62 | 75 | 0", 83 | "id": 3, 84 | "weight": 1, 85 | }, 86 | "type": "NOT_APPLICABLE", 87 | } 88 | } 89 | /> 90 | 91 | `; 92 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardDetailsPage } from './ScorecardDetailsPage'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardsPage/ScorecardsPage.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from 'react'; 17 | import { Button } from '@material-ui/core'; 18 | import { useAsync } from 'react-use'; 19 | import { Route, Routes } from 'react-router-dom'; 20 | 21 | import { EmptyState, Progress, WarningPanel } from '@backstage/core-components'; 22 | import { useApi } from '@backstage/core-plugin-api'; 23 | 24 | import { cortexApiRef } from '../../../api'; 25 | import { ScorecardDetailsPage } from '../ScorecardDetailsPage'; 26 | import { ScorecardsServicePage } from '../ScorecardsServicePage'; 27 | 28 | import { ScorecardList } from './ScorecardList'; 29 | 30 | export const ScorecardsPage = () => { 31 | const cortexApi = useApi(cortexApiRef); 32 | 33 | const { 34 | value: scorecards, 35 | loading, 36 | error, 37 | } = useAsync(async () => { 38 | return await cortexApi.getScorecards(); 39 | }, []); 40 | 41 | if (loading) { 42 | return ; 43 | } 44 | 45 | if (error) { 46 | return ( 47 | 48 | {error.message} 49 | 50 | ); 51 | } 52 | 53 | if (!scorecards?.length) { 54 | return ( 55 | 65 | Read more 66 | 67 | } 68 | /> 69 | ); 70 | } 71 | 72 | return ( 73 | 74 | } 77 | /> 78 | } /> 79 | } /> 80 | 81 | ); 82 | }; 83 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardsPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardsPage } from './ScorecardsPage'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardsServicePage/ScorecardsServiceRulesFilter.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { FormControl, Select, MenuItem } from '@material-ui/core'; 17 | import React, { useMemo } from 'react'; 18 | import { getRuleTitle } from '../../../utils/ScorecardRules'; 19 | import { StringIndexable } from '../../ReportsPage/AllScorecardsPage/HeatmapUtils'; 20 | import { Rule, RuleOutcome } from '../../../api/types'; 21 | 22 | interface ScorecardsServiceRulesFilterProps { 23 | selected?: Rule; 24 | setSelected: (value?: Rule) => void; 25 | rules: RuleOutcome[]; 26 | } 27 | 28 | const allRulesValue = 'all-rules'; 29 | 30 | const ScorecardsServiceRulesFilter: React.FC = 31 | ({ selected, setSelected, rules }) => { 32 | const rulesMap: StringIndexable = useMemo(() => { 33 | return rules.reduce( 34 | (acc, curr) => ({ 35 | ...acc, 36 | [getRuleTitle(curr.rule)]: curr.rule, 37 | }), 38 | { [allRulesValue]: undefined }, 39 | ); 40 | }, [rules]); 41 | 42 | return ( 43 | 44 | 60 | 61 | ); 62 | }; 63 | 64 | export default ScorecardsServiceRulesFilter; 65 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardsServicePage/ScorecardsServiceTooltipRuleRow.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Box, Typography } from '@material-ui/core'; 18 | import React from 'react'; 19 | import { 20 | RuleDetail, 21 | getRuleTitle, 22 | isRuleFailing, 23 | } from '../../../utils/ScorecardRules'; 24 | import CheckCircleIcon from '@material-ui/icons/CheckCircle'; 25 | import CancelIcon from '@material-ui/icons/Cancel'; 26 | import { useIconsStyles } from '../../../styles/styles'; 27 | import classNames from 'classnames'; 28 | import { isNil } from 'lodash'; 29 | 30 | interface ScorecardsServiceTooltipRuleRowProps { 31 | rule: RuleDetail; 32 | } 33 | 34 | export const ScorecardsServiceTooltipRuleRow: React.FC = 35 | ({ rule }) => { 36 | const isFailing = isRuleFailing(rule); 37 | const iconStyles = useIconsStyles(); 38 | 39 | return ( 40 | 41 | {!isNil(rule.score) && ( 42 | 43 | {isFailing ? ( 44 | 51 | ) : ( 52 | 58 | )} 59 | 60 | )} 61 | 62 | 69 | {getRuleTitle(rule)} 70 | 71 | 72 | 73 | ); 74 | }; 75 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardsServicePage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { ScorecardsServicePage } from './ScorecardsServicePage'; 17 | -------------------------------------------------------------------------------- /src/components/Scorecards/ScorecardsServicePage/useScorecardServiceRuleRowStyles.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { makeStyles } from '@material-ui/core'; 17 | import { fallbackPalette } from '../../../styles/styles'; 18 | 19 | const useScorecardServiceRuleRowStyle = makeStyles(theme => ({ 20 | root: { 21 | display: 'flex', 22 | flexDirection: 'row', 23 | justifyContent: 'space-between', 24 | marginBottom: theme.spacing(1), 25 | '&:last-child': { 26 | marginBottom: 0, 27 | }, 28 | }, 29 | expandIcon: { 30 | padding: 0, 31 | }, 32 | ruleDescription: { 33 | margin: theme.spacing(1, 0), 34 | '& p': { 35 | margin: 0, 36 | }, 37 | }, 38 | ruleQuery: { 39 | color: fallbackPalette.common.gray, 40 | fontFamily: 'Monospace', 41 | fontSize: 12, 42 | }, 43 | })); 44 | 45 | export default useScorecardServiceRuleRowStyle; 46 | -------------------------------------------------------------------------------- /src/components/SettingsPage/SettingsPage.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { Grid } from '@material-ui/core'; 17 | import React from 'react'; 18 | import { Content, ContentHeader } from '@backstage/core-components'; 19 | import { SyncCard } from './SyncCard'; 20 | import { SyncJobsTable } from './SyncJobsTable'; 21 | 22 | export const SettingsPage = () => { 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/components/SettingsPage/SyncJobsTable.test.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { CortexApi } from '../../api/CortexApi'; 17 | import React from 'react'; 18 | import { JobsResponse, JobStatus } from '../../api/types'; 19 | import moment from 'moment'; 20 | import { renderWrapped } from '../../utils/TestUtils'; 21 | import { SyncJobsTable } from './SyncJobsTable'; 22 | 23 | describe('SyncJobsTable', () => { 24 | beforeEach(() => { 25 | jest.useFakeTimers('modern'); 26 | jest.setSystemTime(new Date(2024, 4, 7, 14, 24, 33)); 27 | }); 28 | 29 | afterEach(() => { 30 | jest.useRealTimers(); 31 | }); 32 | 33 | it('should render timestamps correctly', async () => { 34 | const cortexApi: Partial = { 35 | getSyncJobs(): Promise { 36 | return Promise.resolve({ 37 | jobs: [ 38 | { 39 | id: 'backstage-sync-12345', 40 | status: JobStatus.Done, 41 | dateCreated: moment.utc().toISOString(), 42 | }, 43 | { 44 | id: 'backstage-sync-abcd', 45 | status: JobStatus.TimedOut, 46 | dateCreated: moment.utc().subtract(1, 'days').toISOString(), 47 | }, 48 | ], 49 | }); 50 | }, 51 | }; 52 | 53 | const { checkForText } = renderWrapped(, cortexApi); 54 | 55 | await checkForText('May 7th 2024, 2:24:33 pm'); 56 | await checkForText('May 6th 2024, 2:24:33 pm'); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /src/components/SettingsPage/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { SettingsPage } from './SettingsPage'; 17 | -------------------------------------------------------------------------------- /src/components/StatsCard/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { StatsCard } from './StatsCard'; 17 | -------------------------------------------------------------------------------- /src/components/Timeseries/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { Timeseries } from './Timeseries'; 17 | -------------------------------------------------------------------------------- /src/extensions.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { cortexPlugin } from './plugin'; 17 | import { createComponentExtension } from '@backstage/core-plugin-api'; 18 | 19 | export const ScorecardsPage = cortexPlugin.provide( 20 | createComponentExtension({ 21 | name: 'ScorecardsPage', 22 | component: { 23 | lazy: () => 24 | import('./components/Scorecards/ScorecardsPage').then( 25 | m => m.ScorecardsPage, 26 | ), 27 | }, 28 | }), 29 | ); 30 | -------------------------------------------------------------------------------- /src/filters.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { Entity } from '@backstage/catalog-model'; 17 | 18 | export type EntityFilterGroup = { 19 | name: string; 20 | groupProperty: (entity: Entity) => string[] | undefined; 21 | formatProperty?: (property: string) => string; 22 | }; 23 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import CortexIconComponent from './assets/cortex.icon.svg'; 17 | import { IconComponent } from '@backstage/core-plugin-api'; 18 | import('@cortexapps/birdseye/index.css' as any); 19 | 20 | export { 21 | cortexPlugin, 22 | CortexPage, 23 | CortexScorecardWidget, 24 | CortexGroupActionItemsWidget, 25 | EntityCortexContent, 26 | SystemCortexContent, 27 | extendableCortexPlugin, 28 | CortexHomepage, 29 | CortexInsightsCard, 30 | } from './plugin'; 31 | export { extensionApiRef } from './api/ExtensionApi'; 32 | export type { CortexScorecardWidgetProps } from './components/CortexScorecardWidget/CortexScorecardWidget'; 33 | export type { 34 | ExtensionApi, 35 | CortexYaml, 36 | CustomMapping, 37 | } from '@cortexapps/backstage-plugin-extensions'; 38 | export type { EntityFilterGroup } from './filters'; 39 | export const CortexIcon: IconComponent = CortexIconComponent as IconComponent; 40 | -------------------------------------------------------------------------------- /src/plugin.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { cortexPlugin } from './plugin'; 17 | import { CortexPage } from './components/CortexPage'; 18 | 19 | describe('cortex', () => { 20 | it('should export plugin', () => { 21 | expect(cortexPlugin).toBeDefined(); 22 | expect(CortexPage).toBeDefined(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/routes.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { createRouteRef, createSubRouteRef } from '@backstage/core-plugin-api'; 17 | 18 | export const rootRouteRef = createRouteRef({ 19 | id: 'cortex', 20 | }); 21 | 22 | export const scorecardsRouteRef = createSubRouteRef({ 23 | id: 'scorecards', 24 | parent: rootRouteRef, 25 | path: '/scorecards', 26 | }); 27 | 28 | export const scorecardRouteRef = createSubRouteRef({ 29 | id: 'scorecard', 30 | path: '/scorecards/:id', 31 | parent: rootRouteRef, 32 | }); 33 | 34 | export const scorecardServiceDetailsRouteRef = createSubRouteRef({ 35 | id: 'scorecardServiceDetails', 36 | path: '/scorecards/:scorecardId/:namespace/:kind/:name', 37 | parent: rootRouteRef, 38 | }); 39 | 40 | export const initiativeRouteRef = createSubRouteRef({ 41 | id: 'initiative', 42 | path: '/initiatives/:id', 43 | parent: rootRouteRef, 44 | }); 45 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import '@testing-library/jest-dom'; 17 | import 'cross-fetch/polyfill'; 18 | import 'jest-canvas-mock'; 19 | import Adapter from 'enzyme-adapter-react-16'; 20 | import { configure } from 'enzyme'; 21 | 22 | configure({ adapter: new Adapter() }); 23 | -------------------------------------------------------------------------------- /src/utils/ComponentUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Entity } from '@backstage/catalog-model'; 18 | import { CustomMapping } from '@cortexapps/backstage-plugin-extensions'; 19 | import { isNil, merge } from 'lodash'; 20 | import { HomepageEntity } from '../api/userInsightTypes'; 21 | 22 | export type EntityRefContext = { 23 | defaultKind?: string; 24 | defaultNamespace?: string; 25 | }; 26 | 27 | export const defaultComponentRefContext: EntityRefContext = { 28 | defaultKind: 'Component', 29 | defaultNamespace: 'default', 30 | }; 31 | 32 | export const defaultGroupRefContext: EntityRefContext = { 33 | defaultKind: 'Group', 34 | defaultNamespace: 'default', 35 | }; 36 | 37 | export const defaultSystemRefContext: EntityRefContext = { 38 | defaultKind: 'System', 39 | defaultNamespace: 'default', 40 | }; 41 | 42 | export const applyCustomMappings = ( 43 | entity: Entity, 44 | customMappings: CustomMapping[], 45 | ) => { 46 | return customMappings.reduce( 47 | (modifiedEntity: Entity, customMapping: CustomMapping) => { 48 | const partialEntity = { 49 | spec: customMapping(entity), 50 | }; 51 | 52 | merge(modifiedEntity, partialEntity); 53 | 54 | return modifiedEntity; 55 | }, 56 | entity, 57 | ); 58 | }; 59 | 60 | export const entityComponentRef = ( 61 | entitiesByTag: Record, 62 | tag: string, 63 | ) => { 64 | const entity = entitiesByTag[tag]; 65 | if (isNil(entity)) { 66 | return tag; 67 | } 68 | return isNil(entity.definition) || isNil(entity.definition.name) 69 | ? entity.codeTag 70 | : `${entity.definition.kind}:${entity.definition.namespace}/${entity.definition.name}`; 71 | }; 72 | -------------------------------------------------------------------------------- /src/utils/HookUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { useEffect, useRef } from 'react'; 18 | 19 | export const useInterval = ( 20 | callback: (...args: any[]) => Promise, 21 | /** Use null delay to end timer */ 22 | delay: number | null, 23 | ) => { 24 | const savedCallbackRef = useRef<(...args: any[]) => void>(); 25 | 26 | useEffect(() => { 27 | savedCallbackRef.current = callback; 28 | }, [callback]); 29 | 30 | useEffect(() => { 31 | const handler = (...args: any[]) => savedCallbackRef.current!(...args); 32 | 33 | if (delay !== null) { 34 | const intervalId = setInterval(handler, delay); 35 | return () => clearInterval(intervalId); 36 | } 37 | return undefined; 38 | }, [delay]); 39 | }; 40 | -------------------------------------------------------------------------------- /src/utils/MathUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | export type ExponentialBackoffOptions = { 18 | /** Starting delay, in milliseconds */ 19 | initialDelay: number; 20 | maxDelay?: number; 21 | maxTries?: number; 22 | multiplier: number; 23 | numTries?: number; 24 | }; 25 | 26 | /** 27 | * Immutable ExponentialBackoff helper 28 | * 29 | * @param multiplier Multiplier for exponential growth (e.g. 2.0 would yield [1000 -> 2000 -> 4000]) 30 | * @param maxDelay Ceiling for delay, subsequent advances will not increase delay 31 | * @param maxTries Max tries, after which delay will return null 32 | * @param numTries Starting point for number of tries 33 | */ 34 | export class ExponentialBackoff { 35 | currentDelay; 36 | maxDelay; 37 | maxTries; 38 | multiplier; 39 | numTries; 40 | 41 | constructor(opts: ExponentialBackoffOptions) { 42 | const { initialDelay, multiplier, maxDelay, maxTries, numTries } = opts; 43 | this.currentDelay = initialDelay; 44 | this.maxDelay = maxDelay ?? Number.MAX_SAFE_INTEGER; 45 | this.maxTries = maxTries; 46 | this.multiplier = multiplier; 47 | this.numTries = numTries ?? 0; 48 | } 49 | 50 | delay(): number | null { 51 | if (this.numTries === this.maxTries) { 52 | return null; 53 | } 54 | return Math.min(this.currentDelay, this.maxDelay); 55 | } 56 | 57 | advance(): ExponentialBackoff { 58 | let nextDelay = Math.min( 59 | this.currentDelay * this.multiplier, 60 | this.maxDelay ?? Number.MAX_SAFE_INTEGER, 61 | ); 62 | 63 | return new ExponentialBackoff({ 64 | initialDelay: nextDelay, 65 | multiplier: this.multiplier, 66 | maxDelay: this.maxDelay, 67 | maxTries: this.maxTries, 68 | numTries: this.numTries + 1, 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/utils/MomentUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { isNil } from 'lodash'; 17 | import moment from 'moment'; 18 | 19 | export const daysUntil = (endDate?: string) => { 20 | if (isNil(endDate)) { 21 | return undefined; 22 | } 23 | 24 | const currDate = moment(); 25 | const expirationDate = moment(endDate); 26 | return expirationDate.diff(currDate, 'days'); 27 | }; 28 | 29 | export const getFormattedDate = ( 30 | dateStr: string | Date, 31 | format: string = 'MM/DD/YYYY', 32 | ) => { 33 | const date = moment(dateStr); 34 | return date.format(format); 35 | }; 36 | -------------------------------------------------------------------------------- /src/utils/NumberUtils.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { percentify, safeDivide } from './NumberUtils'; 17 | 18 | describe('NumberUtils', () => { 19 | it('percentify handles 0', () => { 20 | expect(percentify(0)).toBe(0); 21 | }); 22 | 23 | it('percentify handles 1', () => { 24 | expect(percentify(1)).toBe(100); 25 | }); 26 | 27 | it('percentify handles decimals less than 1 that lack precise float representation', () => { 28 | expect(percentify(0.58)).toBe(58); 29 | }); 30 | 31 | it('percentify handles values greater than 1', () => { 32 | expect(percentify(29)).toBe(29); 33 | }); 34 | 35 | it('safeDivide should handle 0 in the denominator', () => { 36 | expect(safeDivide(2, 0)).toBe(0); 37 | }); 38 | 39 | it('safeDivide should handle normal division', () => { 40 | expect(safeDivide(1, 2)).toBe(0.5); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/utils/NumberUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { round } from 'lodash'; 18 | 19 | export function percentify(value: number = 0, decimal: number = 0): number { 20 | return value <= 1 ? round(value * 100, decimal) : round(value, decimal); 21 | } 22 | 23 | /** 24 | * Safe division that converts division by 0 errors to 0 25 | * @param numerator Numerator 26 | * @param denominator Denominator 27 | */ 28 | export const safeDivide = (numerator: number, denominator: number) => { 29 | if (denominator === 0) { 30 | return 0; 31 | } else { 32 | return numerator / denominator; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /src/utils/ScorecardFilterUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { CategoryFilter, FilterType, Scorecard } from "../api/types"; 17 | 18 | export function isScorecardTeamBased(scorecard?: Scorecard): boolean { 19 | if (!scorecard) return false; 20 | 21 | switch (scorecard.filter?.type) { 22 | case FilterType.TEAM_FILTER: 23 | return true; 24 | case FilterType.CQL_FILTER: 25 | return scorecard.filter?.category === CategoryFilter.Team; 26 | case FilterType.COMPOUND_FILTER: 27 | const { typeFilter } = scorecard.filter; 28 | return !!typeFilter?.include && typeFilter?.types.includes('team'); 29 | default: 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/ScorecardLadderUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { ScorecardLadder } from '../api/types'; 17 | 18 | export const getSortedLadderLevels = (ladder: ScorecardLadder) => 19 | [...(ladder?.levels ?? [])].sort((a, b) => a.rank - b.rank); 20 | 21 | export const getSortedLadderLevelNames = ( 22 | ladder: ScorecardLadder, 23 | includeNoLevel: boolean = false, 24 | ) => { 25 | const sortedLevels = getSortedLadderLevels(ladder).map(level => level.name); 26 | return includeNoLevel ? ['No Level', ...sortedLevels] : sortedLevels; 27 | }; 28 | -------------------------------------------------------------------------------- /src/utils/SearchUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { get } from 'lodash'; 17 | 18 | export function hasText(item: T, key: string, query: string): boolean { 19 | return get(item, key)?.toLowerCase()?.includes(query.toLowerCase()); 20 | } 21 | 22 | export function searchItems( 23 | items: T[], 24 | keys: string[], 25 | searchQuery: string | undefined, 26 | ): T[] { 27 | if (searchQuery === undefined || searchQuery.length === 0) { 28 | return items; 29 | } 30 | 31 | return items.filter(item => 32 | keys.reduce( 33 | (containsQuery: boolean, key: string) => 34 | containsQuery || hasText(item, key, searchQuery), 35 | false, 36 | ), 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/utils/URLUtils.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { 17 | cortexScorecardPageUrl, 18 | cortexScorecardServicePageUrl, 19 | } from './URLUtils'; 20 | 21 | describe('URLUtils', () => { 22 | describe('cortexScorecardPageURL', () => { 23 | it('should use the provided url', () => { 24 | expect( 25 | cortexScorecardPageUrl({ 26 | scorecardId: '1', 27 | cortexUrl: 'https://test.domain', 28 | }), 29 | ).toEqual('https://test.domain/admin/scorecards/1'); 30 | }); 31 | }); 32 | 33 | describe('cortexScorecardServicePageURL', () => { 34 | it('should use custom domain url', () => { 35 | expect( 36 | cortexScorecardServicePageUrl({ 37 | scorecardId: '1', 38 | serviceId: '1', 39 | cortexUrl: 'https://test.domain', 40 | }), 41 | ).toEqual('https://test.domain/admin/scorecards/1?service=1'); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/utils/URLUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { StringifiableRecord, stringifyUrl } from 'query-string'; 17 | 18 | interface CortexScorecardServicePageURLProperties { 19 | scorecardId: string | number; 20 | serviceId: string | number; 21 | cortexUrl: string; 22 | } 23 | 24 | interface CortexScorecardPageURLProperties { 25 | scorecardId: string | number; 26 | cortexUrl: string; 27 | } 28 | 29 | interface CortexInitiativePageURLProperties { 30 | initiativeId: string | number; 31 | cortexUrl: string; 32 | } 33 | 34 | export const buildUrl = ( 35 | queryParamsObj: StringifiableRecord, 36 | pathname: string, 37 | ) => 38 | `${window.location.origin}${stringifyUrl({ 39 | url: pathname, 40 | query: queryParamsObj, 41 | })}`; 42 | 43 | export const cortexScorecardPageUrl = ({ 44 | scorecardId, 45 | cortexUrl, 46 | }: CortexScorecardPageURLProperties) => { 47 | return `${cortexUrl}/admin/scorecards/${scorecardId}`; 48 | }; 49 | 50 | export const cortexScorecardServicePageUrl = ({ 51 | scorecardId, 52 | serviceId, 53 | cortexUrl, 54 | }: CortexScorecardServicePageURLProperties) => 55 | `${cortexScorecardPageUrl({ 56 | scorecardId: scorecardId, 57 | cortexUrl: cortexUrl, 58 | })}?service=${serviceId}`; 59 | 60 | export const cortexInitiativePageUrl = ({ 61 | initiativeId, 62 | cortexUrl, 63 | }: CortexInitiativePageURLProperties) => 64 | `${cortexUrl}/admin/initiatives/${initiativeId}`; 65 | -------------------------------------------------------------------------------- /src/utils/WindowUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { isNil } from 'lodash'; 18 | 19 | const copyFallback = (text: string) => { 20 | const textArea = document.createElement('textarea'); 21 | textArea.value = text; 22 | 23 | // Avoid scrolling to bottom 24 | textArea.style.top = '0'; 25 | textArea.style.left = '0'; 26 | textArea.style.position = 'fixed'; 27 | 28 | document.body.appendChild(textArea); 29 | textArea.focus(); 30 | textArea.select(); 31 | 32 | try { 33 | // noinspection JSDeprecatedSymbols 34 | document.execCommand('copy'); 35 | } finally { 36 | document.body.removeChild(textArea); 37 | } 38 | }; 39 | 40 | export const copyText = async ( 41 | text?: string, 42 | onSuccess?: () => void, 43 | onFail?: () => void, 44 | ) => { 45 | if (isNil(text)) { 46 | return; 47 | } 48 | 49 | try { 50 | navigator.clipboard 51 | ? await navigator.clipboard.writeText(text) 52 | : copyFallback(text); 53 | 54 | onSuccess?.(); 55 | } catch (err) { 56 | onFail?.(); 57 | console.error(text); 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /src/utils/dates.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { isNil } from 'lodash'; 17 | import moment from 'moment'; 18 | 19 | export const daysUntil = (expirationDate: string | null) => { 20 | return !isNil(expirationDate) 21 | ? moment(expirationDate).diff(moment(), 'days') 22 | : undefined; 23 | }; 24 | -------------------------------------------------------------------------------- /src/utils/lookback.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { assertUnreachable } from './types'; 17 | import moment from 'moment'; 18 | 19 | export enum Lookback { 20 | DAYS_7, 21 | WEEKS_2, 22 | MONTHS_1, 23 | MONTHS_2, 24 | MONTHS_3, 25 | } 26 | 27 | export const lookbackLabels = (lookback: Lookback) => { 28 | // eslint-disable-next-line default-case 29 | switch (lookback) { 30 | case Lookback.DAYS_7: 31 | return 'Last 7 days'; 32 | case Lookback.WEEKS_2: 33 | return 'Last 2 weeks'; 34 | case Lookback.MONTHS_1: 35 | return 'Last 1 month'; 36 | case Lookback.MONTHS_2: 37 | return 'Last 2 months'; 38 | case Lookback.MONTHS_3: 39 | return 'Last 3 months'; 40 | } 41 | 42 | return assertUnreachable(lookback); 43 | }; 44 | 45 | export const getLookbackRange = (timeRange: Lookback) => { 46 | // eslint-disable-next-line default-case 47 | switch (timeRange) { 48 | case Lookback.DAYS_7: 49 | return [moment().subtract('1', 'week'), moment()]; 50 | case Lookback.WEEKS_2: 51 | return [moment().subtract('2', 'week'), moment()]; 52 | case Lookback.MONTHS_1: 53 | return [moment().subtract('1', 'month'), moment()]; 54 | case Lookback.MONTHS_2: 55 | return [moment().subtract('2', 'months'), moment()]; 56 | case Lookback.MONTHS_3: 57 | return [moment().subtract('3', 'months'), moment()]; 58 | } 59 | 60 | return assertUnreachable(timeRange); 61 | }; 62 | -------------------------------------------------------------------------------- /src/utils/pureHooks.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { 17 | EffectCallback, 18 | RefObject, 19 | useCallback, 20 | useEffect, 21 | useState, 22 | } from 'react'; 23 | 24 | // Equivalent to componentDidMount 25 | export const useMountEffect = (fun?: EffectCallback) => 26 | useEffect(() => fun?.(), []); // eslint-disable-line react-hooks/exhaustive-deps 27 | 28 | const getSize = (el: HTMLElement | null): { height: number; width: number } => { 29 | if (!el) { 30 | return { 31 | height: 0, 32 | width: 0, 33 | }; 34 | } 35 | 36 | return { 37 | height: el.offsetHeight, 38 | width: el.offsetWidth, 39 | }; 40 | }; 41 | 42 | export const useDimensions = (ref: RefObject) => { 43 | const [componentSize, setComponentSize] = useState( 44 | getSize(ref ? ref.current : null), 45 | ); 46 | 47 | const handleResize = useCallback(() => { 48 | setComponentSize({ 49 | height: ref.current ? ref.current.offsetHeight : 0, 50 | width: ref.current ? ref.current.offsetWidth : 0, 51 | }); 52 | }, [ref, setComponentSize]); 53 | 54 | const [observer] = useState(() => { 55 | if (typeof ResizeObserver === 'function') { 56 | return new ResizeObserver(() => { 57 | handleResize(); 58 | }); 59 | } 60 | 61 | return undefined; 62 | }); 63 | 64 | useMountEffect(() => { 65 | if (typeof ResizeObserver !== 'function') { 66 | window.addEventListener('resize', handleResize); 67 | } 68 | 69 | handleResize(); 70 | 71 | return () => { 72 | observer?.disconnect(); 73 | window.removeEventListener('resize', handleResize); 74 | }; 75 | }); 76 | 77 | return componentSize; 78 | }; 79 | -------------------------------------------------------------------------------- /src/utils/strings.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const suffixes: Record = { 18 | one: 'st', 19 | two: 'nd', 20 | few: 'rd', 21 | other: 'th', 22 | }; 23 | 24 | const english_ordinal_rules = new Intl.PluralRules('en', { type: 'ordinal' }); 25 | 26 | export function ordinal(value: number) { 27 | return suffixes[english_ordinal_rules.select(value)]; 28 | } 29 | 30 | // Only handles cases where the plural form has a suffix appended. Does not handle cases like baby => babies, person => people 31 | export const maybePluralizeOnlyNoun = ( 32 | count = 0, 33 | singularNoun = '', 34 | suffix = 's', 35 | ) => `${singularNoun}${count !== 1 ? suffix : ''}`; 36 | 37 | export function maybePluralize( 38 | count: number = 0, 39 | singularNoun: string = '', 40 | suffix: string = 's', 41 | ): string { 42 | return `${count} ${maybePluralizeOnlyNoun(count, singularNoun, suffix)}`; 43 | } 44 | 45 | export const joinWithSpecialLastJoin = ( 46 | strings: string[], 47 | { 48 | joiner = ', ', 49 | lastJoiner, 50 | lastJoinerWhenTwoItems = lastJoiner, 51 | }: { 52 | joiner?: string; 53 | lastJoiner: string; 54 | lastJoinerWhenTwoItems?: string; 55 | }, 56 | ) => { 57 | if (strings.length === 1) { 58 | return strings[0]; 59 | } else if (strings.length === 2) { 60 | return strings.join(lastJoinerWhenTwoItems); 61 | } 62 | return strings.slice(0, -1).join(joiner) + lastJoiner + strings.slice(-1); 63 | }; 64 | 65 | export const joinWithAnds = (strings: string[]) => { 66 | return joinWithSpecialLastJoin(strings, { 67 | lastJoiner: ', and ', 68 | lastJoinerWhenTwoItems: ' and ', 69 | }); 70 | }; 71 | 72 | export function isNotNullOrEmpty(value?: string | null): value is string { 73 | return (value?.trim()?.length ?? 0) > 0; 74 | } 75 | -------------------------------------------------------------------------------- /src/utils/types.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Cortex Applications, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { humanizeAnyEntityRef, nullsToUndefined } from './types'; 18 | import { defaultComponentRefContext } from './ComponentUtils'; 19 | 20 | describe('humanizeEntityRef', () => { 21 | it('should handle different kind and namespace', () => { 22 | expect(humanizeAnyEntityRef('Group:other-namespace/foo')).toBe( 23 | 'group:other-namespace/foo', 24 | ); 25 | }); 26 | 27 | it('should handle default kind only', () => { 28 | expect( 29 | humanizeAnyEntityRef( 30 | 'Component:other-namespace/foo', 31 | defaultComponentRefContext, 32 | ), 33 | ).toBe('other-namespace/foo'); 34 | }); 35 | 36 | it('should handle default namespace only', () => { 37 | expect( 38 | humanizeAnyEntityRef('Group:default/foo', defaultComponentRefContext), 39 | ).toBe('group:foo'); 40 | }); 41 | 42 | it('should handle both default kind and namespace', () => { 43 | expect( 44 | humanizeAnyEntityRef('Component:default/foo', defaultComponentRefContext), 45 | ).toBe('foo'); 46 | }); 47 | }); 48 | 49 | describe('nullsToUndefined', () => { 50 | it.each([ 51 | [{ a: null }, { a: undefined }], 52 | [ 53 | { a: null, b: null }, 54 | { a: undefined, b: undefined }, 55 | ], 56 | [{ a: { b: { c: null } } }, { a: { b: { c: undefined } } }], 57 | ])('should deeply convert nulls to undefined', (input, expected) => { 58 | expect(nullsToUndefined(input)).toEqual(expected); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@backstage/cli/config/tsconfig.json", 3 | "include": ["src", "dev"], 4 | "files": ["custom.d.ts"], 5 | "compilerOptions": { 6 | "outDir": "dist-types", 7 | "incremental": false, 8 | "sourceMap": true, 9 | "jsx": "react" 10 | } 11 | } 12 | --------------------------------------------------------------------------------