Compare commits

...

7 Commits

Author SHA1 Message Date
Med Mouine
7895114acd merge and small fixes 2024-05-27 18:45:26 -04:00
Med Mouine
2335b85099 more UI tweaks 2024-05-27 14:26:33 -04:00
Med Mouine
5e03926195 entity page UI and workflow fix 2024-05-27 14:24:10 -04:00
Med Mouine
acd9d2093e custom theme and UI 2024-05-27 14:18:59 -04:00
e12023ddd8 feat(DevSpacesPlugin): Adjusted plugin style a little 2024-05-27 13:55:11 -04:00
6f72adb559 feat(DevSpacesPlugin): Adjusted plugin style a little 2024-05-27 13:49:47 -04:00
bf2b5ef867 feat(DevSpacesPlugin): created a base plugin for DevSpaces 2024-05-27 13:02:29 -04:00
42 changed files with 3355 additions and 1066 deletions

View File

@ -1,6 +1,6 @@
# macOS
.DS_Store
kube-config.yaml
# Logs
logs
*.log

View File

@ -1,29 +1,23 @@
app:
title: Scaffolded Backstage App
title: Sreez Portal
baseUrl: http://localhost:3000
organization:
name: Sreez
backend:
# Used for enabling authentication, secret is shared by all backend plugins
# See https://backstage.io/docs/auth/service-to-service-auth for
# information on the format
# auth:
# keys:
# - secret: ${BACKEND_SECRET}
baseUrl: http://localhost:7007
listen:
port: 7007
# Uncomment the following host directive to bind to specific interfaces
# host: 127.0.0.1
csp:
connect-src: ["'self'", "http:", "https:"]
connect-src: [ "'self'", "http:", "https:" ]
# Content-Security-Policy directives follow the Helmet format: https://helmetjs.github.io/#reference
# Default Helmet Content-Security-Policy values can be removed by setting the key to false
cors:
origin: http://localhost:3000
methods: [GET, HEAD, PATCH, POST, PUT, DELETE]
methods: [ GET, HEAD, PATCH, POST, PUT, DELETE ]
credentials: true
# This is for local development only, it is not recommended to use this in production
# The production database configuration is stored in app-config.production.yaml
@ -35,7 +29,7 @@ backend:
proxy:
"/github/api":
target: https://api.github.com/repos
allowedHeaders: ["Authorization", "X-GitHub-Api-Version"]
allowedHeaders: [ "Authorization", "X-GitHub-Api-Version" ]
headers:
Accept: application/vnd.github+json
X-GitHub-Api-Version: "2022-11-28"
@ -73,7 +67,7 @@ auth:
# see https://backstage.io/docs/auth/ to learn about auth providers
providers:
# See https://backstage.io/docs/auth/guest/provider
guest: {}
guest: { }
github:
development:
clientId: Iv1.62e73edda444f42c
@ -89,8 +83,6 @@ integrations:
apps:
- $include: github-app-backstage-sreez-credentials.yaml
scaffolder:
# see https://backstage.io/docs/features/software-templates/configuration for software template options
events:
modules:
github:
@ -106,7 +98,7 @@ catalog:
timeout: { seconds: 30 }
githubOrg:
id: "kheops-org"
orgs: [kheops-org]
orgs: [ kheops-org ]
githubUrl: "https://github.com/"
schedule: # same options as in TaskScheduleDefinition
frequency: { minutes: 30 }
@ -115,70 +107,32 @@ catalog:
entityFilename: catalog-info.yaml
pullRequestBranchName: backstage-integration
rules:
- allow: [Component, System, API, Resource, Location, Users, Group]
- allow: [ Component, System, API, Resource, Location, Users, Group ]
locations:
# Local example data, file locations are relative to the backend process, typically `packages/backend`
- type: file
target: ../../examples/entities.yaml
# Local example template
- type: file
target: ../../examples/template/template.yaml
rules:
- allow: [Template]
- allow: [ Template ]
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all.yaml
# Local example organizational data
# - type: file
# target: ../../examples/org.yaml
# rules:
# - allow: [User, Group]
# - type: github-org
# target: https://github.com/kheops-org
# rules:
# - allow: [User, Group]
# - type: file
# target: ./examples/entities.yaml
# - type: file
# target: ./examples/template/template.yaml
# rules:
# - allow: [Template]
# - type: file
# target: ./examples/org.yaml
# rules:
# - allow: [User, Group]
- type: url
target: https://github.com/backstage/backstage/raw/master/packages/catalog-model/examples/apis/petstore-api.yaml
- type: url
target: https://github.com/Kheops-org/backstage-templates/blob/main/templates.yaml
rules:
- allow: [Template]
- allow: [ Template ]
- type: url
target: https://github.com/redhat-developer/red-hat-developer-hub-software-templates/blob/main/templates.yaml
rules:
- allow: [Template]
## Uncomment these lines to add more example data
# - type: url
# target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all.yaml
## Uncomment these lines to add an example org
# - type: url
# target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme-corp.yaml
# rules:
# - allow: [User, Group]
devTools:
externalDependencies:
endpoints:
- name: "Google"
type: "fetch"
target: "https://google.ca"
- name: "Google Public DNS"
type: "ping"
target: "8.8.8.8"
- allow: [ Template ]
permission:
enabled: true
notifications:
# curl -X POST http://localhost:7007/api/notifications/notifications -H "Content-Type: application/json" -H "notifications-secret: your-secret-token-shared-with-external-services" -d '{"title":"my-title","origin":"my-origin","message":"message one","topic":"my-topic"}'
externalCallerSecret: notifications-secret
iframe:
allowList: [ "sreez.nationtech.io", "www.hyperdx.io", "http://localhost:8080" ]

View File

@ -15,43 +15,50 @@
},
"dependencies": {
"@backstage-community/plugin-github-actions": "^0.6.16",
"@backstage/app-defaults": "^1.5.4",
"@backstage/catalog-model": "^1.4.5",
"@backstage/cli": "^0.26.3",
"@backstage/core-app-api": "^1.12.4",
"@backstage/core-components": "^0.14.4",
"@backstage/app-defaults": "^1.5.5",
"@backstage/catalog-model": "^1.5.0",
"@backstage/cli": "^0.26.6",
"@backstage/core-app-api": "^1.12.5",
"@backstage/core-components": "^0.14.7",
"@backstage/core-plugin-api": "^1.9.2",
"@backstage/integration-react": "^1.1.26",
"@backstage/plugin-api-docs": "^0.11.4",
"@backstage/plugin-catalog": "^1.19.0",
"@backstage/plugin-catalog-common": "^1.0.22",
"@backstage/plugin-catalog-graph": "^0.4.4",
"@backstage/plugin-catalog-import": "^0.10.10",
"@backstage/plugin-catalog-react": "^1.11.3",
"@backstage/integration-react": "^1.1.27",
"@backstage/plugin-api-docs": "^0.11.5",
"@backstage/plugin-catalog": "^1.20.0",
"@backstage/plugin-catalog-common": "^1.0.23",
"@backstage/plugin-catalog-graph": "^0.4.5",
"@backstage/plugin-catalog-import": "^0.11.0",
"@backstage/plugin-catalog-react": "^1.12.0",
"@backstage/plugin-devtools": "^0.1.13",
"@backstage/plugin-org": "^0.6.24",
"@backstage/plugin-org": "^0.6.25",
"@backstage/plugin-permission-react": "^0.4.22",
"@backstage/plugin-scaffolder": "^1.19.3",
"@backstage/plugin-search": "^1.4.10",
"@backstage/plugin-search-react": "^1.7.10",
"@backstage/plugin-scaffolder": "^1.20.0",
"@backstage/plugin-search": "^1.4.11",
"@backstage/plugin-search-react": "^1.7.11",
"@backstage/plugin-tech-radar": "^0.7.3",
"@backstage/plugin-techdocs": "^1.10.4",
"@backstage/plugin-techdocs-module-addons-contrib": "^1.1.9",
"@backstage/plugin-techdocs-react": "^1.2.3",
"@backstage/plugin-user-settings": "^0.8.5",
"@backstage/theme": "^0.5.3",
"@backstage/plugin-techdocs": "^1.10.5",
"@backstage/plugin-techdocs-module-addons-contrib": "^1.1.10",
"@backstage/plugin-techdocs-react": "^1.2.4",
"@backstage/plugin-user-settings": "^0.8.6",
"@backstage/theme": "^0.5.5",
"@bestsellerit/backstage-plugin-harbor": "^0.3.1",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@roadiehq/backstage-plugin-argo-cd": "^2.6.4",
"@roadiehq/backstage-plugin-github-pull-requests": "^2.5.25",
"@veecode-platform/backstage-plugin-github-workflows": "^0.2.70",
"history": "^5.0.0",
"react": "^18.0.2",
"react-dom": "^18.0.2",
"react-router": "^6.3.0",
"react-router-dom": "^6.3.0",
"react-use": "^17.2.4"
"@mui/material": "next",
"@internal/backstage-plugin-devspaces-plugin": "^0.1.0",
"@janus-idp/plugin-notifications": "^1.3.1",
"@material-ui/icons": "^4.11.3",
"@roadiehq/backstage-plugin-argo-cd": "^2.6.5",
"@roadiehq/backstage-plugin-github-insights": "^2.3.29",
"@roadiehq/backstage-plugin-github-pull-requests": "^2.5.26",
"@roadiehq/backstage-plugin-iframe": "^1.3.17",
"@rsc-labs/backstage-changelog-plugin": "^0.5.0",
"@veecode-platform/backstage-plugin-github-workflows": "^0.4.9",
"add": "^2.0.6",
"history": "^5.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1",
"react-use": "^17.5.0",
"yarn": "^1.22.22"
},
"devDependencies": {
"@backstage/test-utils": "^1.5.4",

View File

@ -1,132 +1,127 @@
import React from 'react';
import { Navigate, Route } from 'react-router-dom';
import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs';
import {
CatalogEntityPage,
CatalogIndexPage,
catalogPlugin,
} from '@backstage/plugin-catalog';
import {
CatalogImportPage,
catalogImportPlugin,
} from '@backstage/plugin-catalog-import';
import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder';
import { orgPlugin } from '@backstage/plugin-org';
import { SearchPage } from '@backstage/plugin-search';
import { TechRadarPage } from '@backstage/plugin-tech-radar';
import {
TechDocsIndexPage,
techdocsPlugin,
TechDocsReaderPage,
} from '@backstage/plugin-techdocs';
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
import { UserSettingsPage } from '@backstage/plugin-user-settings';
import { apis } from './apis';
import { entityPage } from './components/catalog/EntityPage';
import { searchPage } from './components/search/SearchPage';
import { Root } from './components/Root';
import {
AlertDisplay,
OAuthRequestDialog,
SignInPage,
} from '@backstage/core-components';
import { createApp } from '@backstage/app-defaults';
import { AppRouter, FlatRoutes } from '@backstage/core-app-api';
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
import { RequirePermission } from '@backstage/plugin-permission-react';
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
import { githubAuthApiRef } from '@backstage/core-plugin-api';
import { DevToolsPage } from '@backstage/plugin-devtools';
import {Navigate, Route} from 'react-router-dom';
import {apiDocsPlugin, ApiExplorerPage} from '@backstage/plugin-api-docs';
import {CatalogEntityPage, CatalogIndexPage, catalogPlugin,} from '@backstage/plugin-catalog';
import {CatalogImportPage, catalogImportPlugin,} from '@backstage/plugin-catalog-import';
import {ScaffolderPage, scaffolderPlugin} from '@backstage/plugin-scaffolder';
import {orgPlugin} from '@backstage/plugin-org';
import {SearchPage} from '@backstage/plugin-search';
import {TechRadarPage} from '@backstage/plugin-tech-radar';
import {TechDocsIndexPage, techdocsPlugin, TechDocsReaderPage,} from '@backstage/plugin-techdocs';
import {TechDocsAddons} from '@backstage/plugin-techdocs-react';
import {ReportIssue} from '@backstage/plugin-techdocs-module-addons-contrib';
import {UserSettingsPage} from '@backstage/plugin-user-settings';
import {apis} from './apis';
import {entityPage} from './components/catalog/EntityPage';
import {searchPage} from './components/search/SearchPage';
import {Root} from './components/Root';
import {AlertDisplay, OAuthRequestDialog, SignInPage,} from '@backstage/core-components';
import {createApp} from '@backstage/app-defaults';
import {AppRouter, FlatRoutes} from '@backstage/core-app-api';
import {CatalogGraphPage} from '@backstage/plugin-catalog-graph';
import {RequirePermission} from '@backstage/plugin-permission-react';
import {catalogEntityCreatePermission} from '@backstage/plugin-catalog-common/alpha';
import {githubAuthApiRef} from '@backstage/core-plugin-api';
import {UnifiedThemeProvider} from '@backstage/theme';
import {SreezCustomTheme} from './theme/custom';
import LightIcon from '@material-ui/icons/WbSunny';
import {DevspacesPluginPage} from '@internal/backstage-plugin-devspaces-plugin';
const app = createApp({
apis,
bindRoutes({ bind }) {
bind(catalogPlugin.externalRoutes, {
createComponent: scaffolderPlugin.routes.root,
viewTechDoc: techdocsPlugin.routes.docRoot,
createFromTemplate: scaffolderPlugin.routes.selectedTemplate,
});
bind(apiDocsPlugin.externalRoutes, {
registerApi: catalogImportPlugin.routes.importPage,
});
bind(scaffolderPlugin.externalRoutes, {
registerComponent: catalogImportPlugin.routes.importPage,
viewTechDoc: techdocsPlugin.routes.docRoot,
});
bind(orgPlugin.externalRoutes, {
catalogIndex: catalogPlugin.routes.catalogIndex,
});
},
components: {
SignInPage: props => (
<SignInPage
{...props}
auto
providers={[
'guest',
{
id: 'github-auth-provider',
title: 'GitHub',
message: 'Sign in using GitHub',
apiRef: githubAuthApiRef,
}]}
/>
),
},
themes: [{
id: 'sreez-theme',
title: 'Sreez Default Theme',
variant: 'dark',
icon: <LightIcon/>,
Provider: ({children}) => (
<UnifiedThemeProvider theme={SreezCustomTheme} children={children}/>
)
}],
apis,
bindRoutes({bind}) {
bind(catalogPlugin.externalRoutes, {
createComponent: scaffolderPlugin.routes.root,
viewTechDoc: techdocsPlugin.routes.docRoot,
createFromTemplate: scaffolderPlugin.routes.selectedTemplate,
});
bind(apiDocsPlugin.externalRoutes, {
registerApi: catalogImportPlugin.routes.importPage,
});
bind(scaffolderPlugin.externalRoutes, {
registerComponent: catalogImportPlugin.routes.importPage,
viewTechDoc: techdocsPlugin.routes.docRoot,
});
bind(orgPlugin.externalRoutes, {
catalogIndex: catalogPlugin.routes.catalogIndex,
});
},
components: {
SignInPage: props => (
<SignInPage
{...props}
auto
providers={[
'guest',
{
id: 'github-auth-provider',
title: 'GitHub',
message: 'Sign in using GitHub',
apiRef: githubAuthApiRef,
}]}
/>
),
},
});
const routes = (
<FlatRoutes>
<Route path="/devtools" element={<DevToolsPage />} />
<Route path="/" element={<Navigate to="catalog" />} />
<Route path="/catalog" element={<CatalogIndexPage />} />
<Route
path="/catalog/:namespace/:kind/:name"
element={<CatalogEntityPage />}
>
{entityPage}
</Route>
<Route path="/docs" element={<TechDocsIndexPage />} />
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</Route>
<Route path="/create" element={<ScaffolderPage />} />
<Route path="/api-docs" element={<ApiExplorerPage />} />
<Route
path="/tech-radar"
element={<TechRadarPage width={1500} height={800} />}
/>
<Route
path="/catalog-import"
element={
<RequirePermission permission={catalogEntityCreatePermission}>
<CatalogImportPage />
</RequirePermission>
}
/>
<Route path="/search" element={<SearchPage />}>
{searchPage}
</Route>
<Route path="/settings" element={<UserSettingsPage />} />
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
</FlatRoutes>
<FlatRoutes>
<Route path="/devspaces-plugin" element={<DevspacesPluginPage/>}></Route>
<Route path="/" element={<Navigate to="catalog"/>}/>
<Route path="/catalog" element={<CatalogIndexPage/>}/>
<Route
path="/catalog/:namespace/:kind/:name"
element={<CatalogEntityPage/>}
>
{entityPage}
</Route>
<Route path="/docs" element={<TechDocsIndexPage/>}/>
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage/>}
>
<TechDocsAddons>
<ReportIssue/>
</TechDocsAddons>
</Route>
<Route path="/create" element={<ScaffolderPage/>}/>
<Route path="/api-docs" element={<ApiExplorerPage/>}/>
<Route
path="/tech-radar"
element={<TechRadarPage width={1500} height={800}/>}
/>
<Route
path="/catalog-import"
element={
<RequirePermission permission={catalogEntityCreatePermission}>
<CatalogImportPage/>
</RequirePermission>
}
/>
<Route path="/search" element={<SearchPage/>}>
{searchPage}
</Route>
<Route path="/settings" element={<UserSettingsPage/>}/>
<Route path="/catalog-graph" element={<CatalogGraphPage/>}/>
</FlatRoutes>
);
export default app.createRoot(
<>
<AlertDisplay />
<OAuthRequestDialog />
<AppRouter>
<Root>{routes}</Root>
</AppRouter>
</>,
<>
<AlertDisplay/>
<OAuthRequestDialog/>
<AppRouter>
<Root>{routes}</Root>
</AppRouter>
</>,
);

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,9 @@
import React from 'react';
import { makeStyles } from '@material-ui/core';
import logoIcon from './sreez_logo.png';
const useStyles = makeStyles({
svg: {
img: {
width: 'auto',
height: 28,
},
@ -13,18 +14,8 @@ const useStyles = makeStyles({
const LogoIcon = () => {
const classes = useStyles();
return (
<svg
className={classes.svg}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 337.46 428.5"
>
<path
className={classes.path}
d="M303,166.05a80.69,80.69,0,0,0,13.45-10.37c.79-.77,1.55-1.53,2.3-2.3a83.12,83.12,0,0,0,7.93-9.38A63.69,63.69,0,0,0,333,133.23a48.58,48.58,0,0,0,4.35-16.4c1.49-19.39-10-38.67-35.62-54.22L198.56,0,78.3,115.23,0,190.25l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.69,19.16-18.36,25.52-42.12,13.7-61.87a49.22,49.22,0,0,0-6.8-8.87A89.17,89.17,0,0,0,259,178.29h.15a85.08,85.08,0,0,0,31-5.79A80.88,80.88,0,0,0,303,166.05ZM202.45,225.86c-19.32,18.51-50.4,21.23-75.7,5.9L51.61,186.15l67.45-64.64,76.41,46.38C223,184.58,221.49,207.61,202.45,225.86Zm8.93-82.22-70.65-42.89L205.14,39,274.51,81.1c25.94,15.72,29.31,37,10.55,55A60.69,60.69,0,0,1,211.38,143.64Zm29.86,190c-19.57,18.75-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,282.52v24.67L108.6,373.1a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A87.27,87.27,0,0,1,241.24,333.68Zm0-39c-19.57,18.75-46.17,29.08-74.88,29.08a123.81,123.81,0,0,1-64.1-18.19L0,243.53v24.68l108.6,65.91a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.5v-1.78A87.27,87.27,0,0,1,241.24,294.7Zm0-39c-19.57,18.76-46.17,29.09-74.88,29.09a123.81,123.81,0,0,1-64.1-18.19L0,204.55v24.68l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.68,12.88-12.35,20-27.13,19.68-41.5v-1.82A86.09,86.09,0,0,1,241.24,255.71Zm83.7,25.74a94.15,94.15,0,0,1-60.2,25.86h0V334a81.6,81.6,0,0,0,51.74-22.37c14-13.38,21.14-28.11,21-42.64v-2.19A94.92,94.92,0,0,1,324.94,281.45Zm-83.7,91.21c-19.57,18.76-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,321.5v24.68l108.6,65.9a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.8,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A86.29,86.29,0,0,1,241.24,372.66ZM327,162.45c-.68.69-1.35,1.38-2.05,2.06a94.37,94.37,0,0,1-10.64,8.65,91.35,91.35,0,0,1-11.6,7,94.53,94.53,0,0,1-26.24,8.71,97.69,97.69,0,0,1-14.16,1.57c.5,1.61.9,3.25,1.25,4.9a53.27,53.27,0,0,1,1.14,12V217h.05a84.41,84.41,0,0,0,25.35-5.55,81,81,0,0,0,26.39-16.82c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,172.17a48.55,48.55,0,0,0,4.32-16.45c.09-1.23.2-2.47.19-3.7V150q-1.08,1.54-2.25,3.09A96.73,96.73,0,0,1,327,162.45Zm0,77.92c-.69.7-1.31,1.41-2,2.1a94.2,94.2,0,0,1-60.2,25.86h0l0,26.67h0a81.6,81.6,0,0,0,51.74-22.37A73.51,73.51,0,0,0,333,250.13a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.47.19-3.71v-2.19c-.74,1.07-1.46,2.15-2.27,3.21A95.68,95.68,0,0,1,327,240.37Zm0-39c-.69.7-1.31,1.41-2,2.1a93.18,93.18,0,0,1-10.63,8.65,91.63,91.63,0,0,1-11.63,7,95.47,95.47,0,0,1-37.94,10.18h0V256h0a81.65,81.65,0,0,0,51.74-22.37c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,211.15a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.48.19-3.71v-2.2c-.74,1.08-1.46,2.16-2.27,3.22A95.68,95.68,0,0,1,327,201.39Z"
/>
</svg>
<img src={logoIcon} alt='Sreez' className={classes.img}/>
);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,442 +1,22 @@
import React from 'react';
import { Button, Grid } from '@material-ui/core';
import {
EntityApiDefinitionCard,
EntityConsumedApisCard,
EntityConsumingComponentsCard,
EntityHasApisCard,
EntityProvidedApisCard,
EntityProvidingComponentsCard,
} from '@backstage/plugin-api-docs';
import {
EntityAboutCard,
EntityDependsOnComponentsCard,
EntityDependsOnResourcesCard,
EntityHasComponentsCard,
EntityHasResourcesCard,
EntityHasSubcomponentsCard,
EntityHasSystemsCard,
EntityLayout,
EntityLinksCard,
EntitySwitch,
EntityOrphanWarning,
EntityProcessingErrorsPanel,
isComponentType,
isKind,
hasCatalogProcessingErrors,
isOrphan,
hasRelationWarnings,
EntityRelationWarning,
} from '@backstage/plugin-catalog';
import {
isGithubActionsAvailable,
EntityGithubActionsContent,
} from '@backstage-community/plugin-github-actions';
import {
EntityUserProfileCard,
EntityGroupProfileCard,
EntityMembersListCard,
EntityOwnershipCard,
} from '@backstage/plugin-org';
import { EntityTechdocsContent } from '@backstage/plugin-techdocs';
import { EmptyState } from '@backstage/core-components';
import {
Direction,
EntityCatalogGraphCard,
} from '@backstage/plugin-catalog-graph';
import {
RELATION_API_CONSUMED_BY,
RELATION_API_PROVIDED_BY,
RELATION_CONSUMES_API,
RELATION_DEPENDENCY_OF,
RELATION_DEPENDS_ON,
RELATION_HAS_PART,
RELATION_PART_OF,
RELATION_PROVIDES_API,
} from '@backstage/catalog-model';
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
import {
EntityArgoCDOverviewCard,
isArgocdAvailable,
} from '@roadiehq/backstage-plugin-argo-cd';
import {
GithubWorkflowsCard,
GithubWorkflowsList,
isGithubAvailable,
isGithubWorkflowsAvailable,
} from '@veecode-platform/backstage-plugin-github-workflows';
import {
EntityGithubPullRequestsContent,
EntityGithubPullRequestsOverviewCard,
} from '@roadiehq/backstage-plugin-github-pull-requests';
const techdocsContent = (
<EntityTechdocsContent>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</EntityTechdocsContent>
);
const cicdContent = (
<EntitySwitch>
<EntitySwitch.Case if={isGithubActionsAvailable}>
<EntityGithubActionsContent />
</EntitySwitch.Case>
<EntitySwitch.Case>
<EmptyState
title="No CI/CD available for this entity"
missing="info"
description="You need to add an annotation to your component if you want to enable CI/CD for it. You can read more about annotations in Backstage by clicking the button below."
action={
<Button
variant="contained"
color="primary"
href="https://backstage.io/docs/features/software-catalog/well-known-annotations"
>
Read more
</Button>
}
/>
</EntitySwitch.Case>
</EntitySwitch>
);
const workflowsContent = (
<EntitySwitch>
<EntitySwitch.Case if={isGithubActionsAvailable}>
<GithubWorkflowsList />
</EntitySwitch.Case>
<EntitySwitch.Case>
<EmptyState
title="No CI/CD available for this entity"
missing="info"
description="You need to add an annotation to your component if you want to enable CI/CD for it. You can read more about annotations in Backstage by clicking the button below."
action={
<Button
variant="contained"
color="primary"
href="https://backstage.io/docs/features/software-catalog/well-known-annotations"
>
Read more
</Button>
}
/>
</EntitySwitch.Case>
</EntitySwitch>
);
const entityWarningContent = (
<>
<EntitySwitch>
<EntitySwitch.Case if={isOrphan}>
<Grid item xs={12}>
<EntityOrphanWarning />
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
<EntitySwitch>
<EntitySwitch.Case if={hasRelationWarnings}>
<Grid item xs={12}>
<EntityRelationWarning />
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
<EntitySwitch>
<EntitySwitch.Case if={hasCatalogProcessingErrors}>
<Grid item xs={12}>
<EntityProcessingErrorsPanel />
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
</>
);
const overviewContent = (
<Grid container spacing={1} alignItems="stretch">
{entityWarningContent}
<Grid item md={6} xs={12}>
<EntityAboutCard variant="gridItem" />
</Grid>
<EntitySwitch>
<EntitySwitch.Case if={isArgocdAvailable}>
<Grid item md={6} xs={12}>
<EntityArgoCDOverviewCard />
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
<EntitySwitch>
<EntitySwitch.Case if={isGithubWorkflowsAvailable}>
<Grid item md={6} xs={12}>
<GithubWorkflowsCard />
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
<Grid item md={6}>
<EntityGithubPullRequestsOverviewCard />
</Grid>
<Grid item md={6} xs={12}>
<EntityHasSubcomponentsCard variant="gridItem" />
</Grid>
<Grid item md={6} xs={12}>
<EntityCatalogGraphCard variant="gridItem" height={400} />
</Grid>
<Grid item md={6} xs={12}>
<EntityLinksCard />
</Grid>
</Grid>
);
const serviceEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
{overviewContent}
</EntityLayout.Route>
<EntityLayout.Route path="/pull-requests" title="Pull Requests">
<EntityGithubPullRequestsContent />
</EntityLayout.Route>
<EntityLayout.Route path="/ci-cd" title="CI/CD">
{cicdContent}
</EntityLayout.Route>
<EntityLayout.Route
if={isGithubAvailable}
path="/workflows"
title="Workflows"
>
{workflowsContent}
</EntityLayout.Route>
<EntityLayout.Route path="/api" title="API">
<Grid container spacing={3} alignItems="stretch">
<Grid item md={6}>
<EntityProvidedApisCard />
</Grid>
<Grid item md={6}>
<EntityConsumedApisCard />
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/dependencies" title="Dependencies">
<Grid container spacing={3} alignItems="stretch">
<Grid item md={6}>
<EntityDependsOnComponentsCard variant="gridItem" />
</Grid>
<Grid item md={6}>
<EntityDependsOnResourcesCard variant="gridItem" />
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/docs" title="Docs">
{techdocsContent}
</EntityLayout.Route>
</EntityLayout>
);
const websiteEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
{overviewContent}
</EntityLayout.Route>
<EntityLayout.Route path="/dependencies" title="Dependencies">
<Grid container spacing={3} alignItems="stretch">
<Grid item md={6}>
<EntityDependsOnComponentsCard variant="gridItem" />
</Grid>
<Grid item md={6}>
<EntityDependsOnResourcesCard variant="gridItem" />
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/docs" title="Docs">
{techdocsContent}
</EntityLayout.Route>
</EntityLayout>
);
/**
* NOTE: This page is designed to work on small screens such as mobile devices.
* This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`,
* since this does not default. If no breakpoints are used, the items will equitably share the available space.
* https://material-ui.com/components/grid/#basic-grid.
*/
const defaultEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
{overviewContent}
</EntityLayout.Route>
<EntityLayout.Route path="/docs" title="Docs">
{techdocsContent}
</EntityLayout.Route>
</EntityLayout>
);
const componentPage = (
<EntitySwitch>
<EntitySwitch.Case if={isComponentType('service')}>
{serviceEntityPage}
</EntitySwitch.Case>
<EntitySwitch.Case if={isComponentType('website')}>
{websiteEntityPage}
</EntitySwitch.Case>
<EntitySwitch.Case>{defaultEntityPage}</EntitySwitch.Case>
</EntitySwitch>
);
const apiPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3}>
{entityWarningContent}
<Grid item md={6}>
<EntityAboutCard />
</Grid>
<Grid item md={6} xs={12}>
<EntityCatalogGraphCard variant="gridItem" height={400} />
</Grid>
<Grid item md={4} xs={12}>
<EntityLinksCard />
</Grid>
<Grid container item md={12}>
<Grid item md={6}>
<EntityProvidingComponentsCard />
</Grid>
<Grid item md={6}>
<EntityConsumingComponentsCard />
</Grid>
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/definition" title="Definition">
<Grid container spacing={3}>
<Grid item xs={12}>
<EntityApiDefinitionCard />
</Grid>
</Grid>
</EntityLayout.Route>
</EntityLayout>
);
const userPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3}>
{entityWarningContent}
<Grid item xs={12} md={6}>
<EntityUserProfileCard variant="gridItem" />
</Grid>
<Grid item xs={12} md={6}>
<EntityOwnershipCard variant="gridItem" />
</Grid>
</Grid>
</EntityLayout.Route>
</EntityLayout>
);
const groupPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3}>
{entityWarningContent}
<Grid item xs={12} md={6}>
<EntityGroupProfileCard variant="gridItem" />
</Grid>
<Grid item xs={12} md={6}>
<EntityOwnershipCard variant="gridItem" />
</Grid>
<Grid item xs={12} md={6}>
<EntityMembersListCard />
</Grid>
<Grid item xs={12} md={6}>
<EntityLinksCard />
</Grid>
</Grid>
</EntityLayout.Route>
</EntityLayout>
);
const systemPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3} alignItems="stretch">
{entityWarningContent}
<Grid item md={6}>
<EntityAboutCard variant="gridItem" />
</Grid>
<Grid item md={6} xs={12}>
<EntityCatalogGraphCard variant="gridItem" height={400} />
</Grid>
<Grid item md={4} xs={12}>
<EntityLinksCard />
</Grid>
<Grid item md={8}>
<EntityHasComponentsCard variant="gridItem" />
</Grid>
<Grid item md={6}>
<EntityHasApisCard variant="gridItem" />
</Grid>
<Grid item md={6}>
<EntityHasResourcesCard variant="gridItem" />
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/diagram" title="Diagram">
<EntityCatalogGraphCard
variant="gridItem"
direction={Direction.TOP_BOTTOM}
title="System Diagram"
height={700}
relations={[
RELATION_PART_OF,
RELATION_HAS_PART,
RELATION_API_CONSUMED_BY,
RELATION_API_PROVIDED_BY,
RELATION_CONSUMES_API,
RELATION_PROVIDES_API,
RELATION_DEPENDENCY_OF,
RELATION_DEPENDS_ON,
]}
unidirectional={false}
/>
</EntityLayout.Route>
</EntityLayout>
);
const domainPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3} alignItems="stretch">
{entityWarningContent}
<Grid item md={6}>
<EntityAboutCard variant="gridItem" />
</Grid>
<Grid item md={6} xs={12}>
<EntityCatalogGraphCard variant="gridItem" height={400} />
</Grid>
<Grid item md={6}>
<EntityHasSystemsCard variant="gridItem" />
</Grid>
</Grid>
</EntityLayout.Route>
</EntityLayout>
);
import {EntitySwitch, isKind,} from '@backstage/plugin-catalog';
import {componentPage} from "./entities/ComponentEntity";
import {apiPage} from "./entities/ApiEntity";
import {groupPage} from "./entities/Group";
import {userPage} from "./entities/User";
import {systemPage} from "./entities/System";
import {domainPage} from "./entities/Domain";
import {defaultEntityPage} from "./entities/DefaultEntityPage";
export const entityPage = (
<EntitySwitch>
<EntitySwitch.Case if={isKind('component')} children={componentPage} />
<EntitySwitch.Case if={isKind('api')} children={apiPage} />
<EntitySwitch.Case if={isKind('group')} children={groupPage} />
<EntitySwitch.Case if={isKind('user')} children={userPage} />
<EntitySwitch.Case if={isKind('system')} children={systemPage} />
<EntitySwitch.Case if={isKind('domain')} children={domainPage} />
<EntitySwitch>
<EntitySwitch.Case if={isKind('component')} children={componentPage}/>
<EntitySwitch.Case if={isKind('api')} children={apiPage}/>
<EntitySwitch.Case if={isKind('group')} children={groupPage}/>
<EntitySwitch.Case if={isKind('user')} children={userPage}/>
<EntitySwitch.Case if={isKind('system')} children={systemPage}/>
<EntitySwitch.Case if={isKind('domain')} children={domainPage}/>
<EntitySwitch.Case>{defaultEntityPage}</EntitySwitch.Case>
</EntitySwitch>
<EntitySwitch.Case>{defaultEntityPage}</EntitySwitch.Case>
</EntitySwitch>
);

View File

@ -0,0 +1,45 @@
import {EntityAboutCard, EntityLayout, EntityLinksCard} from "@backstage/plugin-catalog";
import {Grid} from "@material-ui/core";
import {entityWarningContent} from "../tabs/EntityWarningContent";
import {EntityCatalogGraphCard} from "@backstage/plugin-catalog-graph";
import {
EntityApiDefinitionCard,
EntityConsumingComponentsCard,
EntityProvidingComponentsCard
} from "@backstage/plugin-api-docs";
import React from "react";
export const apiPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3}>
{entityWarningContent}
<Grid item md={6}>
<EntityAboutCard/>
</Grid>
<Grid item md={6} xs={12}>
<EntityCatalogGraphCard variant="gridItem" height={400}/>
</Grid>
<Grid item md={4} xs={12}>
<EntityLinksCard/>
</Grid>
<Grid container item md={12}>
<Grid item md={6}>
<EntityProvidingComponentsCard/>
</Grid>
<Grid item md={6}>
<EntityConsumingComponentsCard/>
</Grid>
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/definition" title="Definition">
<Grid container spacing={3}>
<Grid item xs={12}>
<EntityApiDefinitionCard/>
</Grid>
</Grid>
</EntityLayout.Route>
</EntityLayout>
);

View File

@ -0,0 +1,17 @@
import {EntitySwitch, isComponentType} from "@backstage/plugin-catalog";
import {serviceEntityPage} from "./ServiceComponent";
import {websiteEntityPage} from "./WebsiteComponent";
import {defaultEntityPage} from "./DefaultEntityPage";
import React from "react";
export const componentPage = (
<EntitySwitch>
<EntitySwitch.Case if={isComponentType('service')}>
{serviceEntityPage}
</EntitySwitch.Case>
<EntitySwitch.Case if={isComponentType('website')}>
{websiteEntityPage}
</EntitySwitch.Case>
<EntitySwitch.Case>{defaultEntityPage}</EntitySwitch.Case>
</EntitySwitch>
);

View File

@ -0,0 +1,21 @@
/**
* NOTE: This page is designed to work on small screens such as mobile devices.
* This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`,
* since this does not default. If no breakpoints are used, the items will equitably share the available space.
* https://material-ui.com/components/grid/#basic-grid.
*/
import {EntityLayout} from "@backstage/plugin-catalog";
import React from "react";
import {overviewContent} from "../tabs/OverviewContent";
import {techdocsContent} from "../tabs/TechdocsContent";
export const defaultEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
{overviewContent}
</EntityLayout.Route>
<EntityLayout.Route path="/docs" title="Docs">
{techdocsContent}
</EntityLayout.Route>
</EntityLayout>
);

View File

@ -0,0 +1,24 @@
import {EntityAboutCard, EntityHasSystemsCard, EntityLayout} from "@backstage/plugin-catalog";
import {Grid} from "@material-ui/core";
import {EntityCatalogGraphCard} from "@backstage/plugin-catalog-graph";
import React from "react";
import {entityWarningContent} from "../tabs/EntityWarningContent";
export const domainPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3} alignItems="stretch">
{entityWarningContent}
<Grid item md={6}>
<EntityAboutCard variant="gridItem"/>
</Grid>
<Grid item md={6} xs={12}>
<EntityCatalogGraphCard variant="gridItem" height={400}/>
</Grid>
<Grid item md={6}>
<EntityHasSystemsCard variant="gridItem"/>
</Grid>
</Grid>
</EntityLayout.Route>
</EntityLayout>
);

View File

@ -0,0 +1,27 @@
import {EntityLayout, EntityLinksCard} from "@backstage/plugin-catalog";
import {Grid} from "@material-ui/core";
import {entityWarningContent} from "../tabs/EntityWarningContent";
import {EntityGroupProfileCard, EntityMembersListCard, EntityOwnershipCard} from "@backstage/plugin-org";
import React from "react";
export const groupPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3}>
{entityWarningContent}
<Grid item xs={12} md={6}>
<EntityGroupProfileCard variant="gridItem"/>
</Grid>
<Grid item xs={12} md={6}>
<EntityOwnershipCard variant="gridItem"/>
</Grid>
<Grid item xs={12} md={6}>
<EntityMembersListCard/>
</Grid>
<Grid item xs={12} md={6}>
<EntityLinksCard/>
</Grid>
</Grid>
</EntityLayout.Route>
</EntityLayout>
);

View File

@ -0,0 +1,71 @@
import {EntityDependsOnComponentsCard, EntityDependsOnResourcesCard, EntityLayout} from "@backstage/plugin-catalog";
import {EntityGithubInsightsContent} from "@roadiehq/backstage-plugin-github-insights";
import {EntityGithubPullRequestsContent} from "@roadiehq/backstage-plugin-github-pull-requests";
import {Grid} from "@material-ui/core";
import {EntityConsumedApisCard, EntityProvidedApisCard} from "@backstage/plugin-api-docs";
import React from "react";
import {overviewContent} from "../tabs/OverviewContent";
import {buildsContent} from "../tabs/BuildsContent";
import {workflowsContent} from "../tabs/WorkflowsContent";
import {techdocsContent} from "../tabs/TechdocsContent";
export const serviceEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
{overviewContent}
</EntityLayout.Route>
{/*<EntityLayout.Route*/}
{/* path="/logs"*/}
{/* title="Logs">*/}
{/* <EntityIFrameContent title={" "} iframe={{*/}
{/* src: 'https://sreez.nationtech.io',*/}
{/* // src: 'http://localhost:8080/search',*/}
{/* title: " ",*/}
{/* width: "100%",*/}
{/* height: "1000px"*/}
{/* }}/>*/}
{/*</EntityLayout.Route>*/}
<EntityLayout.Route
path="/code-insights"
title="Code Insights">
<EntityGithubInsightsContent/>
</EntityLayout.Route>
<EntityLayout.Route path="/pull-requests" title="Pull Requests">
<EntityGithubPullRequestsContent/>
</EntityLayout.Route>
<EntityLayout.Route path="/ci-cd" title="Builds">
{buildsContent}
</EntityLayout.Route>
<EntityLayout.Route
path="/workflows"
title="Workflows"
>
{workflowsContent}
</EntityLayout.Route>
<EntityLayout.Route path="/api" title="API">
<Grid container spacing={3} alignItems="stretch">
<Grid item md={6}>
<EntityProvidedApisCard/>
</Grid>
<Grid item md={6}>
<EntityConsumedApisCard/>
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/dependencies" title="Dependencies">
<Grid container spacing={3} alignItems="stretch">
<Grid item md={6}>
<EntityDependsOnComponentsCard variant="gridItem"/>
</Grid>
<Grid item md={6}>
<EntityDependsOnResourcesCard variant="gridItem"/>
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/docs" title="Docs">
{techdocsContent}
</EntityLayout.Route>
</EntityLayout>
);

View File

@ -0,0 +1,69 @@
import {
EntityAboutCard,
EntityHasComponentsCard,
EntityHasResourcesCard,
EntityLayout,
EntityLinksCard
} from "@backstage/plugin-catalog";
import {Grid} from "@material-ui/core";
import {Direction, EntityCatalogGraphCard} from "@backstage/plugin-catalog-graph";
import {EntityHasApisCard} from "@backstage/plugin-api-docs";
import {
RELATION_API_CONSUMED_BY,
RELATION_API_PROVIDED_BY,
RELATION_CONSUMES_API,
RELATION_DEPENDENCY_OF,
RELATION_DEPENDS_ON,
RELATION_HAS_PART,
RELATION_PART_OF,
RELATION_PROVIDES_API
} from "@backstage/catalog-model";
import React from "react";
import {entityWarningContent} from "../tabs/EntityWarningContent";
export const systemPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3} alignItems="stretch">
{entityWarningContent}
<Grid item md={6}>
<EntityAboutCard variant="gridItem"/>
</Grid>
<Grid item md={6} xs={12}>
<EntityCatalogGraphCard variant="gridItem" height={400}/>
</Grid>
<Grid item md={4} xs={12}>
<EntityLinksCard/>
</Grid>
<Grid item md={8}>
<EntityHasComponentsCard variant="gridItem"/>
</Grid>
<Grid item md={6}>
<EntityHasApisCard variant="gridItem"/>
</Grid>
<Grid item md={6}>
<EntityHasResourcesCard variant="gridItem"/>
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/diagram" title="Diagram">
<EntityCatalogGraphCard
variant="gridItem"
direction={Direction.TOP_BOTTOM}
title="System Diagram"
height={700}
relations={[
RELATION_PART_OF,
RELATION_HAS_PART,
RELATION_API_CONSUMED_BY,
RELATION_API_PROVIDED_BY,
RELATION_CONSUMES_API,
RELATION_PROVIDES_API,
RELATION_DEPENDENCY_OF,
RELATION_DEPENDS_ON,
]}
unidirectional={false}
/>
</EntityLayout.Route>
</EntityLayout>
);

View File

@ -0,0 +1,21 @@
import {EntityLayout} from "@backstage/plugin-catalog";
import {Grid} from "@material-ui/core";
import {EntityOwnershipCard, EntityUserProfileCard} from "@backstage/plugin-org";
import React from "react";
import {entityWarningContent} from "../tabs/EntityWarningContent";
export const userPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3}>
{entityWarningContent}
<Grid item xs={12} md={6}>
<EntityUserProfileCard variant="gridItem"/>
</Grid>
<Grid item xs={12} md={6}>
<EntityOwnershipCard variant="gridItem"/>
</Grid>
</Grid>
</EntityLayout.Route>
</EntityLayout>
);

View File

@ -0,0 +1,28 @@
import {EntityDependsOnComponentsCard, EntityDependsOnResourcesCard, EntityLayout} from "@backstage/plugin-catalog";
import {Grid} from "@material-ui/core";
import React from "react";
import {overviewContent} from "../tabs/OverviewContent";
import {techdocsContent} from "../tabs/TechdocsContent";
export const websiteEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
{overviewContent}
</EntityLayout.Route>
<EntityLayout.Route path="/dependencies" title="Dependencies">
<Grid container spacing={3} alignItems="stretch">
<Grid item md={6}>
<EntityDependsOnComponentsCard variant="gridItem"/>
</Grid>
<Grid item md={6}>
<EntityDependsOnResourcesCard variant="gridItem"/>
</Grid>
</Grid>
</EntityLayout.Route>
<EntityLayout.Route path="/docs" title="Docs">
{techdocsContent}
</EntityLayout.Route>
</EntityLayout>
);

View File

@ -0,0 +1,29 @@
import {EntitySwitch} from "@backstage/plugin-catalog";
import {EntityGithubActionsContent, isGithubActionsAvailable} from "@backstage-community/plugin-github-actions";
import {EmptyState} from "@backstage/core-components";
import {Button} from "@material-ui/core";
import React from "react";
export const buildsContent = (
<EntitySwitch>
<EntitySwitch.Case if={isGithubActionsAvailable}>
<EntityGithubActionsContent/>
</EntitySwitch.Case>
<EntitySwitch.Case>
<EmptyState
title="No CI/CD available for this entity"
missing="info"
description="You need to add an annotation to your component if you want to enable CI/CD for it. You can read more about annotations in Backstage by clicking the button below."
action={
<Button
variant="contained"
color="primary"
href="https://backstage.io/docs/features/software-catalog/well-known-annotations"
>
Read more
</Button>
}
/>
</EntitySwitch.Case>
</EntitySwitch>
);

View File

@ -0,0 +1,39 @@
import {
EntityOrphanWarning,
EntityProcessingErrorsPanel,
EntityRelationWarning,
EntitySwitch,
hasCatalogProcessingErrors,
hasRelationWarnings,
isOrphan
} from "@backstage/plugin-catalog";
import {Grid} from "@material-ui/core";
import React from "react";
export const entityWarningContent = (
<>
<EntitySwitch>
<EntitySwitch.Case if={isOrphan}>
<Grid item xs={12}>
<EntityOrphanWarning/>
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
<EntitySwitch>
<EntitySwitch.Case if={hasRelationWarnings}>
<Grid item xs={12}>
<EntityRelationWarning/>
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
<EntitySwitch>
<EntitySwitch.Case if={hasCatalogProcessingErrors}>
<Grid item xs={12}>
<EntityProcessingErrorsPanel/>
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
</>
);

View File

@ -0,0 +1,80 @@
import {Grid} from "@material-ui/core";
import {EntityAboutCard, EntityHasSubcomponentsCard, EntityLinksCard} from "@backstage/plugin-catalog";
import React from "react";
import {entityWarningContent} from "./EntityWarningContent";
import {GithubWorkflowsCard} from "@veecode-platform/backstage-plugin-github-workflows";
import {LatestWorkflowRunCard} from "@backstage-community/plugin-github-actions";
import {EntityGithubInsightsReadmeCard} from "@roadiehq/backstage-plugin-github-insights";
import {EntityArgoCDOverviewCard} from "@roadiehq/backstage-plugin-argo-cd";
import {EntityGithubPullRequestsOverviewCard} from "@roadiehq/backstage-plugin-github-pull-requests";
import {EntityCatalogGraphCard} from "@backstage/plugin-catalog-graph";
export const overviewContent = (
<Grid container spacing={1} alignItems={'stretch'} justifyContent={'flex-start'}>
<Grid item xs={12}>
{entityWarningContent}
</Grid>
<Grid item xs={4}>
<EntityAboutCard variant={'flex'}/>
<EntityLinksCard variant="flex"/>
{/*<DevspacesPluginPage/>*/}
</Grid>
<Grid item xs={8}>
<Grid container>
<Grid item xs={4}>
<LatestWorkflowRunCard branch={'master'} variant="fullHeight"/>
</Grid>
<Grid item xs={8}>
<EntityArgoCDOverviewCard/>
</Grid>
</Grid>
<GithubWorkflowsCard/>
{/*<EntityGithubInsightsReadmeCard maxHeight={300}/>*/}
</Grid>
<Grid container spacing={1} alignItems={'stretch'}>
<Grid item xs={4}>
<EntityHasSubcomponentsCard variant="flex"/>
</Grid>
<Grid item xs={8}>
<EntityGithubPullRequestsOverviewCard/>
</Grid>
</Grid>
<Grid item xs={3}>
<EntityCatalogGraphCard variant="gridItem"/>
</Grid>
<Grid item xs={9}>
<EntityGithubInsightsReadmeCard/>
</Grid>
{/*<Grid item md={8} xs={12}>*/}
{/* <EntitySwitch>*/}
{/* <EntitySwitch.Case if={isGithubWorkflowsAvailable}>*/}
{/* </EntitySwitch.Case>*/}
{/* </EntitySwitch>*/}
{/*</Grid>*/}
{/*<Grid item md={4}>*/}
{/* <EntitySwitch>*/}
{/* <EntitySwitch.Case if={isGithubInsightsAvailable}>*/}
{/* </EntitySwitch.Case>*/}
{/* </EntitySwitch>*/}
{/*</Grid>*/}
{/*/!*<EntitySwitch>*!/*/}
{/*/!* <EntitySwitch.Case if={isArgocdAvailable}>*!/*/}
{/*/!* <Grid item md={4} xs={12}>*!/*/}
{/*<EntityArgoCDOverviewCard/>*/}
{/*/!* </Grid>*!/*/}
{/*/!* </EntitySwitch.Case>*!/*/}
{/*/!*</EntitySwitch>*!/*/}
{/*<Grid item md={4}>*/}
{/*</Grid>*/}
{/*<Grid item md={4} xs={12}>*/}
{/*</Grid>*/}
{/*<Grid item md={4} xs={12}>*/}
{/*</Grid>*/}
</Grid>
);

View File

@ -0,0 +1,12 @@
import {EntityTechdocsContent} from "@backstage/plugin-techdocs";
import {TechDocsAddons} from "@backstage/plugin-techdocs-react";
import {ReportIssue} from "@backstage/plugin-techdocs-module-addons-contrib";
import React from "react";
export const techdocsContent = (
<EntityTechdocsContent>
<TechDocsAddons>
<ReportIssue/>
</TechDocsAddons>
</EntityTechdocsContent>
);

View File

@ -0,0 +1,30 @@
import {EntitySwitch} from "@backstage/plugin-catalog";
import {isGithubActionsAvailable} from "@backstage-community/plugin-github-actions";
import {GithubWorkflowsList} from "@veecode-platform/backstage-plugin-github-workflows";
import {EmptyState} from "@backstage/core-components";
import {Button} from "@material-ui/core";
import React from "react";
export const workflowsContent = (
<EntitySwitch>
<EntitySwitch.Case if={isGithubActionsAvailable}>
<GithubWorkflowsList/>
</EntitySwitch.Case>
<EntitySwitch.Case>
<EmptyState
title="No CI/CD available for this entity"
missing="info"
description="You need to add an annotation to your component if you want to enable CI/CD for it. You can read more about annotations in Backstage by clicking the button below."
action={
<Button
variant="contained"
color="primary"
href="https://backstage.io/docs/features/software-catalog/well-known-annotations"
>
Read more
</Button>
}
/>
</EntitySwitch.Case>
</EntitySwitch>
);

View File

@ -0,0 +1,77 @@
import {createBaseThemeOptions, createUnifiedTheme, genPageTheme, palettes, shapes,} from '@backstage/theme';
export const SreezCustomTheme = createUnifiedTheme({
...createBaseThemeOptions({
palette: {
...palettes.light,
primary: {
main: '#0664c1',
},
secondary: {
main: '#d6589f',
},
error: {
main: '#d32f2f',
},
warning: {
main: '#ed6c02',
},
info: {
main: '#0288d1',
},
success: {
main: '#2e7d32',
},
banner: {
info: '#34548a',
error: '#8c4351',
text: '#343b58',
link: '#565a6e',
},
errorBackground: '#8c4351',
warningBackground: '#8f5e15',
infoBackground: '#343b58',
navigation: {
background: '#2F323A',
indicator: '#8f5e15',
color: '#d5d6db',
selectedColor: '#ffffff',
},
},
}),
defaultPageTheme: 'home',
pageTheme: {
home: genPageTheme({
colors: [
'#5006c1',
'#06c1ae',
'#0664c1',
],
shape: shapes.wave
}),
documentation: genPageTheme({
colors: ['#2F323A'],
shape: shapes.wave2,
}),
tool: genPageTheme({colors: ['#8c4351', '#343b58'], shape: shapes.round}),
service: genPageTheme({
colors: [
'#5006c1',
'#06c1ae',
'#0664c1',
],
shape: shapes.wave,
}),
website: genPageTheme({
colors: ['#8c4351', '#343b58'],
shape: shapes.wave,
}),
library: genPageTheme({
colors: ['#8c4351', '#343b58'],
shape: shapes.wave,
}),
other: genPageTheme({colors: ['#8c4351', '#343b58'], shape: shapes.wave}),
app: genPageTheme({colors: ['#8c4351', '#343b58'], shape: shapes.wave}),
apis: genPageTheme({colors: ['#8c4351', '#343b58'], shape: shapes.wave}),
},
});

View File

@ -17,8 +17,8 @@
},
"dependencies": {
"@backstage/backend-common": "^0.21.7",
"@backstage/backend-defaults": "^0.2.17",
"@backstage/backend-plugin-api": "^0.6.17",
"@backstage/backend-defaults": "^0.2.18",
"@backstage/backend-plugin-api": "^0.6.18",
"@backstage/backend-tasks": "^0.5.22",
"@backstage/config": "^1.2.0",
"@backstage/integration": "^1.10.0",
@ -41,18 +41,24 @@
"@backstage/plugin-proxy-backend": "^0.4.15",
"@backstage/plugin-scaffolder-backend": "^1.22.4",
"@backstage/plugin-scaffolder-backend-module-github": "^0.2.7",
"@backstage/plugin-scaffolder-node": "^0.4.3",
"@backstage/plugin-search-backend": "^1.5.7",
"@backstage/plugin-search-backend-module-catalog": "^0.1.22",
"@backstage/plugin-search-backend-module-techdocs": "^0.1.22",
"@backstage/plugin-search-backend-node": "^1.2.21",
"@backstage/plugin-techdocs-backend": "^1.10.4",
"@janus-idp/plugin-notifications-backend": "^1.4.12",
"@muvaf/create-argocd-application": "^0.4.1",
"@muvaf/kubernetes-apply": "^0.1.0",
"@roadiehq/scaffolder-backend-argocd": "^1.1.26",
"@rsc-labs/backstage-changelog-plugin-backend": "^0.3.1",
"app": "link:../app",
"better-sqlite3": "^9.0.0",
"dockerode": "^3.3.1",
"node-gyp": "^9.0.0",
"pg": "^8.11.3",
"winston": "^3.2.1"
"winston": "^3.2.1",
"zod": "^3.23.6"
},
"devDependencies": {
"@backstage/cli": "^0.26.3",

View File

@ -6,9 +6,16 @@
* Happy hacking!
*/
import { createBackend } from '@backstage/backend-defaults';
import { eventsModuleGithubEventRouter } from '@backstage/plugin-events-backend-module-github/alpha';
import { eventsModuleGithubWebhook } from '@backstage/plugin-events-backend-module-github/alpha';
import {createBackend} from '@backstage/backend-defaults';
import {
eventsModuleGithubEventRouter,
eventsModuleGithubWebhook
} from '@backstage/plugin-events-backend-module-github/alpha';
import {scaffolderActionsExtensionPoint} from '@backstage/plugin-scaffolder-node/alpha';
import {createBackendModule,} from '@backstage/backend-plugin-api';
import {createNewFileAction} from './plugins/scaffolder/actions/custom';
import {kubernetesApply} from "@muvaf/kubernetes-apply";
import {createArgoProjectAction} from './plugins/scaffolder/actions/argo';
const backend = createBackend();
@ -27,7 +34,7 @@ backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));
// catalog plugin
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));
backend.add(import('@backstage/plugin-catalog-backend-module-github/alpha'));
@ -39,7 +46,7 @@ backend.add(eventsModuleGithubWebhook());
// permission plugin
backend.add(import('@backstage/plugin-permission-backend/alpha'));
backend.add(
import('@backstage/plugin-permission-backend-module-allow-all-policy'),
import('@backstage/plugin-permission-backend-module-allow-all-policy'),
);
// search plugin
@ -49,28 +56,24 @@ backend.add(import('@backstage/plugin-search-backend/alpha'));
backend.add(import('@backstage/plugin-devtools-backend'));
import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha';
import { createBackendModule } from '@backstage/backend-plugin-api';
import { createNewFileAction } from './plugins/scaffolder/actions/custom';
import { createArgoProjectAction } from './plugins/scaffolder/actions/argo';
const scaffolderModuleCustomExtensions = createBackendModule({
pluginId: 'scaffolder', // name of the plugin that the module is targeting
moduleId: 'custom-extensions',
register(env) {
env.registerInit({
deps: {
scaffolder: scaffolderActionsExtensionPoint,
// ... and other dependencies as needed
},
async init({ scaffolder /* ..., other dependencies */ }) {
// Here you have the opportunity to interact with the extension
// point before the plugin itself gets instantiated
scaffolder.addActions(createNewFileAction()); // just an example
scaffolder.addActions(createArgoProjectAction()); // just an example
},
});
},
pluginId: 'scaffolder', // name of the plugin that the module is targeting
moduleId: 'custom-extensions',
register(env) {
env.registerInit({
deps: {
scaffolder: scaffolderActionsExtensionPoint,
// ... and other dependencies as needed
},
async init({scaffolder /* ..., other dependencies */}) {
// Here you have the opportunity to interact with the extension
// point before the plugin itself gets instantiated
scaffolder.addActions(createNewFileAction()); // just an example
scaffolder.addActions(createArgoProjectAction()); // just an example
scaffolder.addActions(kubernetesApply());
},
});
},
});
backend.add(scaffolderModuleCustomExtensions());

View File

@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);

View File

@ -0,0 +1,13 @@
# devspaces-plugin
Welcome to the devspaces-plugin plugin!
_This plugin was created through the Backstage CLI_
## Getting started
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/devspaces-plugin](http://localhost:3000/devspaces-plugin).
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.

View File

@ -0,0 +1,12 @@
import React from 'react';
import { createDevApp } from '@backstage/dev-utils';
import { devspacesPluginPlugin, DevspacesPluginPage } from '../src/plugin';
createDevApp()
.registerPlugin(devspacesPluginPlugin)
.addPage({
element: <DevspacesPluginPage />,
title: 'Root Page',
path: '/devspaces-plugin',
})
.render();

View File

@ -0,0 +1,51 @@
{
"name": "@internal/backstage-plugin-devspaces-plugin",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "frontend-plugin"
},
"sideEffects": false,
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/core-components": "^0.14.4",
"@backstage/core-plugin-api": "^1.9.2",
"@backstage/theme": "^0.5.3",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.60",
"react-use": "^17.2.4"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.26.3",
"@backstage/core-app-api": "^1.12.4",
"@backstage/dev-utils": "^1.0.31",
"@backstage/test-utils": "^1.5.4",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.0.0",
"msw": "^1.0.0"
},
"files": [
"dist"
]
}

View File

@ -0,0 +1,29 @@
import React from 'react';
import {useEntity} from '@backstage/plugin-catalog-react'
import {Box, Card, CardContent, CardHeader, Divider, Grid, Paper} from '@material-ui/core';
import {Link,} from '@backstage/core-components';
export const DevspacesComponent = () => {
const {entity} = useEntity();
const annotations = entity?.metadata?.annotations;
const clusterUrl = "oc-med.wk.nt.local"
const devspacesApp = "devspaces"
const devspacesLink = `https://${devspacesApp}.apps.${clusterUrl}/#https://github.com/${annotations?.['github.com/project-slug']}`;
return (
<Paper>
<Grid>
<Card>
{/*<CardHeader title="OpenShift DevSpaces">*/}
<CardHeader title={<Box mb={2}> OpenShift DevSpaces </Box>}/>
<Divider/>
<CardContent>
<Link to={devspacesLink}>
Open project in VSCode Web IDE
</Link>
</CardContent>
</Card>
</Grid>
</Paper>
);
};

View File

@ -0,0 +1,38 @@
import React from 'react';
import { Typography, Grid } from '@material-ui/core';
import {
InfoCard,
Header,
Page,
Content,
ContentHeader,
HeaderLabel,
SupportButton,
} from '@backstage/core-components';
import { ExampleFetchComponent } from '../ExampleFetchComponent';
export const DevspacesComponent = () => (
<Page themeId="tool">
<Header title="Welcome to devspaces-plugin!" subtitle="Optional subtitle">
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
<ContentHeader title="Plugin title">
<SupportButton>A description of your plugin goes here.</SupportButton>
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Information card">
<Typography variant="body1">
All content should be wrapped in a card like this.
</Typography>
</InfoCard>
</Grid>
<Grid item>
<ExampleFetchComponent />
</Grid>
</Grid>
</Content>
</Page>
);

View File

@ -0,0 +1 @@
export { DevspacesComponent } from './DevspacesComponent';

View File

@ -0,0 +1,19 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { ExampleFetchComponent } from './ExampleFetchComponent';
describe('ExampleFetchComponent', () => {
it('renders the user table', async () => {
render(<ExampleFetchComponent />);
// Wait for the table to render
const table = await screen.findByRole('table');
const nationality = screen.getAllByText('GB')
// Assert that the table contains the expected user data
expect(table).toBeInTheDocument();
expect(screen.getByAltText('Carolyn')).toBeInTheDocument();
expect(screen.getByText('Carolyn Moore')).toBeInTheDocument();
expect(screen.getByText('carolyn.moore@example.com')).toBeInTheDocument();
expect(nationality[0]).toBeInTheDocument();
});
});

View File

@ -0,0 +1,309 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import {
Table,
TableColumn,
Progress,
ResponseErrorPanel,
} from '@backstage/core-components';
import useAsync from 'react-use/lib/useAsync';
export const exampleUsers = {
results: [
{
gender: 'female',
name: {
title: 'Miss',
first: 'Carolyn',
last: 'Moore',
},
email: 'carolyn.moore@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Carolyn',
nat: 'GB',
},
{
gender: 'female',
name: {
title: 'Ms',
first: 'Esma',
last: 'Berberoğlu',
},
email: 'esma.berberoglu@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Esma',
nat: 'TR',
},
{
gender: 'female',
name: {
title: 'Ms',
first: 'Isabella',
last: 'Rhodes',
},
email: 'isabella.rhodes@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella',
nat: 'GB',
},
{
gender: 'male',
name: {
title: 'Mr',
first: 'Derrick',
last: 'Carter',
},
email: 'derrick.carter@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Derrick',
nat: 'IE',
},
{
gender: 'female',
name: {
title: 'Miss',
first: 'Mattie',
last: 'Lambert',
},
email: 'mattie.lambert@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mattie',
nat: 'AU',
},
{
gender: 'male',
name: {
title: 'Mr',
first: 'Mijat',
last: 'Rakić',
},
email: 'mijat.rakic@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mijat',
nat: 'RS',
},
{
gender: 'male',
name: {
title: 'Mr',
first: 'Javier',
last: 'Reid',
},
email: 'javier.reid@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Javier',
nat: 'US',
},
{
gender: 'female',
name: {
title: 'Ms',
first: 'Isabella',
last: 'Li',
},
email: 'isabella.li@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella',
nat: 'CA',
},
{
gender: 'female',
name: {
title: 'Mrs',
first: 'Stephanie',
last: 'Garrett',
},
email: 'stephanie.garrett@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Stephanie',
nat: 'AU',
},
{
gender: 'female',
name: {
title: 'Ms',
first: 'Antonia',
last: 'Núñez',
},
email: 'antonia.nunez@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Antonia',
nat: 'ES',
},
{
gender: 'male',
name: {
title: 'Mr',
first: 'Donald',
last: 'Young',
},
email: 'donald.young@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Donald',
nat: 'US',
},
{
gender: 'male',
name: {
title: 'Mr',
first: 'Iegor',
last: 'Holodovskiy',
},
email: 'iegor.holodovskiy@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Iegor',
nat: 'UA',
},
{
gender: 'female',
name: {
title: 'Madame',
first: 'Jessica',
last: 'David',
},
email: 'jessica.david@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jessica',
nat: 'CH',
},
{
gender: 'female',
name: {
title: 'Ms',
first: 'Eve',
last: 'Martinez',
},
email: 'eve.martinez@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Eve',
nat: 'FR',
},
{
gender: 'male',
name: {
title: 'Mr',
first: 'Caleb',
last: 'Silva',
},
email: 'caleb.silva@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Caleb',
nat: 'US',
},
{
gender: 'female',
name: {
title: 'Miss',
first: 'Marcia',
last: 'Jenkins',
},
email: 'marcia.jenkins@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Marcia',
nat: 'US',
},
{
gender: 'female',
name: {
title: 'Mrs',
first: 'Mackenzie',
last: 'Jones',
},
email: 'mackenzie.jones@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mackenzie',
nat: 'NZ',
},
{
gender: 'male',
name: {
title: 'Mr',
first: 'Jeremiah',
last: 'Gutierrez',
},
email: 'jeremiah.gutierrez@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jeremiah',
nat: 'AU',
},
{
gender: 'female',
name: {
title: 'Ms',
first: 'Luciara',
last: 'Souza',
},
email: 'luciara.souza@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Luciara',
nat: 'BR',
},
{
gender: 'male',
name: {
title: 'Mr',
first: 'Valgi',
last: 'da Cunha',
},
email: 'valgi.dacunha@example.com',
picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Valgi',
nat: 'BR',
},
],
};
const useStyles = makeStyles({
avatar: {
height: 32,
width: 32,
borderRadius: '50%',
},
});
type User = {
gender: string; // "male"
name: {
title: string; // "Mr",
first: string; // "Duane",
last: string; // "Reed"
};
email: string; // "duane.reed@example.com"
picture: string; // "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane"
nat: string; // "AU"
};
type DenseTableProps = {
users: User[];
};
export const DenseTable = ({ users }: DenseTableProps) => {
const classes = useStyles();
const columns: TableColumn[] = [
{ title: 'Avatar', field: 'avatar' },
{ title: 'Name', field: 'name' },
{ title: 'Email', field: 'email' },
{ title: 'Nationality', field: 'nationality' },
];
const data = users.map(user => {
return {
avatar: (
<img
src={user.picture}
className={classes.avatar}
alt={user.name.first}
/>
),
name: `${user.name.first} ${user.name.last}`,
email: user.email,
nationality: user.nat,
};
});
return (
<Table
title="Example User List"
options={{ search: false, paging: false }}
columns={columns}
data={data}
/>
);
};
export const ExampleFetchComponent = () => {
const { value, loading, error } = useAsync(async (): Promise<User[]> => {
// Would use fetch in a real world example
return exampleUsers.results;
}, []);
if (loading) {
return <Progress />;
} else if (error) {
return <ResponseErrorPanel error={error} />;
}
return <DenseTable users={value || []} />;
};

View File

@ -0,0 +1 @@
export { ExampleFetchComponent } from './ExampleFetchComponent';

View File

@ -0,0 +1 @@
export { devspacesPluginPlugin, DevspacesPluginPage } from './plugin';

View File

@ -0,0 +1,7 @@
import { devspacesPluginPlugin } from './plugin';
describe('devspaces-plugin', () => {
it('should export plugin', () => {
expect(devspacesPluginPlugin).toBeDefined();
});
});

View File

@ -0,0 +1,22 @@
import {
createPlugin,
createRoutableExtension,
} from '@backstage/core-plugin-api';
import { rootRouteRef } from './routes';
export const devspacesPluginPlugin = createPlugin({
id: 'devspaces-plugin',
routes: {
root: rootRouteRef,
},
});
export const DevspacesPluginPage = devspacesPluginPlugin.provide(
createRoutableExtension({
name: 'DevspacesPluginPage',
component: () =>
import('./components/DevspacesComponent').then(m => m.DevspacesComponent),
mountPoint: rootRouteRef,
}),
);

View File

@ -0,0 +1,5 @@
import { createRouteRef } from '@backstage/core-plugin-api';
export const rootRouteRef = createRouteRef({
id: 'devspaces-plugin',
});

View File

@ -0,0 +1 @@
import '@testing-library/jest-dom';

File diff suppressed because it is too large Load Diff