feat: [CDE-107]: Gitspace in standalone gitness (#2207)
|
@ -30,6 +30,7 @@ const moduleFederationConfig = require('./moduleFederation.config')
|
|||
const moduleFederationConfigCDE = require('./cde/moduleFederation.config')
|
||||
const CONTEXT = process.cwd()
|
||||
const DEV = process.env.NODE_ENV === 'development'
|
||||
const ENABLE_GITSPACE = process.env.ENABLE_GITSPACE === 'true'
|
||||
|
||||
const getModuleFields = () => {
|
||||
if (process.env.MODULE === 'cde') {
|
||||
|
@ -223,7 +224,8 @@ module.exports = {
|
|||
moduleFederationPlugin,
|
||||
new DefinePlugin({
|
||||
'process.env': '{}', // required for @blueprintjs/core
|
||||
__DEV__: DEV
|
||||
__DEV__: DEV,
|
||||
__ENABLE_GITSPACE__: ENABLE_GITSPACE
|
||||
}),
|
||||
new GenerateStringTypesPlugin(),
|
||||
new RetryChunkLoadPlugin({
|
||||
|
|
|
@ -34,12 +34,13 @@ const API_URL = process.env.API_URL ?? 'http://localhost:3000'
|
|||
const HOST = 'localhost'
|
||||
const PORT = getPortByModule()
|
||||
const STANDALONE = process.env.STANDALONE === 'true'
|
||||
const ENABLE_GITSPACE = process.env.ENABLE_GITSPACE === 'true'
|
||||
const CONTEXT = process.cwd()
|
||||
const prodConfig = require('./webpack.prod')
|
||||
|
||||
console.info(`Starting development build... http://${HOST}:${PORT}`)
|
||||
console.info('Environment variables:')
|
||||
console.table({ STANDALONE, HOST, PORT, API_URL })
|
||||
console.table({ STANDALONE, ENABLE_GITSPACE, HOST, PORT, API_URL })
|
||||
|
||||
const devConfig = {
|
||||
mode: 'development',
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
"webpack:cde": "NODE_ENV=development MODULE=cde webpack serve --config config/webpack.dev.js",
|
||||
"typed-scss": "typed-scss-modules src --watch",
|
||||
"dev": "run-p webpack typed-scss",
|
||||
"dev:cde": "MODULE=cde run-p webpack:cde typed-scss",
|
||||
"dev:cde": "MODULE=cde ENABLE_GITSPACE=true run-p webpack:cde typed-scss",
|
||||
"test": "jest src --silent",
|
||||
"test:watch": "jest --watch",
|
||||
"build": "rm -rf dist && webpack --config config/webpack.prod.js",
|
||||
"build:cde": "rm -rf dist && MODULE=cde webpack --config config/webpack.prod.js",
|
||||
"lint": "eslint --rulesdir ./scripts/eslint-rules --ext .ts --ext .tsx src",
|
||||
"build:cde": "rm -rf dist && MODULE=cde ENABLE_GITSPACE=true webpack --config config/webpack.prod.js",
|
||||
"lint": "eslint --fix --rulesdir ./scripts/eslint-rules --ext .ts --ext .tsx src",
|
||||
"prettier": "prettier --check \"./src/**/*.{ts,tsx,css,scss}\"",
|
||||
"coverage": "npm test --coverage",
|
||||
"typecheck": "tsc",
|
||||
|
@ -47,7 +47,7 @@
|
|||
"@codemirror/state": "^6.2.0",
|
||||
"@codemirror/view": "^6.9.6",
|
||||
"@harnessio/design-system": "^2.1.1",
|
||||
"@harnessio/icons": "^2.1.3",
|
||||
"@harnessio/icons": "^2.1.5",
|
||||
"@harnessio/uicore": "^4.1.2",
|
||||
"@types/dompurify": "^3.0.2",
|
||||
"@types/react-monaco-editor": "^0.16.0",
|
||||
|
|
|
@ -51,7 +51,7 @@ import CodeSearchPage from 'pages/Search/CodeSearchPage'
|
|||
import AddUpdatePipeline from 'pages/AddUpdatePipeline/AddUpdatePipeline'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import PipelineSettings from 'components/PipelineSettings/PipelineSettings'
|
||||
import GitspaceDetail from 'cde/pages/GitspaceDetail/GitspaceDetail'
|
||||
import { GitspaceDetails } from 'cde-gitness/pages/GitspaceDetails/GitspaceDetails'
|
||||
import { GitspaceListing } from 'cde-gitness/pages/GitspaceListing/GitspaceListing'
|
||||
import { GitspaceCreate } from 'cde-gitness/pages/GitspaceCreate/GitspaceCreate'
|
||||
|
||||
|
@ -283,7 +283,7 @@ export const RouteDestinations: React.FC = React.memo(function RouteDestinations
|
|||
{standalone && (
|
||||
<Route path={routes.toCDEGitspaceDetail({ space: pathProps.space, gitspaceId: pathProps.gitspaceId })}>
|
||||
<LayoutWithSideNav title={getString('cde.gitspaces')}>
|
||||
<GitspaceDetail />
|
||||
<GitspaceDetails />
|
||||
</LayoutWithSideNav>
|
||||
</Route>
|
||||
)}
|
||||
|
|
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 95 KiB |
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M3.429 6.32a.8.8 0 0 1 .793-.907h7.099a.8.8 0 0 1 .796.88l-.693 6.805a.8.8 0 0 1-.795.72H5.145a.8.8 0 0 1-.793-.693L3.43 6.32Zm7.892-.107h-7.1l.924 6.805h5.484l.692-6.805ZM6.61 1.729a.4.4 0 0 1 .48-.3l2.816.642a.4.4 0 0 1 .301.48l-.206.902 2.573.588a.4.4 0 1 1-.178.78L3.69 2.832a.4.4 0 0 1 .178-.78l2.537.58.206-.903Zm.574 1.08 2.037.466.117-.513-2.036-.465-.118.513Z" fill="#E43326"/><path opacity=".5" fill-rule="evenodd" clip-rule="evenodd" d="M8 7.872c.184 0 .334.15.334.333v3.334a.333.333 0 1 1-.667 0V8.205c0-.184.15-.333.333-.333ZM6.16 7.88a.333.333 0 0 1 .36.303l.29 3.32a.333.333 0 1 1-.663.059l-.29-3.321a.333.333 0 0 1 .302-.361ZM9.841 7.88a.333.333 0 0 1 .303.36l-.29 3.322a.333.333 0 1 1-.664-.059l.29-3.32a.333.333 0 0 1 .361-.303Z" fill="#E43326"/></svg>
|
After Width: | Height: | Size: 891 B |
Before Width: | Height: | Size: 395 B After Width: | Height: | Size: 395 B |
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 70 70"><path fill-rule="evenodd" clip-rule="evenodd" d="M54.704 60H13.508C6.048 60 0 53.844 0 46.25c0-6.662 4.654-12.217 10.834-13.48a.275.275 0 0 0 .218-.27c0-12.427 9.896-22.5 22.104-22.5 10.62 0 19.49 7.622 21.619 17.785.117.559.589.971 1.148 1.018C63.805 29.463 70 36.183 70 44.375c0 8.419-6.541 15.283-14.733 15.612a.006.006 0 0 0-.006.007.006.006 0 0 1-.006.006h-.551Zm-27.886-7.613a2.817 2.817 0 0 1 2.817-2.818h12.963a2.817 2.817 0 1 1 0 5.636H29.635a2.818 2.818 0 0 1-2.817-2.818Zm4.734-29.431a2.818 2.818 0 0 0-3.834 4.129l7.859 7.297-7.859 7.298a2.817 2.817 0 1 0 3.834 4.13l10.083-9.363a2.817 2.817 0 0 0 0-4.13l-10.083-9.361Z" fill="#004BA4" opacity=".8"/></svg>
|
After Width: | Height: | Size: 740 B |
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17"><path fill="#fff" fill-opacity=".01" d="M0 .5h16v16H0z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M7.551.737a.64.64 0 0 1 .898 0l7.2 7.083a.64.64 0 0 1-.898.912l-.884-.87v5.971a.533.533 0 0 1-.534.534H2.668a.533.533 0 0 1-.534-.534V7.862l-.884.87a.64.64 0 1 1-.898-.912l7.2-7.083Zm.45 1.354 4.8 4.722V13.3h-2.134V9.567a.533.533 0 0 0-.534-.534h-3.2a.533.533 0 0 0-.533.534V13.3H3.2V6.813L8 2.09ZM7.466 13.3H9.6v-3.2H7.467v3.2Z" fill="#030010" fill-opacity=".612"/></svg>
|
After Width: | Height: | Size: 550 B |
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 92 87"><path d="m8.64 62.856-1.671-.967 5.803-9.233 1.846.88-5.979 9.32ZM31.5 63.472l-1.582.615-6.33-8.617h2.637l5.276 8.002Z" fill="#642F08"/><path d="M10.509 5.266C7.9 7.22 8.245 26.724 8.743 36.23c.815 1.54 2.092 5.84.68 10.73-1.413 4.888 4.21 6.654 7.197 6.925l6.519-6.926.815-16.569L30.2 7.71c-.996-1.675-3.64-5.242-6.247-6.111C20.694.512 13.768 2.82 10.509 5.266ZM5.142 65.362c-1.208-1.542.504-2.9 1.51-3.385 2.46.666 2.657 2.118 2.449 2.76-.816.851-2.75 2.167-3.959.625ZM29.624 66.925c-2.084-1.334-.59-2.778.417-3.334 1.254-1.146 3.281 0 3.542 1.771.26 1.771-1.355 3.23-3.96 1.563Z" fill="#00ADE4"/><path d="M1.287 44.358v-3.922l42.136-18.728 18.601-4.049 28.977 14.299v4.049L56.457 50.81 34.44 60.175l-10.503-5.188v21.385l12.02 5.947v2.15l-3.289 1.266L4.324 71.816v-1.771l3.163-1.392 8.858 3.923V51.824L1.287 44.358Z" fill="#A64F0E"/><path d="M67.97 51.57v-5.567l6.833-3.543v12.274l12.021 5.694v2.404l-2.784 1.012-27.458-13.16 4.935-2.024 6.453 2.91Z" fill="#A64F0E"/><path d="M33.775 56.084v4.143" stroke="#000" stroke-width=".627" stroke-linecap="round" stroke-linejoin="round"/><path d="M90.85 32.03v4.138M1 39.953v4.138" stroke="#000" stroke-width=".627" stroke-linecap="round"/><path d="m33.746 56.185 56.699-24.054M34.15 60.222l56.7-24.054M1 39.953l33.15 16.07M57.7 16.06l33.149 16.07M1 44.153l33.15 16.07M16.205 51.638V75.08M22.072 54.717v23.441M23.516 55.294v23.442M32.74 83.65l3.009-1.277M7.173 68.515l-3.009 1.276v2.052M32.74 85.601l3.027-1.284M4.182 71.88 32.78 85.743M4.232 69.772l28.65 13.888M35.62 84.206v-1.84l-11.84-5.74m-7.613-3.69-9.001-4.363m25.725 15.102v1.839M67.545 45.929v7.706M73.412 56.713V43.598M74.856 57.29V43.069M84.08 62.205l3.009-1.276M84.08 64.157l3.027-1.284M84.12 64.298 56.41 50.865M84.222 62.216l-25.474-12.35M86.962 62.76v-1.838l-11.84-5.74m-7.614-3.691-5.84-2.831m22.564 13.57v1.84" stroke="#000" stroke-width=".627" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.58 36.714c-.262-6.654-.651-12.75 0-19.392.354-3.611.207-7.548 1.235-11.05.54-1.839 4.72-3.688 7.82-4.648C23.57-.213 27.4 2.192 30.053 7.94M8.683 48.342c-.16 1.852 2.131 3.954 3.59 4.716 1.067.558 2.705.943 3.819.943M12.707 53.505l-5.82 8.293M14.58 53.984l-5.74 8.491M9.218 64.67c-.614 1.842-3.199 2.258-4.34.723-.834-1.155-.413-3.057 1.167-3.456 1.488-.375 3.766.953 3.173 2.733ZM31.21 63.009l-4.32-6.154m-3.374-1.66 5.74 8.491m3.963 2.918c-1.142 1.535-3.727 1.119-4.34-.723-.594-1.78 1.684-3.108 3.172-2.733 1.58.399 2.002 2.301 1.168 3.456Z" stroke="#000" stroke-width=".627" stroke-linecap="round"/><path d="m33.51 29.878-11.613 5.02L34.1 41.1l28.347-11.91V9.111L34.1 21.217l-.59 8.661Z" fill="#6B6D85"/><path d="M21.395 35.097 33.88 29.8" stroke="#000" stroke-width=".627" stroke-linecap="round" stroke-linejoin="round"/><path d="m24.095 29.985-7.172 3.306c-1.953-1.831 1.797-7.884 3.917-10.68l1.474-3.765C17.758 10.584 25.74 8.012 30.3 7.76c8.137 1.22 7.256 6.849 5.798 9.51l2.746 1.577-4.984 2.187v4.883l-5.85 2.594c-.243 2.889 2.85 6.154 4.426 7.425-1.22.163-6.07-3.899-8.341-5.95Z" fill="#fff"/><path d="m33.824 21 28.35-12.026M62.174 9.301v19.795l-28.35 12.027v-20.06" stroke="#000" stroke-width=".627" stroke-linecap="round" stroke-linejoin="round"/><path d="m33.826 41.123-8.288-4.017-4.143-2.01" stroke="#000" stroke-width=".627" stroke-linecap="round" stroke-linejoin="round"/><path d="M22.273 18.912c-4.277 5.477 2.631 13.572 10.276 17.256" stroke="#000" stroke-width=".627" stroke-linecap="round"/><path d="M32.041 35.787s-4.23-3.827-4.23-7.511c0-3.004 3.304-5.248 5.877-3.705m5.206-5.579-2.74-1.768M20.723 22.307c-1.74 3.544-5.866 9.365-3.91 11.114M36.97 12.943c.485 1.923-.575 3.712-1.841 5.1-3.42 3.749-14.14 3.336-14.4-3.064-.333-8.147 14.154-10.304 16.24-2.036Z" stroke="#000" stroke-width=".627" stroke-linecap="round"/><path d="M28.016 15.516c.823 2.778 3.602 3.601 6.998 2.572" stroke="#000" stroke-width=".627" stroke-linecap="round"/><path d="M1 39.952 24.226 30.1m3.652-1.55 6.092-2.584" stroke="#000" stroke-width=".627" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
After Width: | Height: | Size: 4.0 KiB |
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17"><path d="M4 12.833a1 1 0 0 0 1 1h.125a1 1 0 0 0 1-1V4.167a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v8.666Zm6.323-9.666a1 1 0 0 0-1 1v8.666a1 1 0 0 0 1 1h.145a1 1 0 0 0 1-1V4.167a1 1 0 0 0-1-1h-.145Z" fill="#6B6D85"/></svg>
|
After Width: | Height: | Size: 282 B |
After Width: | Height: | Size: 548 KiB |
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#fff" fill-opacity=".01" d="M0 0h16v16H0z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M8 1.92a6.08 6.08 0 1 0 0 12.16A6.08 6.08 0 0 0 8 1.92ZM.96 8a7.04 7.04 0 1 1 14.08 0A7.04 7.04 0 0 1 .96 8Z" fill="#6B6D85"/><path fill-rule="evenodd" clip-rule="evenodd" d="M14.4 8.427H1.6v-.854h12.8v.854Z" fill="#6B6D85"/><path fill-rule="evenodd" clip-rule="evenodd" d="M7.573 14.4V1.6h.853v12.8h-.853ZM11.066 8c0-2.317-.833-4.61-2.467-6.126l.508-.548C10.922 3.01 11.813 5.518 11.813 8s-.891 4.99-2.706 6.674l-.508-.548c1.634-1.515 2.467-3.808 2.467-6.126Zm-6.8 0c0-2.479.862-4.985 2.622-6.67l.516.54C5.822 3.385 5.013 5.679 5.013 8c0 2.321.809 4.615 2.391 6.13l-.516.54C5.128 12.985 4.266 10.479 4.266 8Z" fill="#6B6D85"/><path fill-rule="evenodd" clip-rule="evenodd" d="M8 4.222c2.314 0 4.667.428 6.262 1.319a.373.373 0 1 1-.364.652c-1.44-.805-3.65-1.224-5.898-1.224-2.249 0-4.458.42-5.898 1.224a.373.373 0 0 1-.364-.652C3.333 4.65 5.686 4.22 8 4.22Zm0 7.351c2.314 0 4.667-.428 6.262-1.319a.373.373 0 1 0-.364-.651c-1.44.804-3.65 1.224-5.898 1.224-2.249 0-4.458-.42-5.898-1.224a.373.373 0 1 0-.364.651c1.595.891 3.948 1.32 6.262 1.32Z" fill="#6B6D85"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2023 Harness, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@import 'src/utils/utils';
|
||||
|
||||
.consoleContainer {
|
||||
min-height: 500px !important;
|
||||
max-height: 70vh !important;
|
||||
overflow: scroll;
|
||||
align-items: start !important;
|
||||
padding-top: var(--spacing-large) !important;
|
||||
padding-left: var(--spacing-xlarge) !important;
|
||||
background-color: var(--black) !important;
|
||||
color: var(--white) !important;
|
||||
pre {
|
||||
background-color: var(--black) !important;
|
||||
color: var(--white) !important;
|
||||
text-align: start !important;
|
||||
font-size: 12px;
|
||||
font-family: var(--font-family-mono) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.logTitle {
|
||||
color: var(--white) !important;
|
||||
background: var(--black) !important;
|
||||
border-top-left-radius: 50px;
|
||||
border-top-right-radius: 50px;
|
||||
padding-top: var(--spacing-medium) !important;
|
||||
text-align: left;
|
||||
padding-left: var(--spacing-xxlarge) !important;
|
||||
padding-bottom: var(--spacing-large) !important;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.mainLog {
|
||||
flex-shrink: 0;
|
||||
|
||||
.line {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
cursor: text;
|
||||
min-height: 20px;
|
||||
display: block;
|
||||
|
||||
@include mono-font;
|
||||
color: var(--white);
|
||||
|
||||
word-wrap: break-word !important;
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
}
|
||||
|
||||
.stepLogContainer {
|
||||
padding: var(--spacing-small) !important;
|
||||
flex-shrink: 0;
|
||||
|
||||
.consoleLine {
|
||||
color: var(--white);
|
||||
|
||||
@include mono-font;
|
||||
|
||||
word-wrap: break-word !important;
|
||||
white-space: pre-wrap !important;
|
||||
cursor: text;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
&:empty {
|
||||
display: inline-block;
|
||||
min-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logContainer {
|
||||
width: 60%;
|
||||
max-width: 800px !important;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2023 Harness, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
// This is an auto-generated file
|
||||
export declare const consoleContainer: string
|
||||
export declare const consoleLine: string
|
||||
export declare const line: string
|
||||
export declare const logContainer: string
|
||||
export declare const logTitle: string
|
||||
export declare const mainLog: string
|
||||
export declare const stepLogContainer: string
|
|
@ -0,0 +1,52 @@
|
|||
import { Container } from '@harnessio/uicore'
|
||||
import cx from 'classnames'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { lineElement } from 'components/LogViewer/LogViewer'
|
||||
import type { LogData } from '../../hooks/useGetLogStream'
|
||||
import css from './ContainerLogs.module.scss'
|
||||
|
||||
const ContainerLogs = ({ data }: { data: LogData[] }) => {
|
||||
const localRef = useRef<HTMLDivElement | null>()
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (data) {
|
||||
const fragment = new DocumentFragment()
|
||||
const logContainer = localRef.current as HTMLDivElement
|
||||
// Clear the container first
|
||||
if (localRef.current) {
|
||||
localRef.current.innerHTML = ''
|
||||
}
|
||||
if (data) {
|
||||
data?.forEach((line: any) => {
|
||||
const linePos = line.pos + 1
|
||||
const localDate = new Date(line.time)
|
||||
// Format date to a more readable format (local time)
|
||||
const formattedDate = localDate.toLocaleString()
|
||||
fragment.appendChild(lineElement(`${linePos} ${formattedDate.replace(',', '')} ${line.out}`))
|
||||
})
|
||||
|
||||
logContainer.appendChild(fragment)
|
||||
}
|
||||
|
||||
const scrollParent = logContainer.parentElement as HTMLDivElement
|
||||
const autoScroll =
|
||||
scrollParent && scrollParent.scrollTop === scrollParent.scrollHeight - scrollParent.offsetHeight
|
||||
|
||||
if (autoScroll || scrollParent.scrollTop === 0) {
|
||||
scrollParent.scrollTop = scrollParent.scrollHeight
|
||||
}
|
||||
}
|
||||
} catch (_err) {
|
||||
//
|
||||
}
|
||||
}, [data])
|
||||
|
||||
return (
|
||||
<Container className={css.consoleContainer}>
|
||||
<Container key={`harnesslog`} ref={localRef} className={cx(css.mainLog, css.stepLogContainer)} />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContainerLogs
|
|
@ -0,0 +1,89 @@
|
|||
import { Color } from '@harnessio/design-system'
|
||||
import { Layout, Text } from '@harnessio/uicore'
|
||||
import React from 'react'
|
||||
import ReactTimeago from 'react-timeago'
|
||||
import { Circle } from 'iconoir-react'
|
||||
import type { IconName } from '@harnessio/icons'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { getIconByRepoType } from 'cde/components/CreateGitspace/components/SelectRepository/SelectRepository.utils'
|
||||
import type { TypesGitspaceConfig } from 'cde-gitness/services'
|
||||
import { GitspaceStatus } from 'cde/constants'
|
||||
import { getStatusColor, getStatusText } from '../GitspaceListing/ListGitspaces'
|
||||
|
||||
export const DetailsCard = ({ data }: { data: TypesGitspaceConfig | null; loading?: boolean }) => {
|
||||
const { getString } = useStrings()
|
||||
const { branch, state, name, code_repo_url, code_repo_type, instance } = data || {}
|
||||
const color = getStatusColor(state)
|
||||
const customProps =
|
||||
state === GitspaceStatus.STARTING
|
||||
? {
|
||||
icon: 'loading' as IconName,
|
||||
iconProps: { color: Color.PRIMARY_4 }
|
||||
}
|
||||
: { icon: undefined }
|
||||
return (
|
||||
<>
|
||||
<Layout.Horizontal
|
||||
width={'80%'}
|
||||
flex={{ justifyContent: 'space-between' }}
|
||||
padding={{ bottom: 'xlarge', top: 'xlarge' }}>
|
||||
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
|
||||
<Text>{getString('cde.status')}</Text>
|
||||
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
|
||||
{state !== GitspaceStatus.STARTING && <Circle height={10} width={10} color={color} fill={color} />}
|
||||
<Text
|
||||
{...customProps}
|
||||
color={Color.BLACK}
|
||||
title={name}
|
||||
font={{ align: 'left', size: 'normal', weight: 'semi-bold' }}>
|
||||
{getStatusText(getString, state)}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
</Layout.Vertical>
|
||||
|
||||
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
|
||||
<Text>{getString('cde.repository.repo')}</Text>
|
||||
<Layout.Horizontal
|
||||
spacing="small"
|
||||
flex={{ alignItems: 'center', justifyContent: 'start' }}
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}}>
|
||||
{getIconByRepoType({ repoType: code_repo_type, height: 20 })}
|
||||
<Text
|
||||
title={'RepoName'}
|
||||
color={Color.PRIMARY_7}
|
||||
margin={{ left: 'small' }}
|
||||
style={{ cursor: 'pointer' }}
|
||||
font={{ align: 'left', size: 'normal' }}
|
||||
onClick={() => window.open(code_repo_url, '_blank')}>
|
||||
{name}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
</Layout.Vertical>
|
||||
|
||||
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
|
||||
<Text>{getString('branch')}</Text>
|
||||
<Text
|
||||
iconProps={{ size: 10 }}
|
||||
color={Color.PRIMARY_7}
|
||||
icon="git-branch"
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => window.open(code_repo_url, '_blank')}>
|
||||
{branch}
|
||||
</Text>
|
||||
</Layout.Vertical>
|
||||
|
||||
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
|
||||
<Text>{getString('cde.lastUsed')}</Text>
|
||||
{instance?.last_used ? (
|
||||
<ReactTimeago date={instance?.last_used || 0} />
|
||||
) : (
|
||||
<Text color={Color.GREY_500}>{getString('cde.na')}</Text>
|
||||
)}
|
||||
</Layout.Vertical>
|
||||
</Layout.Horizontal>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
.marker {
|
||||
align-self: center;
|
||||
position: relative;
|
||||
left: 5.5px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
background: var(--grey-400) !important;
|
||||
border-radius: 6px;
|
||||
padding: 2px;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
.main {
|
||||
overflow: scroll;
|
||||
max-height: 350px;
|
||||
}
|
||||
|
||||
.lightBackground {
|
||||
background: #dee8f9 !important;
|
||||
}
|
||||
.darkBackground {
|
||||
background: #bbd1f6 !important;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2023 Harness, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
// This is an auto-generated file
|
||||
export declare const darkBackground: string
|
||||
export declare const lightBackground: string
|
||||
export declare const main: string
|
||||
export declare const marker: string
|
|
@ -0,0 +1,80 @@
|
|||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { Color } from '@harnessio/design-system'
|
||||
import { Container, Text, Layout } from '@harnessio/uicore'
|
||||
import { isArray, isEqual } from 'lodash-es'
|
||||
import type { TypesGitspaceEventResponse } from 'cde-gitness/services'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { formatTimestamp } from './EventTimeline.utils'
|
||||
import css from './EventTimeline.module.scss'
|
||||
|
||||
const EventTimeline = ({ data }: { data?: TypesGitspaceEventResponse[] | null; polling?: boolean }) => {
|
||||
const localRef = useRef<HTMLDivElement | null>(null)
|
||||
const scrollContainerRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const { getString } = useStrings()
|
||||
const [cache, setCache] = useState(data)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEqual(data, cache)) {
|
||||
setCache(data)
|
||||
}
|
||||
}, [data])
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollContainerRef.current) {
|
||||
const scrollParent = scrollContainerRef.current
|
||||
|
||||
const autoScroll = scrollParent.scrollTop <= scrollParent.scrollHeight - scrollParent.clientHeight
|
||||
|
||||
if (autoScroll || scrollParent.scrollTop === 0) {
|
||||
scrollParent.scrollTop = scrollParent.scrollHeight
|
||||
}
|
||||
}
|
||||
}, [cache])
|
||||
|
||||
return (
|
||||
<Container className={css.main} ref={scrollContainerRef}>
|
||||
{!data?.length && isArray(data) && (
|
||||
<Container
|
||||
width={'100%'}
|
||||
padding={{ left: 'large' }}
|
||||
flex={{ alignItems: 'center' }}
|
||||
height={'64px'}
|
||||
border={{ left: true, color: Color.GREY_200 }}
|
||||
background={Color.PRIMARY_2}>
|
||||
<Text iconProps={{ color: Color.GREEN_450 }} icon="no-deployments">
|
||||
{getString('cde.details.fetchingDetails')}
|
||||
</Text>
|
||||
</Container>
|
||||
)}
|
||||
<Container ref={localRef}>
|
||||
{data?.map((item, index) => {
|
||||
return (
|
||||
<Layout.Horizontal background={Color.GREY_50} key={item.query_key}>
|
||||
<Container
|
||||
background={Color.GREY_50}
|
||||
width={'8%'}
|
||||
flex={{ alignItems: 'center', justifyContent: 'center' }}>
|
||||
{formatTimestamp(item.timestamp || 0)}
|
||||
</Container>
|
||||
<Text className={css.marker} />
|
||||
<Container
|
||||
width={'92%'}
|
||||
padding={{ left: 'large' }}
|
||||
flex={{ alignItems: 'center' }}
|
||||
height={'64px'}
|
||||
border={{ left: true, color: Color.GREY_200 }}
|
||||
className={index % 2 ? css.lightBackground : css.darkBackground}>
|
||||
<Text iconProps={{ color: Color.GREEN_450 }} icon="tick-circle">
|
||||
{`${item.message}`}
|
||||
</Text>
|
||||
</Container>
|
||||
</Layout.Horizontal>
|
||||
)
|
||||
})}
|
||||
</Container>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default EventTimeline
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react'
|
||||
import moment from 'moment'
|
||||
import { Text, Layout } from '@harnessio/uicore'
|
||||
import { Color } from '@harnessio/design-system'
|
||||
|
||||
export const formatTimestamp = (timestamp: number) => {
|
||||
const inputDate = moment(timestamp)
|
||||
const currentDate = moment()
|
||||
|
||||
if (inputDate.isSame(currentDate, 'day')) {
|
||||
return (
|
||||
<Text width="70%" color={Color.GREY_500}>
|
||||
{inputDate.format('HH:mm:ss')}
|
||||
</Text>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Layout.Vertical width="70%" spacing="small">
|
||||
<Text font={{ size: 'small' }} color={Color.GREY_500}>
|
||||
{inputDate.format('YYYY-MM-DD')}
|
||||
</Text>
|
||||
<Text font={{ size: 'small' }} color={Color.GREY_500}>
|
||||
{inputDate.format('HH:mm:ss')}
|
||||
</Text>
|
||||
</Layout.Vertical>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import React from 'react'
|
||||
import { defaultTo } from 'lodash-es'
|
||||
import { Accordion } from '@harnessio/uicore'
|
||||
import type { TypesGitspaceEventResponse } from 'cde-gitness/services'
|
||||
import EventTimelineSummary from '../EventTimelineSummary/EventTimelineSummary'
|
||||
import EventTimeline from '../EventTimeline/EventTimeline'
|
||||
import parentCss from 'cde-gitness/pages/GitspaceDetails/GitspaceDetails.module.scss'
|
||||
|
||||
const EventTimelineAccordion = ({ data }: { data: TypesGitspaceEventResponse[] | null; polling?: boolean }) => {
|
||||
const sortedData = data?.sort((a, b) => defaultTo(a?.timestamp, 0) - defaultTo(b?.timestamp, 0))
|
||||
const latestEvent = sortedData?.[sortedData?.length - 1] || { message: '', timestamp: 0 }
|
||||
return (
|
||||
<Accordion activeId="eventsCard">
|
||||
<Accordion.Panel
|
||||
shouldRender
|
||||
id="eventsCard"
|
||||
details={<EventTimeline data={sortedData} />}
|
||||
summary={<EventTimelineSummary message={latestEvent.message} timestamp={latestEvent.timestamp} />}
|
||||
className={parentCss.accordionnCustomSummary}
|
||||
/>
|
||||
</Accordion>
|
||||
)
|
||||
}
|
||||
|
||||
export default EventTimelineAccordion
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react'
|
||||
import moment from 'moment'
|
||||
import { Container, Text, Layout } from '@harnessio/uicore'
|
||||
import { Color, FontVariation } from '@harnessio/design-system'
|
||||
import { useStrings } from 'framework/strings'
|
||||
|
||||
const EventTimelineSummary = ({ timestamp, message }: { timestamp?: number; message?: string }) => {
|
||||
const { getString } = useStrings()
|
||||
return (
|
||||
<Container width="100%" flex={{ alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text font={{ variation: FontVariation.CARD_TITLE }} margin={{ left: 'large' }}>
|
||||
{getString('cde.details.gitspaceActivity')}
|
||||
</Text>
|
||||
{Boolean(message) && (
|
||||
<Layout.Horizontal spacing="large" flex={{ alignItems: 'center' }}>
|
||||
<Text iconProps={{ color: Color.GREEN_450 }} icon="tick-circle">
|
||||
{message}
|
||||
</Text>
|
||||
<Text margin={{ left: 'large' }} font={{ size: 'small' }}>
|
||||
{moment(timestamp).format('DD MMM, YYYY hh:mma')}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default EventTimelineSummary
|
|
@ -1,3 +1,8 @@
|
|||
.repoAndBranch {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.noReposContainer {
|
||||
padding: var(--spacing-large) !important;
|
||||
background-color: var(--grey-50) !important;
|
||||
}
|
||||
|
|
|
@ -16,4 +16,5 @@
|
|||
|
||||
/* eslint-disable */
|
||||
// This is an auto-generated file
|
||||
export declare const noReposContainer: string
|
||||
export declare const repoAndBranch: string
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useGet } from 'restful-react'
|
||||
import { Container, ExpandingSearchInput, Layout, Text } from '@harnessio/uicore'
|
||||
import { Button, ButtonVariation, Container, ExpandingSearchInput, Layout, Text } from '@harnessio/uicore'
|
||||
import { Menu, MenuItem } from '@blueprintjs/core'
|
||||
import { Color } from '@harnessio/design-system'
|
||||
import { Icon } from '@harnessio/icons'
|
||||
|
@ -10,8 +10,10 @@ import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
|||
import { String, useStrings } from 'framework/strings'
|
||||
import { LIST_FETCHING_LIMIT } from 'utils/Utils'
|
||||
import NewRepoModalButton from 'components/NewRepoModalButton/NewRepoModalButton'
|
||||
import noRepo from 'cde-gitness/assests/noRepo.svg?url'
|
||||
import { RepoCreationType } from 'utils/GitUtils'
|
||||
import gitnessRepoLogo from 'cde-gitness/assests/gitness.svg?url'
|
||||
import { GitspaceSelect } from '../../../cde/components/GitspaceSelect/GitspaceSelect'
|
||||
import gitnessRepoLogo from './gitness.svg?url'
|
||||
import css from './GitnessRepoImportForm.module.scss'
|
||||
|
||||
const RepositoryText = ({ repoList, value }: { repoList: TypesRepository[] | null; value?: string }) => {
|
||||
|
@ -64,6 +66,7 @@ export const GitnessRepoImportForm = () => {
|
|||
const space = useGetSpaceParam()
|
||||
const [branchSearch, setBranchSearch] = useState('')
|
||||
const [repoSearch, setRepoSearch] = useState('')
|
||||
const [hadReops, setHadRepos] = useState(false)
|
||||
const [repoRef, setReporef] = useState('')
|
||||
|
||||
const {
|
||||
|
@ -76,6 +79,12 @@ export const GitnessRepoImportForm = () => {
|
|||
debounce: 500
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!hadReops && repositories?.length) {
|
||||
setHadRepos(true)
|
||||
}
|
||||
}, [repositories])
|
||||
|
||||
const {
|
||||
data: branches,
|
||||
refetch,
|
||||
|
@ -100,6 +109,7 @@ export const GitnessRepoImportForm = () => {
|
|||
}, [repoRef, branchSearch])
|
||||
|
||||
const repoListOptions = repositories || []
|
||||
const hideInitialMenu = Boolean(repoSearch) || Boolean(repositories)
|
||||
|
||||
const formik = useFormikContext<any>()
|
||||
|
||||
|
@ -124,15 +134,17 @@ export const GitnessRepoImportForm = () => {
|
|||
}}
|
||||
renderMenu={
|
||||
<Menu>
|
||||
<Container margin={'small'}>
|
||||
<ExpandingSearchInput
|
||||
placeholder={getString('cde.create.searchRepositoryPlaceholder')}
|
||||
alwaysExpanded
|
||||
autoFocus={false}
|
||||
defaultValue={repoSearch}
|
||||
onChange={setRepoSearch}
|
||||
/>
|
||||
</Container>
|
||||
{hideInitialMenu && (
|
||||
<Container margin={'small'}>
|
||||
<ExpandingSearchInput
|
||||
placeholder={getString('cde.create.searchRepositoryPlaceholder')}
|
||||
alwaysExpanded
|
||||
autoFocus={false}
|
||||
defaultValue={repoSearch}
|
||||
onChange={setRepoSearch}
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
{loading ? (
|
||||
<MenuItem disabled text={getString('loading')} />
|
||||
) : repoListOptions?.length ? (
|
||||
|
@ -147,11 +159,12 @@ export const GitnessRepoImportForm = () => {
|
|||
}
|
||||
active={repo.git_url === values.code_repo_url}
|
||||
onClick={() => {
|
||||
const repoParams = repo?.path?.split('/') || []
|
||||
formik.setValues((prvValues: any) => {
|
||||
return {
|
||||
...prvValues,
|
||||
code_repo_url: repo.git_url,
|
||||
id: repo.path,
|
||||
identifier: repoParams?.[repoParams.length - 1],
|
||||
name: repo.path
|
||||
}
|
||||
})
|
||||
|
@ -159,18 +172,69 @@ export const GitnessRepoImportForm = () => {
|
|||
}}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
) : hideInitialMenu ? (
|
||||
<Container>
|
||||
<NewRepoModalButton
|
||||
space={space}
|
||||
newRepoModalOnly
|
||||
notFoundRepoName={repoSearch}
|
||||
repoCreationType={RepoCreationType.CREATE}
|
||||
customRenderer={fn => (
|
||||
<MenuItem
|
||||
icon="plus"
|
||||
text={<String stringID="cde.create.repoNotFound" vars={{ repo: repoSearch }} useRichText />}
|
||||
onClick={fn}
|
||||
/>
|
||||
)}
|
||||
modalTitle={getString('createRepo')}
|
||||
onSubmit={() => {
|
||||
refetchRepos()
|
||||
}}
|
||||
/>
|
||||
</Container>
|
||||
) : !hadReops ? (
|
||||
<Container>
|
||||
<Layout.Vertical
|
||||
spacing="medium"
|
||||
className={css.noReposContainer}
|
||||
flex={{ justifyContent: 'center' }}>
|
||||
<img src={noRepo} height={90} width={90} />
|
||||
<Layout.Vertical spacing="small" flex={{ alignItems: 'center' }}>
|
||||
<Text color={Color.PRIMARY_10} font={{ size: 'normal', weight: 'bold' }}>
|
||||
{getString('cde.getStarted')}
|
||||
</Text>
|
||||
<Text color={Color.PRIMARY_10} font={{ size: 'normal', weight: 'bold' }}>
|
||||
{getString('cde.createImport')}
|
||||
</Text>
|
||||
</Layout.Vertical>
|
||||
<NewRepoModalButton
|
||||
space={space}
|
||||
repoCreationType={RepoCreationType.CREATE}
|
||||
customRenderer={fn => (
|
||||
<Button width={'80%'} variation={ButtonVariation.PRIMARY} onClick={fn}>
|
||||
{getString('createNewRepo')}
|
||||
</Button>
|
||||
)}
|
||||
modalTitle={getString('newRepo')}
|
||||
onSubmit={() => {
|
||||
refetchRepos()
|
||||
}}
|
||||
/>
|
||||
<NewRepoModalButton
|
||||
space={space}
|
||||
repoCreationType={RepoCreationType.IMPORT}
|
||||
customRenderer={fn => (
|
||||
<Button width={'80%'} variation={ButtonVariation.SECONDARY} onClick={fn}>
|
||||
{getString('cde.importInto')}
|
||||
</Button>
|
||||
)}
|
||||
modalTitle={getString('importGitRepo')}
|
||||
onSubmit={() => {
|
||||
refetchRepos()
|
||||
}}
|
||||
/>
|
||||
</Layout.Vertical>
|
||||
</Container>
|
||||
) : (
|
||||
<MenuItem disabled text={getString('loading')} />
|
||||
)}
|
||||
</Menu>
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
}
|
||||
|
||||
.listContainer {
|
||||
border-radius: 20px !important;
|
||||
:global {
|
||||
a.bp3-menu-item:hover,
|
||||
.bp3-active {
|
||||
|
@ -52,4 +53,18 @@
|
|||
|
||||
.repositoryCell {
|
||||
width: fit-content !important;
|
||||
margin-right: var(--spacing-xxlarge) !important;
|
||||
}
|
||||
|
||||
.stopModal {
|
||||
width: 650px !important;
|
||||
[class*='ConfirmationDialog--body'] {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
span[data-icon='info-messaging'] {
|
||||
display: none;
|
||||
}
|
||||
svg {
|
||||
fill: #004ba4;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,5 @@ export declare const gitspaceUrl: string
|
|||
export declare const listContainer: string
|
||||
export declare const popover: string
|
||||
export declare const repositoryCell: string
|
||||
export declare const stopModal: string
|
||||
export declare const table: string
|
||||
|
|
|
@ -14,45 +14,55 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Container, Layout, TableV2, Text, useToaster } from '@harnessio/uicore'
|
||||
import React from 'react'
|
||||
import {
|
||||
ConfirmationDialog,
|
||||
Container,
|
||||
Layout,
|
||||
TableV2,
|
||||
Text,
|
||||
useToaster,
|
||||
Button,
|
||||
ButtonVariation
|
||||
} from '@harnessio/uicore'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Color } from '@harnessio/design-system'
|
||||
import type { Renderer, CellProps } from 'react-table'
|
||||
import ReactTimeago from 'react-timeago'
|
||||
import {
|
||||
Circle,
|
||||
GitBranch,
|
||||
Cpu,
|
||||
Clock,
|
||||
Play,
|
||||
Square,
|
||||
Db,
|
||||
ModernTv,
|
||||
OpenInBrowser,
|
||||
DeleteCircle,
|
||||
EditPencil,
|
||||
ViewColumns2,
|
||||
GithubCircle,
|
||||
GitLabFull,
|
||||
Code,
|
||||
Bitbucket as BitbucketIcon
|
||||
} from 'iconoir-react'
|
||||
import { Menu, MenuItem, PopoverInteractionKind, Position } from '@blueprintjs/core'
|
||||
import { Intent, Menu, MenuItem, PopoverInteractionKind, Position } from '@blueprintjs/core'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { isNil } from 'lodash-es'
|
||||
import { useMutate } from 'restful-react'
|
||||
import type { IconName } from '@harnessio/icons'
|
||||
import { UseStringsReturn, useStrings } from 'framework/strings'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import { getErrorMessage } from 'utils/Utils'
|
||||
import { useConfirmAct } from 'hooks/useConfirmAction'
|
||||
import VSCode from 'cde/icons/VSCode.svg?url'
|
||||
import { GitspaceStatus } from 'cde/constants'
|
||||
import {
|
||||
import { GitspaceActionType, GitspaceStatus } from 'cde/constants'
|
||||
import type {
|
||||
EnumGitspaceStateType,
|
||||
EnumIDEType,
|
||||
useDeleteGitspace,
|
||||
type TypesGitspaceConfig,
|
||||
type EnumCodeRepoType
|
||||
TypesGitspaceConfig,
|
||||
EnumGitspaceCodeRepoType
|
||||
} from 'cde-gitness/services'
|
||||
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||
import gitspaceIcon from 'cde-gitness/assests/gitspace.svg?url'
|
||||
import { useModalHook } from 'hooks/useModalHook'
|
||||
import pause from 'cde-gitness/assests/pause.svg?url'
|
||||
import web from 'cde-gitness/assests/web.svg?url'
|
||||
import deleteIcon from 'cde-gitness/assests/delete.svg?url'
|
||||
import css from './ListGitspaces.module.scss'
|
||||
|
||||
enum CodeRepoType {
|
||||
|
@ -63,18 +73,18 @@ enum CodeRepoType {
|
|||
Unknown = 'unknown'
|
||||
}
|
||||
|
||||
const getIconByRepoType = ({ repoType }: { repoType?: EnumCodeRepoType }): React.ReactNode => {
|
||||
const getIconByRepoType = ({ repoType }: { repoType?: EnumGitspaceCodeRepoType }): React.ReactNode => {
|
||||
switch (repoType) {
|
||||
case CodeRepoType.Github:
|
||||
return <GithubCircle height={40} />
|
||||
return <GithubCircle height={24} width={24} />
|
||||
case CodeRepoType.Gitlab:
|
||||
return <GitLabFull height={40} />
|
||||
return <GitLabFull height={24} width={24} />
|
||||
case CodeRepoType.Bitbucket:
|
||||
return <BitbucketIcon height={40} />
|
||||
return <BitbucketIcon height={24} width={24} />
|
||||
default:
|
||||
case CodeRepoType.Unknown:
|
||||
case CodeRepoType.HarnessCode:
|
||||
return <Code height={40} />
|
||||
return <Code height={24} width={24} />
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,8 +92,11 @@ export const getStatusColor = (status?: EnumGitspaceStateType) => {
|
|||
switch (status) {
|
||||
case GitspaceStatus.RUNNING:
|
||||
return '#42AB45'
|
||||
case GitspaceStatus.STOPPING:
|
||||
return '#FF832B'
|
||||
case GitspaceStatus.STOPPED:
|
||||
return '#F3F3FA'
|
||||
case GitspaceStatus.UNINITIALIZED:
|
||||
return '#D0D0D9'
|
||||
case GitspaceStatus.ERROR:
|
||||
return '#FF0000'
|
||||
default:
|
||||
|
@ -99,6 +112,10 @@ export const getStatusText = (getString: UseStringsReturn['getString'], status?:
|
|||
return getString('cde.listing.offline')
|
||||
case GitspaceStatus.ERROR:
|
||||
return getString('cde.listing.error')
|
||||
case GitspaceStatus.STARTING:
|
||||
return getString('cde.listing.starting')
|
||||
case GitspaceStatus.STOPPING:
|
||||
return getString('cde.listing.stopping')
|
||||
default:
|
||||
return getString('cde.listing.offline')
|
||||
}
|
||||
|
@ -148,7 +165,7 @@ export const RenderRepository: Renderer<CellProps<TypesGitspaceConfig>> = ({ row
|
|||
const { name, branch, code_repo_url, code_repo_type, instance } = details || {}
|
||||
|
||||
return (
|
||||
<Layout.Vertical>
|
||||
<Layout.Vertical spacing={'small'}>
|
||||
<Layout.Horizontal
|
||||
spacing={'small'}
|
||||
className={css.repositoryCell}
|
||||
|
@ -158,13 +175,20 @@ export const RenderRepository: Renderer<CellProps<TypesGitspaceConfig>> = ({ row
|
|||
e.stopPropagation()
|
||||
window.open(code_repo_url, '_blank')
|
||||
}}>
|
||||
{getIconByRepoType({ repoType: code_repo_type })}
|
||||
<Text className={css.gitspaceUrl} color={Color.PRIMARY_7} title={name} font={{ align: 'left', size: 'normal' }}>
|
||||
<Container height={24} width={24}>
|
||||
{getIconByRepoType({ repoType: code_repo_type })}
|
||||
</Container>
|
||||
<Text lineClamp={1} color={Color.PRIMARY_7} title={name} font={{ align: 'left', size: 'normal' }}>
|
||||
{name}
|
||||
</Text>
|
||||
<Text color={Color.PRIMARY_7}>:</Text>
|
||||
<GitBranch />
|
||||
<Text color={Color.PRIMARY_7} title={name} font={{ align: 'left', size: 'normal' }}>
|
||||
<Text
|
||||
lineClamp={1}
|
||||
icon="git-branch"
|
||||
iconProps={{ size: 12 }}
|
||||
color={Color.PRIMARY_7}
|
||||
title={name}
|
||||
font={{ align: 'left', size: 'normal' }}>
|
||||
{branch}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
|
@ -182,7 +206,7 @@ export const RenderCPUUsage: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }
|
|||
const instance = row.original.instance
|
||||
const { resource_usage, total_time_used } = instance || {}
|
||||
|
||||
return getUsageTemplate(getString, <Cpu />, resource_usage, total_time_used)
|
||||
return getUsageTemplate(getString, <Cpu />, resource_usage as string, total_time_used)
|
||||
}
|
||||
|
||||
export const RenderStorageUsage: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
|
||||
|
@ -190,7 +214,7 @@ export const RenderStorageUsage: Renderer<CellProps<TypesGitspaceConfig>> = ({ r
|
|||
const instance = row.original.instance
|
||||
const { resource_usage, total_time_used } = instance || {}
|
||||
|
||||
return getUsageTemplate(getString, <Db />, resource_usage, total_time_used)
|
||||
return getUsageTemplate(getString, <Db />, resource_usage as string, total_time_used)
|
||||
}
|
||||
|
||||
export const RenderLastActivity: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
|
||||
|
@ -214,13 +238,23 @@ export const RenderLastActivity: Renderer<CellProps<TypesGitspaceConfig>> = ({ r
|
|||
export const RenderGitspaceStatus: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
|
||||
const { getString } = useStrings()
|
||||
const details = row.original
|
||||
const { instance, name } = details
|
||||
const { state } = instance || {}
|
||||
const { name, state } = details
|
||||
const color = getStatusColor(state)
|
||||
const customProps =
|
||||
state === GitspaceStatus.STARTING
|
||||
? {
|
||||
icon: 'loading' as IconName,
|
||||
iconProps: { color: Color.PRIMARY_4 }
|
||||
}
|
||||
: { icon: undefined }
|
||||
return (
|
||||
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
|
||||
<Circle height={10} width={10} color={color} fill={color} />
|
||||
<Text color={Color.BLACK} title={name} font={{ align: 'left', size: 'normal', weight: 'semi-bold' }}>
|
||||
{state !== GitspaceStatus.STARTING && <Circle height={10} width={10} color={color} fill={color} />}
|
||||
<Text
|
||||
{...customProps}
|
||||
color={Color.BLACK}
|
||||
title={name}
|
||||
font={{ align: 'left', size: 'normal', weight: 'semi-bold' }}>
|
||||
{getStatusText(getString, state)}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
|
@ -231,10 +265,14 @@ export const StartStopButton = ({ state, loading }: { state?: EnumGitspaceStateT
|
|||
const { getString } = useStrings()
|
||||
return (
|
||||
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||
{loading ? <></> : state === GitspaceStatus.RUNNING ? <Square /> : <Play />}
|
||||
{loading ? <></> : state === GitspaceStatus.RUNNING ? <img src={pause} height={16} width={16} /> : <Play />}
|
||||
<Text icon={loading ? 'loading' : undefined}>
|
||||
{state === GitspaceStatus.RUNNING
|
||||
? getString('cde.details.stopGitspace')
|
||||
? loading
|
||||
? getString('cde.stopingGitspace')
|
||||
: getString('cde.details.stopGitspace')
|
||||
: loading
|
||||
? getString('cde.startingGitspace')
|
||||
: getString('cde.details.startGitspace')}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
|
@ -246,7 +284,7 @@ export const OpenGitspaceButton = ({ ide }: { ide?: EnumIDEType }) => {
|
|||
|
||||
return (
|
||||
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||
{ide === IDEType.VSCODE ? <ModernTv /> : <OpenInBrowser />}
|
||||
{ide === IDEType.VSCODE ? <ModernTv /> : <img src={web} height={16} width={16} />}
|
||||
<Text>{ide === IDEType.VSCODE ? getString('cde.ide.openVSCode') : getString('cde.ide.openBrowser')}</Text>
|
||||
</Layout.Horizontal>
|
||||
)
|
||||
|
@ -254,8 +292,8 @@ export const OpenGitspaceButton = ({ ide }: { ide?: EnumIDEType }) => {
|
|||
|
||||
interface ActionMenuProps {
|
||||
data: TypesGitspaceConfig
|
||||
refreshList: () => void
|
||||
handleStartStop?: () => Promise<void>
|
||||
handleStartGitspace?: () => void
|
||||
handleStopGitspace?: () => void
|
||||
loading?: boolean
|
||||
actionLoading?: boolean
|
||||
deleteLoading?: boolean
|
||||
|
@ -265,19 +303,21 @@ interface ActionMenuProps {
|
|||
const ActionMenu = ({
|
||||
data,
|
||||
deleteGitspace,
|
||||
refreshList,
|
||||
handleStartStop,
|
||||
handleStartGitspace,
|
||||
handleStopGitspace,
|
||||
actionLoading,
|
||||
deleteLoading
|
||||
}: ActionMenuProps) => {
|
||||
const { getString } = useStrings()
|
||||
const { showError } = useToaster()
|
||||
const { instance, ide } = data
|
||||
const { id, state, url = ' ' } = instance || {}
|
||||
const { instance, ide, identifier = '', space_path = '', state } = data
|
||||
const { identifier: id, url = '' } = instance || {}
|
||||
const history = useHistory()
|
||||
const { routes } = useAppContext()
|
||||
const pathparamsList = instance?.space_path?.split('/') || []
|
||||
const projectIdentifier = pathparamsList[pathparamsList.length - 1] || ''
|
||||
const topBorder = state === GitspaceStatus.RUNNING && !actionLoading ? { top: true } : {}
|
||||
const disabledActionButtons = [GitspaceStatus.STARTING, GitspaceStatus.STOPPING].includes(state as GitspaceStatus)
|
||||
|
||||
return (
|
||||
<Container
|
||||
|
@ -287,58 +327,7 @@ const ActionMenu = ({
|
|||
e.stopPropagation()
|
||||
}}>
|
||||
<Menu>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
history.push(
|
||||
routes.toCDEGitspaceDetail({
|
||||
space: instance?.space_path || '',
|
||||
gitspaceId: instance?.id || ''
|
||||
})
|
||||
)
|
||||
}}
|
||||
text={
|
||||
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||
<ViewColumns2 />
|
||||
<Text>{getString('cde.viewGitspace')}</Text>
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
history.push(
|
||||
routes.toCDEGitspacesEdit({
|
||||
space: instance?.space_path || '',
|
||||
gitspaceId: instance?.id || ''
|
||||
})
|
||||
)
|
||||
}}
|
||||
text={
|
||||
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||
<EditPencil />
|
||||
<Text>{getString('cde.editGitspace')}</Text>
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={async e => {
|
||||
try {
|
||||
if (!actionLoading) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
await handleStartStop?.()
|
||||
await refreshList()
|
||||
}
|
||||
} catch (error) {
|
||||
showError(getErrorMessage(error))
|
||||
}
|
||||
}}
|
||||
text={
|
||||
<Layout.Horizontal spacing="small">
|
||||
<StartStopButton state={state} loading={actionLoading} />
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
/>
|
||||
{ide && state == GitspaceStatus.RUNNING && (
|
||||
{ide && state == GitspaceStatus.RUNNING && !actionLoading && (
|
||||
<MenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
|
@ -346,7 +335,7 @@ const ActionMenu = ({
|
|||
if (ide === IDEType.VSCODE) {
|
||||
window.open(`vscode://harness-inc.gitspaces/${projectIdentifier}/${id}`, '_blank')
|
||||
} else {
|
||||
window.open(url, '_blank')
|
||||
window.open(url || '', '_blank')
|
||||
}
|
||||
}}
|
||||
text={
|
||||
|
@ -356,12 +345,55 @@ const ActionMenu = ({
|
|||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Container border={{ bottom: true, ...topBorder }}>
|
||||
{!disabledActionButtons && (
|
||||
<MenuItem
|
||||
onClick={async e => {
|
||||
try {
|
||||
if (!actionLoading) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
if (state === GitspaceStatus.RUNNING) {
|
||||
handleStopGitspace?.()
|
||||
} else {
|
||||
handleStartGitspace?.()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
showError(getErrorMessage(error))
|
||||
}
|
||||
}}
|
||||
disabled={disabledActionButtons}
|
||||
text={
|
||||
<Layout.Horizontal spacing="small">
|
||||
<StartStopButton state={state} loading={actionLoading} />
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
history.push(
|
||||
routes.toCDEGitspaceDetail({
|
||||
space: space_path,
|
||||
gitspaceId: identifier
|
||||
})
|
||||
)
|
||||
}}
|
||||
text={<Text icon="gitspace">{getString('cde.viewGitspace')}</Text>}
|
||||
/>
|
||||
</Container>
|
||||
|
||||
<MenuItem
|
||||
onClick={deleteGitspace as Unknown as () => void}
|
||||
text={
|
||||
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||
{deleteLoading ? <></> : <DeleteCircle />}
|
||||
<Text icon={deleteLoading ? 'loading' : undefined}>{getString('cde.deleteGitspace')}</Text>
|
||||
{deleteLoading ? <></> : <img src={deleteIcon} height={16} width={16} />}
|
||||
<Text color={Color.RED_450} icon={deleteLoading ? 'loading' : undefined}>
|
||||
{getString('cde.deleteGitspace')}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
/>
|
||||
|
@ -376,24 +408,132 @@ interface RenderActionsProps extends CellProps<TypesGitspaceConfig> {
|
|||
|
||||
export const RenderActions = ({ row, refreshList }: RenderActionsProps) => {
|
||||
const { getString } = useStrings()
|
||||
const space = useGetSpaceParam()
|
||||
const history = useHistory()
|
||||
const { routes } = useAppContext()
|
||||
const { showError, showSuccess } = useToaster()
|
||||
const details = row.original
|
||||
const { instance, name } = details
|
||||
const { mutate: deleteGitspace, loading: deleteLoading } = useDeleteGitspace({})
|
||||
const { identifier, name, space_path } = details
|
||||
// const { mutate: deleteGitspace, loading: deleteLoading } = useDeleteGitspace({})
|
||||
|
||||
// To be added in BE later.
|
||||
// const { mutate: actionGitspace, loading: actionLoading } = useGitspaceAction({
|
||||
// accountIdentifier,
|
||||
// projectIdentifier,
|
||||
// orgIdentifier,
|
||||
// gitspaceIdentifier: instance?.id || ''
|
||||
// })
|
||||
const { mutate: deleteGitspace, loading: deleteLoading } = useMutate<any>({
|
||||
verb: 'DELETE',
|
||||
path: `/api/v1/gitspaces/${space}/${identifier}/+`
|
||||
})
|
||||
|
||||
// const handleStartStop = async () => {
|
||||
// return await actionGitspace({
|
||||
// action: instance?.state === GitspaceStatus.RUNNING ? GitspaceActionType.STOP : GitspaceActionType.START
|
||||
// })
|
||||
// }
|
||||
const { mutate: actionGitspace, loading: actionLoading } = useMutate({
|
||||
verb: 'POST',
|
||||
path: `/api/v1/gitspaces/${space}/${identifier}/+/actions`
|
||||
})
|
||||
|
||||
const [handleStopGitspace, hideModal] = useModalHook(() => {
|
||||
return (
|
||||
<ConfirmationDialog
|
||||
isOpen
|
||||
className={css.stopModal}
|
||||
titleText={
|
||||
<Layout.Vertical flex={{ alignItems: 'self-start' }}>
|
||||
<img src={gitspaceIcon} height={44} />
|
||||
<Text color={Color.BLACK} font="medium">{`Do you want to stop the Gitspace “${name}” ?`}</Text>
|
||||
</Layout.Vertical>
|
||||
}
|
||||
contentText={
|
||||
<Container>
|
||||
<Text margin={{ bottom: 'xxlarge' }}>
|
||||
By clicking on “Stop Gitspace”, the gitspace will start de-provisioning.
|
||||
</Text>
|
||||
<Layout.Horizontal width="100%" flex={{ justifyContent: 'space-between', alignItems: 'self-start' }}>
|
||||
<Layout.Horizontal spacing="medium">
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await actionGitspace({
|
||||
action: GitspaceActionType.STOP
|
||||
})
|
||||
await refreshList()
|
||||
hideModal()
|
||||
}}
|
||||
intent={Intent.PRIMARY}>
|
||||
{getString('cde.details.stopGitspace')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
history.push(
|
||||
routes.toCDEGitspaceDetail({
|
||||
space: space_path as string,
|
||||
gitspaceId: identifier as string
|
||||
})
|
||||
)
|
||||
}}
|
||||
icon="gitspace"
|
||||
variation={ButtonVariation.SECONDARY}>
|
||||
{getString('cde.viewGitspace')}
|
||||
</Button>
|
||||
</Layout.Horizontal>
|
||||
<Button variation={ButtonVariation.TERTIARY} onClick={hideModal}>
|
||||
{getString('cancel')}
|
||||
</Button>
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
}
|
||||
onClose={hideModal}
|
||||
/>
|
||||
)
|
||||
}, [details, actionGitspace, history, routes])
|
||||
|
||||
const [handleStartGitspace, hideStartModal] = useModalHook(() => {
|
||||
return (
|
||||
<ConfirmationDialog
|
||||
isOpen
|
||||
className={css.stopModal}
|
||||
titleText={
|
||||
<Layout.Vertical flex={{ alignItems: 'self-start' }}>
|
||||
<img src={gitspaceIcon} height={44} />
|
||||
<Text color={Color.BLACK} font="medium">{`Do you want to start the Gitspace “${name}” ?`}</Text>
|
||||
</Layout.Vertical>
|
||||
}
|
||||
contentText={
|
||||
<Container>
|
||||
<Text margin={{ bottom: 'xxlarge' }}>
|
||||
By clicking on “Start Gitspace”, the gitspace will start provisioning.
|
||||
</Text>
|
||||
<Layout.Horizontal width="100%" flex={{ justifyContent: 'space-between', alignItems: 'self-start' }}>
|
||||
<Layout.Horizontal spacing="medium">
|
||||
<Button
|
||||
onClick={() => {
|
||||
history.push(
|
||||
`${routes.toCDEGitspaceDetail({
|
||||
space: space_path as string,
|
||||
gitspaceId: identifier as string
|
||||
})}?redirectFrom=login`
|
||||
)
|
||||
}}
|
||||
intent={Intent.PRIMARY}>
|
||||
{getString('cde.details.startGitspace')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
history.push(
|
||||
routes.toCDEGitspaceDetail({
|
||||
space: space_path as string,
|
||||
gitspaceId: identifier as string
|
||||
})
|
||||
)
|
||||
}}
|
||||
icon="gitspace"
|
||||
variation={ButtonVariation.SECONDARY}>
|
||||
{getString('cde.viewGitspace')}
|
||||
</Button>
|
||||
</Layout.Horizontal>
|
||||
<Button variation={ButtonVariation.TERTIARY} onClick={hideStartModal}>
|
||||
{getString('cancel')}
|
||||
</Button>
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
}
|
||||
onClose={hideStartModal}
|
||||
/>
|
||||
)
|
||||
}, [details, actionGitspace, history, routes])
|
||||
|
||||
const confirmDelete = useConfirmAct()
|
||||
|
||||
|
@ -405,7 +545,7 @@ export const RenderActions = ({ row, refreshList }: RenderActionsProps) => {
|
|||
try {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
await deleteGitspace(instance?.id || '')
|
||||
await deleteGitspace({})
|
||||
showSuccess(getString('cde.deleteSuccess'))
|
||||
await refreshList()
|
||||
} catch (exception) {
|
||||
|
@ -426,10 +566,11 @@ export const RenderActions = ({ row, refreshList }: RenderActionsProps) => {
|
|||
tooltip={
|
||||
<ActionMenu
|
||||
data={details}
|
||||
actionLoading={false}
|
||||
actionLoading={actionLoading}
|
||||
deleteLoading={deleteLoading}
|
||||
deleteGitspace={handleDelete}
|
||||
refreshList={refreshList}
|
||||
handleStartGitspace={handleStartGitspace}
|
||||
handleStopGitspace={handleStopGitspace}
|
||||
/>
|
||||
}
|
||||
tooltipProps={{
|
||||
|
@ -447,6 +588,77 @@ export const ListGitspaces = ({ data, refreshList }: { data: TypesGitspaceConfig
|
|||
const { getString } = useStrings()
|
||||
const { routes } = useAppContext()
|
||||
|
||||
const [currentRow, setCurrentRow] = useState<TypesGitspaceConfig>()
|
||||
|
||||
const [handleStartGitspace, hideStartModal] = useModalHook(() => {
|
||||
return (
|
||||
<ConfirmationDialog
|
||||
isOpen
|
||||
className={css.stopModal}
|
||||
onClosed={() => setCurrentRow(undefined)}
|
||||
titleText={
|
||||
<Layout.Vertical flex={{ alignItems: 'self-start' }}>
|
||||
<img src={gitspaceIcon} height={44} />
|
||||
<Text color={Color.BLACK} font="medium">{`Do you want to start the Gitspace “${currentRow?.name}” ?`}</Text>
|
||||
</Layout.Vertical>
|
||||
}
|
||||
contentText={
|
||||
<Container>
|
||||
<Text margin={{ bottom: 'xxlarge' }}>
|
||||
By clicking on “Start Gitspace”, the gitspace will start provisioning.
|
||||
</Text>
|
||||
<Layout.Horizontal width="100%" flex={{ justifyContent: 'space-between', alignItems: 'self-start' }}>
|
||||
<Layout.Horizontal spacing="medium">
|
||||
<Button
|
||||
onClick={() => {
|
||||
history.push(
|
||||
`${routes.toCDEGitspaceDetail({
|
||||
space: currentRow?.space_path as string,
|
||||
gitspaceId: currentRow?.identifier as string
|
||||
})}?redirectFrom=login`
|
||||
)
|
||||
}}
|
||||
intent={Intent.PRIMARY}>
|
||||
{getString('cde.details.startGitspace')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
history.push(
|
||||
routes.toCDEGitspaceDetail({
|
||||
space: currentRow?.space_path as string,
|
||||
gitspaceId: currentRow?.identifier as string
|
||||
})
|
||||
)
|
||||
}}
|
||||
icon="gitspace"
|
||||
variation={ButtonVariation.SECONDARY}>
|
||||
{getString('cde.viewGitspace')}
|
||||
</Button>
|
||||
</Layout.Horizontal>
|
||||
<Button
|
||||
variation={ButtonVariation.TERTIARY}
|
||||
onClick={() => {
|
||||
hideStartModal()
|
||||
setCurrentRow(undefined)
|
||||
}}>
|
||||
{getString('cancel')}
|
||||
</Button>
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
}
|
||||
onClose={hideStartModal}
|
||||
/>
|
||||
)
|
||||
}, [currentRow, history, routes])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentRow) {
|
||||
setTimeout(() => {
|
||||
handleStartGitspace()
|
||||
}, 100)
|
||||
}
|
||||
}, [currentRow])
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{data && (
|
||||
|
@ -456,17 +668,22 @@ export const ListGitspaces = ({ data, refreshList }: { data: TypesGitspaceConfig
|
|||
const pathparamsList = row?.instance?.space_path?.split('/') || []
|
||||
const projectIdentifier = pathparamsList[pathparamsList.length - 1] || ''
|
||||
|
||||
if (row?.instance?.state === GitspaceStatus.RUNNING) {
|
||||
if (row?.state === GitspaceStatus.RUNNING) {
|
||||
if (row?.ide === IDEType.VSCODE) {
|
||||
window.open(`vscode://harness-inc.gitspaces/${projectIdentifier}/${row?.instance?.id}`, '_blank')
|
||||
window.open(
|
||||
`vscode://harness-inc.gitspaces/${projectIdentifier}/${row?.instance?.identifier}`,
|
||||
'_blank'
|
||||
)
|
||||
} else {
|
||||
window.open(row?.instance.url, '_blank')
|
||||
window.open(row?.instance?.url || '', '_blank')
|
||||
}
|
||||
} else if (row?.state === GitspaceStatus.STOPPED) {
|
||||
setCurrentRow(row)
|
||||
} else {
|
||||
history.push(
|
||||
routes.toCDEGitspaceDetail({
|
||||
space: row?.instance?.space_path as string,
|
||||
gitspaceId: row?.instance?.id as string
|
||||
space: row?.space_path as string,
|
||||
gitspaceId: row?.identifier as string
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -489,7 +706,7 @@ export const ListGitspaces = ({ data, refreshList }: { data: TypesGitspaceConfig
|
|||
},
|
||||
{
|
||||
id: 'lastactivity',
|
||||
Header: getString('cde.sessionDuration'),
|
||||
Header: getString('cde.lastActivated'),
|
||||
Cell: RenderLastActivity
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,12 +1,37 @@
|
|||
.hideContainer {
|
||||
:global {
|
||||
--bp3-intent-color: unset !important;
|
||||
.bp3-form-helper-text {
|
||||
margin-top: unset !important;
|
||||
}
|
||||
.formFields {
|
||||
[class*='repoInput'],
|
||||
[class*='branchDropdown'] {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.repoAndBranch {
|
||||
width: 47% !important;
|
||||
.importForm {
|
||||
cursor: pointer;
|
||||
display: contents;
|
||||
font-size: var(--font-size-small) !important;
|
||||
}
|
||||
|
||||
.repoInput {
|
||||
span[icon='git-repo'],
|
||||
span[icon='git-branch'] {
|
||||
top: 20%;
|
||||
left: 2% !important;
|
||||
color: var(--grey-500) !important;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 50px !important;
|
||||
border-radius: 7px !important;
|
||||
border: 1px solid var(--grey-200) !important;
|
||||
box-shadow: none !important;
|
||||
background: var(--grey-100);
|
||||
padding-left: 10% !important;
|
||||
|
||||
&:active {
|
||||
background: white;
|
||||
}
|
||||
&:focus {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,5 +16,6 @@
|
|||
|
||||
/* eslint-disable */
|
||||
// This is an auto-generated file
|
||||
export declare const hideContainer: string
|
||||
export declare const repoAndBranch: string
|
||||
export declare const formFields: string
|
||||
export declare const importForm: string
|
||||
export declare const repoInput: string
|
||||
|
|
|
@ -1,125 +1,145 @@
|
|||
import { FormInput, FormikForm, Layout } from '@harnessio/uicore'
|
||||
import React, { useState } from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { get, debounce } from 'lodash-es'
|
||||
import cx from 'classnames'
|
||||
import { FormikForm, Layout, FormInput, Container, Text } from '@harnessio/uicore'
|
||||
import { useFormikContext } from 'formik'
|
||||
import { Color } from '@harnessio/design-system'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { Icon } from '@harnessio/icons'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { GitProviders, ImportFormData, getOrgLabel, getOrgPlaceholder, getProviders } from 'utils/GitUtils'
|
||||
import {
|
||||
getRepoIdFromURL,
|
||||
getRepoNameFromURL,
|
||||
isValidUrl
|
||||
} from 'cde/components/CreateGitspace/components/SelectRepository/SelectRepository.utils'
|
||||
import { BranchInput } from 'cde/components/CreateGitspace/components/BranchInput/BranchInput'
|
||||
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||
import NewRepoModalButton from 'components/NewRepoModalButton/NewRepoModalButton'
|
||||
import { RepoCreationType } from 'utils/GitUtils'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import { OpenapiCreateGitspaceRequest, useGitspacelookup } from 'cde-gitness/services'
|
||||
import css from './ThirdPartyRepoImportForm.module.scss'
|
||||
|
||||
export interface ThirdPartyRepoImportFormProps extends ImportFormData {
|
||||
branch: string
|
||||
ide: string
|
||||
id: string
|
||||
enum RepoCheckStatus {
|
||||
Valid = 'valid',
|
||||
InValid = 'InValid'
|
||||
}
|
||||
|
||||
export const ThirdPartyRepoImportForm = () => {
|
||||
const [auth, setAuth] = useState(false)
|
||||
const { getString } = useStrings()
|
||||
const { values, setFieldValue, validateField } = useFormikContext<ThirdPartyRepoImportFormProps>()
|
||||
const history = useHistory()
|
||||
const space = useGetSpaceParam()
|
||||
const { routes } = useAppContext()
|
||||
const { setValues, setFieldError } = useFormikContext<OpenapiCreateGitspaceRequest>()
|
||||
|
||||
const { mutate, loading } = useGitspacelookup({})
|
||||
|
||||
const [repoCheckState, setRepoCheckState] = useState<RepoCheckStatus | undefined>()
|
||||
|
||||
const onChange = useCallback(
|
||||
debounce(async (url: string) => {
|
||||
let errorMessage = ''
|
||||
try {
|
||||
if (isValidUrl(url)) {
|
||||
const response = (await mutate({ space_ref: space, url })) as {
|
||||
is_private?: boolean
|
||||
branch: string
|
||||
url: string
|
||||
}
|
||||
if (response?.is_private) {
|
||||
errorMessage = getString('cde.repository.privateRepoWarning')
|
||||
setRepoCheckState(RepoCheckStatus.InValid)
|
||||
} else {
|
||||
setValues((prvValues: any) => {
|
||||
return {
|
||||
...prvValues,
|
||||
code_repo_url: response.url,
|
||||
branch: response.branch,
|
||||
identifier: getRepoIdFromURL(response.url),
|
||||
name: getRepoNameFromURL(response.url)
|
||||
}
|
||||
})
|
||||
setRepoCheckState(RepoCheckStatus.Valid)
|
||||
}
|
||||
} else {
|
||||
if (url?.trim()?.length) {
|
||||
errorMessage = 'Invalid URL Format'
|
||||
setRepoCheckState(RepoCheckStatus.InValid)
|
||||
} else {
|
||||
if (repoCheckState) {
|
||||
setRepoCheckState(undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
errorMessage = get(err, 'message') || ''
|
||||
}
|
||||
setFieldError('code_repo_url', errorMessage)
|
||||
}, 1000),
|
||||
[repoCheckState]
|
||||
)
|
||||
|
||||
return (
|
||||
<FormikForm>
|
||||
<FormInput.Select name={'gitProvider'} label={getString('importSpace.gitProvider')} items={getProviders()} />
|
||||
{![GitProviders.GITHUB, GitProviders.GITLAB, GitProviders.BITBUCKET, GitProviders.AZURE].includes(
|
||||
values.gitProvider
|
||||
) && (
|
||||
<FormInput.Text
|
||||
className={css.hideContainer}
|
||||
name="hostUrl"
|
||||
label={getString('importRepo.url')}
|
||||
placeholder={getString('importRepo.urlPlaceholder')}
|
||||
tooltipProps={{
|
||||
dataTooltipId: 'repositoryURLTextField'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<FormInput.Text
|
||||
className={css.hideContainer}
|
||||
name="org"
|
||||
label={getString(getOrgLabel(values.gitProvider))}
|
||||
placeholder={getString(getOrgPlaceholder(values.gitProvider))}
|
||||
/>
|
||||
{values.gitProvider === GitProviders.AZURE && (
|
||||
<FormInput.Text
|
||||
className={css.hideContainer}
|
||||
name="project"
|
||||
label={getString('importRepo.project')}
|
||||
placeholder={getString('importRepo.projectPlaceholder')}
|
||||
/>
|
||||
)}
|
||||
<Layout.Horizontal spacing="medium" flex={{ justifyContent: 'space-between', alignItems: 'baseline' }}>
|
||||
<Layout.Vertical className={css.repoAndBranch} spacing="small">
|
||||
<FormInput.Text
|
||||
className={css.hideContainer}
|
||||
name="repo"
|
||||
label={getString('importRepo.repo')}
|
||||
placeholder={getString('importRepo.repoPlaceholder')}
|
||||
onChange={event => {
|
||||
const target = event.target as HTMLInputElement
|
||||
setFieldValue('repo', target.value)
|
||||
if (target.value) {
|
||||
setFieldValue('name', target.value)
|
||||
validateField('repo')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Layout.Vertical>
|
||||
<Layout.Vertical className={css.repoAndBranch} spacing="small">
|
||||
<FormInput.Text
|
||||
className={css.hideContainer}
|
||||
name="branch"
|
||||
label={getString('branch')}
|
||||
placeholder={getString('cde.create.branchPlaceholder')}
|
||||
onChange={event => {
|
||||
const target = event.target as HTMLInputElement
|
||||
setFieldValue('branch', target.value)
|
||||
}}
|
||||
/>
|
||||
</Layout.Vertical>
|
||||
</Layout.Horizontal>
|
||||
<Layout.Horizontal spacing="medium">
|
||||
<FormInput.CheckBox
|
||||
name="authorization"
|
||||
label={getString('importRepo.reqAuth')}
|
||||
tooltipProps={{
|
||||
dataTooltipId: 'authorization'
|
||||
}}
|
||||
onClick={() => {
|
||||
setAuth(!auth)
|
||||
}}
|
||||
style={auth ? {} : { margin: 0 }}
|
||||
/>
|
||||
</Layout.Horizontal>
|
||||
|
||||
{auth ? (
|
||||
<>
|
||||
{[GitProviders.BITBUCKET, GitProviders.AZURE].includes(values.gitProvider) && (
|
||||
<FormInput.Text
|
||||
name="username"
|
||||
label={getString('userName')}
|
||||
placeholder={getString('importRepo.userPlaceholder')}
|
||||
tooltipProps={{
|
||||
dataTooltipId: 'repositoryUserTextField'
|
||||
<Layout.Horizontal spacing="small">
|
||||
<Text
|
||||
icon="warning-icon"
|
||||
font={{ size: 'small' }}
|
||||
margin={{ bottom: 'medium' }}
|
||||
iconProps={{ size: 20, color: Color.ORANGE_500 }}
|
||||
background={Color.ORANGE_50}
|
||||
padding="small">
|
||||
{getString('cde.create.importWarning')}
|
||||
{
|
||||
<NewRepoModalButton
|
||||
space={space}
|
||||
repoCreationType={RepoCreationType.IMPORT}
|
||||
customRenderer={fn => (
|
||||
<Text className={css.importForm} color={Color.PRIMARY_7} onClick={fn}>
|
||||
{getString('cde.importInto')}
|
||||
</Text>
|
||||
)}
|
||||
modalTitle={getString('importGitRepo')}
|
||||
onSubmit={() => {
|
||||
history.push(routes.toCDEGitspacesCreate({ space }))
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
<Layout.Horizontal spacing="medium">
|
||||
<Container width="63%" className={css.formFields}>
|
||||
<FormInput.Text
|
||||
inputGroup={{ type: 'password' }}
|
||||
name="password"
|
||||
label={
|
||||
[GitProviders.BITBUCKET, GitProviders.AZURE].includes(values.gitProvider)
|
||||
? getString('importRepo.appPassword')
|
||||
: getString('importRepo.passToken')
|
||||
}
|
||||
placeholder={
|
||||
[GitProviders.BITBUCKET, GitProviders.AZURE].includes(values.gitProvider)
|
||||
? getString('importRepo.appPasswordPlaceholder')
|
||||
: getString('importRepo.passTokenPlaceholder')
|
||||
}
|
||||
tooltipProps={{
|
||||
dataTooltipId: 'repositoryPasswordTextField'
|
||||
name="code_repo_url"
|
||||
inputGroup={{
|
||||
leftIcon: 'git-repo',
|
||||
color: Color.GREY_500,
|
||||
rightElement: (
|
||||
<Container height={50} width={25} flex={{ alignItems: 'center' }}>
|
||||
{loading ? (
|
||||
<Icon name="loading" />
|
||||
) : repoCheckState ? (
|
||||
repoCheckState === RepoCheckStatus.Valid ? (
|
||||
<Icon name="tick-circle" color={Color.GREEN_450} />
|
||||
) : (
|
||||
<Icon name="warning-sign" color={Color.ERROR} />
|
||||
)
|
||||
) : undefined}
|
||||
</Container>
|
||||
)
|
||||
}}
|
||||
placeholder={getString('cde.repository.repositoryURL')}
|
||||
className={cx(css.repoInput)}
|
||||
onChange={async event => {
|
||||
const target = event.target as HTMLInputElement
|
||||
await onChange(target.value)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</Container>
|
||||
<Container width="35%" className={css.formFields}>
|
||||
<BranchInput />
|
||||
</Container>
|
||||
</Layout.Horizontal>
|
||||
</FormikForm>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export enum StandaloneIDEType {
|
||||
VSCODE = 'vs_code',
|
||||
VSCODEWEB = 'vs_code_web'
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
|
||||
export interface LogData {
|
||||
pos: number
|
||||
out: string
|
||||
time: number
|
||||
}
|
||||
|
||||
export function parseLog(log: string): LogData[] {
|
||||
const logLines = log.trim().split('\n\n')
|
||||
const parsedData: LogData[] = []
|
||||
|
||||
logLines.forEach(line => {
|
||||
const dataMatch = line.match(/data: (.+)/)
|
||||
|
||||
if (dataMatch && dataMatch[1] !== 'eof') {
|
||||
const eventData: LogData = JSON.parse(dataMatch[1])
|
||||
|
||||
parsedData.push(eventData)
|
||||
}
|
||||
})
|
||||
|
||||
return parsedData
|
||||
}
|
||||
|
||||
export const useGetLogStream = ({ response }: { response: any }) => {
|
||||
const [data, setData] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
const fetchStreamData = async () => {
|
||||
const reader = response?.body?.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let done = false
|
||||
|
||||
while (!done) {
|
||||
/* eslint-disable no-await-in-loop */
|
||||
const { value, done: streamDone } = (await reader?.read()) || {}
|
||||
done = streamDone
|
||||
const chunk = decoder.decode(value)
|
||||
setData(prevData => prevData + chunk)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (response && !response.body.locked) {
|
||||
fetchStreamData()
|
||||
}
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
}, [response])
|
||||
|
||||
return { data: parseLog(data) }
|
||||
}
|
|
@ -1,28 +1,10 @@
|
|||
import type { ThirdPartyRepoImportFormProps } from 'cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm'
|
||||
import type { EnumIDEType, OpenapiCreateGitspaceRequest } from 'cde-gitness/services'
|
||||
import { GitProviders } from 'utils/GitUtils'
|
||||
|
||||
export const gitnessFormInitialValues: OpenapiCreateGitspaceRequest = {
|
||||
branch: '',
|
||||
code_repo_url: '',
|
||||
devcontainer_path: '',
|
||||
id: '',
|
||||
ide: 'vsCode' as EnumIDEType,
|
||||
infra_provider_resource_id: 'default',
|
||||
identifier: '',
|
||||
ide: 'vs_code' as EnumIDEType,
|
||||
resource_identifier: 'default',
|
||||
name: ''
|
||||
}
|
||||
export const thirdPartyformInitialValues: ThirdPartyRepoImportFormProps = {
|
||||
gitProvider: GitProviders.GITHUB,
|
||||
hostUrl: '',
|
||||
org: '',
|
||||
project: '',
|
||||
repo: '',
|
||||
username: '',
|
||||
password: '',
|
||||
name: '',
|
||||
description: '',
|
||||
branch: '',
|
||||
ide: 'vsCode' as EnumIDEType,
|
||||
id: '',
|
||||
importPipelineLabel: false
|
||||
}
|
||||
|
|
|
@ -1,7 +1,21 @@
|
|||
.main {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin-top: 10% !important;
|
||||
|
||||
.titleContainer {
|
||||
width: 50%;
|
||||
margin-bottom: var(--spacing-medium) !important;
|
||||
|
||||
h2 {
|
||||
font-size: 24px !important;
|
||||
}
|
||||
p {
|
||||
font-size: 16px !important;
|
||||
color: #383946;
|
||||
}
|
||||
}
|
||||
|
||||
.cardMain {
|
||||
width: 50%;
|
||||
|
|
|
@ -24,3 +24,4 @@ export declare const formOuterContainer: string
|
|||
export declare const formTitleContainer: string
|
||||
export declare const main: string
|
||||
export declare const subContainers: string
|
||||
export declare const titleContainer: string
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
import React, { useState } from 'react'
|
||||
import {
|
||||
Breadcrumbs,
|
||||
Button,
|
||||
ButtonVariation,
|
||||
Card,
|
||||
Container,
|
||||
Formik,
|
||||
FormikForm,
|
||||
Heading,
|
||||
Layout,
|
||||
Page,
|
||||
Text,
|
||||
useToaster
|
||||
} from '@harnessio/uicore'
|
||||
import { FontVariation } from '@harnessio/design-system'
|
||||
import { Color, FontVariation } from '@harnessio/design-system'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import {
|
||||
ThirdPartyRepoImportForm,
|
||||
ThirdPartyRepoImportFormProps
|
||||
} from 'cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm'
|
||||
import { ThirdPartyRepoImportForm } from 'cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm'
|
||||
import { GitnessRepoImportForm } from 'cde-gitness/components/GitnessRepoImportForm/GitnessRepoImportForm'
|
||||
import { SelectIDE } from 'cde/components/CreateGitspace/components/SelectIDE/SelectIDE'
|
||||
import { useCreateGitspace, type OpenapiCreateGitspaceRequest } from 'cde-gitness/services'
|
||||
|
@ -25,8 +24,8 @@ import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
|||
import { getErrorMessage } from 'utils/Utils'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import RepositoryTypeButton, { RepositoryType } from '../../components/RepositoryTypeButton/RepositoryTypeButton'
|
||||
import { gitnessFormInitialValues, thirdPartyformInitialValues } from './GitspaceCreate.constants'
|
||||
import { handleImportSubmit, validateGitnessForm, validationSchemaStepOne } from './GitspaceCreate.utils'
|
||||
import { gitnessFormInitialValues } from './GitspaceCreate.constants'
|
||||
import { validateGitnessForm } from './GitspaceCreate.utils'
|
||||
import css from './GitspaceCreate.module.scss'
|
||||
|
||||
export const GitspaceCreate = () => {
|
||||
|
@ -40,38 +39,49 @@ export const GitspaceCreate = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Page.Header title={getString('cde.gitspaces')} />
|
||||
<Page.Header
|
||||
title={getString('cde.createGitspace')}
|
||||
breadcrumbs={
|
||||
<Breadcrumbs
|
||||
links={[
|
||||
{ url: routes.toCDEGitspaces({ space }), label: getString('cde.gitspaces') },
|
||||
{ url: routes.toCDEGitspacesCreate({ space }), label: getString('cde.createGitspace') }
|
||||
]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Page.Body className={css.main}>
|
||||
<Container className={css.titleContainer}>
|
||||
<Layout.Vertical spacing="small" margin={{ bottom: 'medium' }}>
|
||||
<Heading font={{ weight: 'bold' }} color={Color.BLACK} level={2}>
|
||||
{getString('cde.createGitspace')}
|
||||
</Heading>
|
||||
<Text font={{ size: 'medium' }}>{getString('cde.create.subtext')}</Text>
|
||||
</Layout.Vertical>
|
||||
</Container>
|
||||
<Card className={css.cardMain}>
|
||||
<Text className={css.cardTitle} font={{ variation: FontVariation.CARD_TITLE }}>
|
||||
{getString('cde.createGitspace')}
|
||||
</Text>
|
||||
<Container className={css.subContainers}>
|
||||
<Formik
|
||||
onSubmit={async data => {
|
||||
try {
|
||||
const payload =
|
||||
activeButton === RepositoryType.GITNESS
|
||||
? data
|
||||
: handleImportSubmit(data as ThirdPartyRepoImportFormProps)
|
||||
await mutate({ ...payload, space_ref: space } as OpenapiCreateGitspaceRequest & {
|
||||
const payload = data
|
||||
const response = await mutate({ ...payload, space_ref: space } as OpenapiCreateGitspaceRequest & {
|
||||
space_ref?: string
|
||||
})
|
||||
showSuccess(getString('cde.create.gitspaceCreateSuccess'))
|
||||
history.push(routes.toCDEGitspaces({ space }))
|
||||
history.push(
|
||||
`${routes.toCDEGitspaceDetail({
|
||||
space,
|
||||
gitspaceId: response.identifier || ''
|
||||
})}?redirectFrom=login`
|
||||
)
|
||||
} catch (error) {
|
||||
showError(getString('cde.create.gitspaceCreateFailed'))
|
||||
showError(getErrorMessage(error))
|
||||
}
|
||||
}}
|
||||
initialValues={
|
||||
activeButton === RepositoryType.GITNESS ? gitnessFormInitialValues : thirdPartyformInitialValues
|
||||
}
|
||||
validationSchema={
|
||||
activeButton === RepositoryType.GITNESS
|
||||
? validateGitnessForm(getString)
|
||||
: validationSchemaStepOne(getString)
|
||||
}
|
||||
initialValues={gitnessFormInitialValues}
|
||||
validationSchema={validateGitnessForm(getString)}
|
||||
formName="importRepoForm"
|
||||
enableReinitialize>
|
||||
{formik => {
|
||||
|
@ -105,7 +115,7 @@ export const GitspaceCreate = () => {
|
|||
)}
|
||||
</Container>
|
||||
<Container className={css.formOuterContainer}>
|
||||
<SelectIDE />
|
||||
<SelectIDE standalone />
|
||||
<Button width={'100%'} variation={ButtonVariation.PRIMARY} height={50} type="submit">
|
||||
{getString('cde.createGitspace')}
|
||||
</Button>
|
||||
|
|
|
@ -1,90 +1,12 @@
|
|||
import * as yup from 'yup'
|
||||
import { compact } from 'lodash-es'
|
||||
import type { UseStringsReturn } from 'framework/strings'
|
||||
import { GitProviders, getProviderTypeMapping } from 'utils/GitUtils'
|
||||
import type { ThirdPartyRepoImportFormProps } from 'cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm'
|
||||
|
||||
export const validateGitnessForm = (getString: UseStringsReturn['getString']) =>
|
||||
yup.object().shape({
|
||||
branch: yup.string().trim().required(getString('cde.branchValidationMessage')),
|
||||
code_repo_url: yup.string().trim().required(getString('cde.repoValidationMessage')),
|
||||
id: yup.string().trim().required(),
|
||||
identifier: yup.string().trim().required(),
|
||||
ide: yup.string().trim().required(),
|
||||
infra_provider_resource_id: yup.string().trim().required(getString('cde.machineValidationMessage')),
|
||||
resource_identifier: yup.string().trim().required(getString('cde.machineValidationMessage')),
|
||||
name: yup.string().trim().required()
|
||||
})
|
||||
|
||||
export const validationSchemaStepOne = (getString: UseStringsReturn['getString']) =>
|
||||
yup.object().shape({
|
||||
gitProvider: yup.string().required(),
|
||||
repo: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when('gitProvider', {
|
||||
is: gitProvider => [GitProviders.GITHUB, GitProviders.GITLAB, GitProviders.BITBUCKET].includes(gitProvider),
|
||||
then: yup.string().required(getString('importSpace.orgRequired'))
|
||||
}),
|
||||
branch: yup.string().trim().required(getString('cde.branchValidationMessage')),
|
||||
hostUrl: yup
|
||||
.string()
|
||||
// .matches(MATCH_REPOURL_REGEX, getString('importSpace.invalidUrl'))
|
||||
.when('gitProvider', {
|
||||
is: gitProvider =>
|
||||
![GitProviders.GITHUB, GitProviders.GITLAB, GitProviders.BITBUCKET, GitProviders.AZURE].includes(gitProvider),
|
||||
then: yup.string().required(getString('importRepo.required')),
|
||||
otherwise: yup.string().notRequired() // Optional based on your needs
|
||||
}),
|
||||
org: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when('gitProvider', {
|
||||
is: GitProviders.AZURE,
|
||||
then: yup.string().required(getString('importSpace.orgRequired'))
|
||||
}),
|
||||
project: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when('gitProvider', {
|
||||
is: GitProviders.AZURE,
|
||||
then: yup.string().required(getString('importSpace.spaceNameRequired'))
|
||||
}),
|
||||
name: yup.string().trim().required(getString('validation.nameIsRequired'))
|
||||
})
|
||||
|
||||
export const handleImportSubmit = (formData: ThirdPartyRepoImportFormProps) => {
|
||||
const type = getProviderTypeMapping(formData.gitProvider)
|
||||
|
||||
const provider = {
|
||||
type,
|
||||
username: formData.username,
|
||||
password: formData.password,
|
||||
host: ''
|
||||
}
|
||||
|
||||
if (
|
||||
![GitProviders.GITHUB, GitProviders.GITLAB, GitProviders.BITBUCKET, GitProviders.AZURE].includes(
|
||||
formData.gitProvider
|
||||
)
|
||||
) {
|
||||
provider.host = formData.hostUrl
|
||||
}
|
||||
|
||||
const importPayload = {
|
||||
name: formData.repo || formData.name,
|
||||
description: formData.description || '',
|
||||
id: formData.repo,
|
||||
provider,
|
||||
ide: formData.ide,
|
||||
branch: formData.branch,
|
||||
infra_provider_resource_id: 'default',
|
||||
provider_repo: compact([
|
||||
formData.org,
|
||||
formData.gitProvider === GitProviders.AZURE ? formData.project : '',
|
||||
formData.repo
|
||||
])
|
||||
.join('/')
|
||||
.replace(/\.git$/, '')
|
||||
}
|
||||
|
||||
return importPayload
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
.customSubheader {
|
||||
height: 10vh;
|
||||
}
|
||||
|
||||
.pageMain {
|
||||
margin: var(--spacing-xxlarge) !important;
|
||||
}
|
||||
|
||||
@media (min-width: 2000px) {
|
||||
.titleContainer {
|
||||
width: 80% !important;
|
||||
margin: 0 10% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
width: 95% !important;
|
||||
margin: 0 5% !important;
|
||||
}
|
||||
|
||||
.cardContainer {
|
||||
margin-top: var(--spacing-xxlarge) !important;
|
||||
width: 100% !important;
|
||||
|
||||
[data-testid='eventsCard-panel'] {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.popover {
|
||||
> div[class*='popover-arrow'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:global {
|
||||
a.bp3-menu-item:hover,
|
||||
.bp3-active {
|
||||
background: var(--primary-1) !important;
|
||||
color: var(--grey-1000) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accordionnCustomSummary {
|
||||
:global(.Accordion--chevron) {
|
||||
margin-left: auto;
|
||||
color: var(--grey-700);
|
||||
}
|
||||
|
||||
:global([class*='Accordion--panel']) {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
div[data-testid='eventsCard-summary'],
|
||||
div[data-testid='logsCard-summary'] {
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-end;
|
||||
margin: 0 var(--spacing-medium);
|
||||
width: 100%;
|
||||
|
||||
div:first-child {
|
||||
width: 97%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2023 Harness, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
// This is an auto-generated file
|
||||
export declare const accordionnCustomSummary: string
|
||||
export declare const cardContainer: string
|
||||
export declare const customSubheader: string
|
||||
export declare const pageMain: string
|
||||
export declare const popover: string
|
||||
export declare const titleContainer: string
|
|
@ -0,0 +1,347 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import {
|
||||
Breadcrumbs,
|
||||
Button,
|
||||
ButtonVariation,
|
||||
Card,
|
||||
Container,
|
||||
Layout,
|
||||
Accordion,
|
||||
Page,
|
||||
Text,
|
||||
useToaster
|
||||
} from '@harnessio/uicore'
|
||||
import { Play } from 'iconoir-react'
|
||||
import { useHistory, useParams } from 'react-router-dom'
|
||||
import { Color, FontVariation, PopoverProps } from '@harnessio/design-system'
|
||||
import { Menu, MenuItem, PopoverInteractionKind, PopoverPosition } from '@blueprintjs/core'
|
||||
import { useGet, useMutate } from 'restful-react'
|
||||
import { defaultTo } from 'lodash-es'
|
||||
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import EventTimelineAccordion from 'cde-gitness/components/EventTimelineAccordion/EventTimelineAccordion'
|
||||
import { DetailsCard } from 'cde-gitness/components/DetailsCard/DetailsCard'
|
||||
import type { TypesGitspaceConfig, TypesGitspaceEventResponse } from 'cde-gitness/services'
|
||||
import { GitspaceActionType, GitspaceStatus } from 'cde/constants'
|
||||
import { useQueryParams } from 'hooks/useQueryParams'
|
||||
import { useUpdateQueryParams } from 'hooks/useUpdateQueryParams'
|
||||
import { getErrorMessage } from 'utils/Utils'
|
||||
import { usePolling } from 'cde/components/GitspaceDetails/usePolling'
|
||||
import deleteIcon from 'cde-gitness/assests/delete.svg?url'
|
||||
import vscodeIcon from 'cde/icons/VSCode.svg?url'
|
||||
import pauseIcon from 'cde-gitness/assests/pause.svg?url'
|
||||
import { StandaloneIDEType } from 'cde-gitness/constants'
|
||||
import homeIcon from 'cde-gitness/assests/home.svg?url'
|
||||
import ContainerLogs from '../../components/ContainerLogs/ContainerLogs'
|
||||
import { useGetLogStream } from '../../hooks/useGetLogStream'
|
||||
import css from './GitspaceDetails.module.scss'
|
||||
|
||||
export const GitspaceDetails = () => {
|
||||
const space = useGetSpaceParam()
|
||||
const { getString } = useStrings()
|
||||
const { routes } = useAppContext()
|
||||
const { showError } = useToaster()
|
||||
const history = useHistory()
|
||||
const [startTriggred, setStartTriggred] = useState<boolean>(false)
|
||||
const { gitspaceId = '' } = useParams<{ gitspaceId?: string }>()
|
||||
|
||||
const [isStreamingLogs, setIsStreamingLogs] = useState(false)
|
||||
|
||||
const [startPolling, setStartPolling] = useState<GitspaceActionType | undefined>(undefined)
|
||||
|
||||
const { loading, data, refetch, error } = useGet<TypesGitspaceConfig>({
|
||||
path: `/api/v1/gitspaces/${space}/${gitspaceId}/+`,
|
||||
debounce: 500
|
||||
})
|
||||
|
||||
const { data: eventData, refetch: refetchEventData } = useGet<TypesGitspaceEventResponse[]>({
|
||||
path: `/api/v1/gitspaces/${space}/${gitspaceId}/+/events`,
|
||||
debounce: 500
|
||||
})
|
||||
|
||||
const { refetch: refetchLogsData, response } = useGet<any>({
|
||||
path: `api/v1/gitspaces/${space}/${gitspaceId}/+/logs/stream`,
|
||||
debounce: 500,
|
||||
lazy: true
|
||||
})
|
||||
|
||||
const { mutate: actionMutate, loading: mutateLoading } = useMutate({
|
||||
verb: 'POST',
|
||||
path: `/api/v1/gitspaces/${space}/${gitspaceId}/+/actions`
|
||||
})
|
||||
|
||||
const { mutate: deleteGitspace, loading: deleteLoading } = useMutate<any>({
|
||||
verb: 'DELETE',
|
||||
path: `/api/v1/gitspaces/${space}/${gitspaceId}/+`
|
||||
})
|
||||
|
||||
const { updateQueryParams } = useUpdateQueryParams<{ redirectFrom?: string }>()
|
||||
const { redirectFrom = '' } = useQueryParams<{ redirectFrom?: string }>()
|
||||
|
||||
const pollingCondition = [
|
||||
GitspaceStatus.RUNNING,
|
||||
GitspaceStatus.STOPPED,
|
||||
GitspaceStatus.ERROR,
|
||||
GitspaceStatus.UNINITIALIZED
|
||||
].includes(data?.state as GitspaceStatus)
|
||||
|
||||
const disabledActionButtons = [GitspaceStatus.STARTING, GitspaceStatus.STOPPING].includes(
|
||||
data?.state as GitspaceStatus
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const filteredEvent = eventData?.filter(
|
||||
item =>
|
||||
item.event === 'agent_gitspace_creation_start' &&
|
||||
defaultTo(item?.timestamp, 0) >= defaultTo(data?.instance?.updated, 0)
|
||||
)
|
||||
if (disabledActionButtons && filteredEvent?.length && !isStreamingLogs) {
|
||||
refetchLogsData()
|
||||
setIsStreamingLogs(true)
|
||||
} else if (filteredEvent?.length && !disabledActionButtons && isStreamingLogs) {
|
||||
setIsStreamingLogs(false)
|
||||
}
|
||||
}, [eventData, data?.instance?.updated, disabledActionButtons])
|
||||
|
||||
usePolling(
|
||||
async () => {
|
||||
await refetchEventData()
|
||||
await refetch()
|
||||
},
|
||||
{
|
||||
pollingInterval: 10000,
|
||||
startCondition: Boolean(startPolling) || !pollingCondition
|
||||
}
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const startTrigger = async () => {
|
||||
if (redirectFrom && !startTriggred && !mutateLoading) {
|
||||
try {
|
||||
setStartTriggred(true)
|
||||
await actionMutate({ action: GitspaceActionType.START })
|
||||
await refetch()
|
||||
updateQueryParams({ redirectFrom: undefined })
|
||||
} catch (err) {
|
||||
showError(getErrorMessage(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data?.state && data?.state !== GitspaceStatus.RUNNING && redirectFrom) {
|
||||
startTrigger()
|
||||
}
|
||||
}, [data?.state, redirectFrom, mutateLoading, startTriggred])
|
||||
|
||||
const formattedlogsdata = useGetLogStream({ response })
|
||||
|
||||
return (
|
||||
<>
|
||||
<Page.Header
|
||||
title=""
|
||||
breadcrumbs={
|
||||
<Breadcrumbs
|
||||
links={[
|
||||
{ url: routes.toCDEGitspaces({ space }), label: getString('cde.gitspaces') },
|
||||
{ url: routes.toCDEGitspaceDetail({ gitspaceId, space }), label: data?.name || 'Gitspace Name' }
|
||||
]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Page.SubHeader className={css.customSubheader}>
|
||||
<Layout.Horizontal width="100%" flex={{ alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Container>
|
||||
<Layout.Horizontal spacing="small">
|
||||
<img src={vscodeIcon} height={32} width={32} />
|
||||
<Text font={{ variation: FontVariation.H3 }}>{data?.name}</Text>
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
|
||||
<Container>
|
||||
<Layout.Horizontal spacing="large">
|
||||
<Button
|
||||
variation={ButtonVariation.SECONDARY}
|
||||
rightIcon="chevron-down"
|
||||
tooltipProps={
|
||||
{
|
||||
interactionKind: PopoverInteractionKind.CLICK,
|
||||
position: PopoverPosition.BOTTOM_LEFT,
|
||||
popoverClassName: css.popover
|
||||
} as PopoverProps
|
||||
}
|
||||
tooltip={
|
||||
<Menu>
|
||||
{!disabledActionButtons && (
|
||||
<MenuItem
|
||||
text={
|
||||
<Layout.Horizontal
|
||||
spacing="small"
|
||||
flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||
{data?.state === GitspaceStatus.RUNNING ? (
|
||||
<img src={pauseIcon} height={16} width={16} />
|
||||
) : (
|
||||
<Play />
|
||||
)}
|
||||
<Text>
|
||||
{data?.state === GitspaceStatus.RUNNING
|
||||
? mutateLoading
|
||||
? getString('cde.stopingGitspace')
|
||||
: getString('cde.details.stopGitspace')
|
||||
: mutateLoading
|
||||
? getString('cde.startingGitspace')
|
||||
: getString('cde.details.startGitspace')}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
disabled={loading || mutateLoading || disabledActionButtons}
|
||||
onClick={async () => {
|
||||
try {
|
||||
setStartPolling(GitspaceActionType.START)
|
||||
await actionMutate({ action: data?.state === GitspaceStatus.RUNNING ? 'stop' : 'start' })
|
||||
await refetch()
|
||||
setStartPolling(undefined)
|
||||
updateQueryParams({ redirectFrom: undefined })
|
||||
} catch (err) {
|
||||
showError(getErrorMessage(err))
|
||||
setStartPolling(undefined)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
text={
|
||||
<Layout.Horizontal
|
||||
spacing="small"
|
||||
flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||
<img src={homeIcon} height={16} width={16} />
|
||||
<Text>{getString('cde.details.goToDashboard')}</Text>
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
onClick={() => {
|
||||
history.push(routes.toCDEGitspaces({ space }))
|
||||
}}
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={deleteGitspace as Unknown as () => void}
|
||||
text={
|
||||
<Layout.Horizontal
|
||||
spacing="small"
|
||||
flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||
{deleteLoading ? <></> : <img src={deleteIcon} height={16} width={16} />}
|
||||
<Text color={Color.RED_450} icon={deleteLoading ? 'loading' : undefined}>
|
||||
{getString('cde.deleteGitspace')}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
disabled={disabledActionButtons}
|
||||
/>
|
||||
</Menu>
|
||||
}>
|
||||
{getString('cde.details.actions')}
|
||||
</Button>
|
||||
{(data?.state === GitspaceStatus.RUNNING ||
|
||||
data?.state === GitspaceStatus.STARTING ||
|
||||
data?.state === GitspaceStatus.STOPPING) &&
|
||||
data?.ide ? (
|
||||
<Button
|
||||
disabled={disabledActionButtons}
|
||||
variation={ButtonVariation.PRIMARY}
|
||||
tooltip={
|
||||
disabledActionButtons ? (
|
||||
<Container width={300} padding="medium">
|
||||
<Layout.Vertical spacing="small">
|
||||
<Text color={Color.WHITE} font="small">
|
||||
We are provisioning the Gitspace
|
||||
</Text>
|
||||
<Text color={Color.WHITE} font="small">
|
||||
Please wait for a few minutes before the VS Code Desktop can be launched
|
||||
</Text>
|
||||
</Layout.Vertical>
|
||||
</Container>
|
||||
) : undefined
|
||||
}
|
||||
tooltipProps={{ isDark: true, position: PopoverPosition.BOTTOM_LEFT }}
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
if (data?.ide === StandaloneIDEType.VSCODE) {
|
||||
window.open(`vscode://harness-inc.gitspaces/${data?.identifier}`, '_blank')
|
||||
} else {
|
||||
window.open(data?.instance?.url || '', '_blank')
|
||||
}
|
||||
}}>
|
||||
{data?.ide === StandaloneIDEType.VSCODE && getString('cde.details.openEditor')}
|
||||
{data?.ide === StandaloneIDEType.VSCODEWEB && getString('cde.details.openBrowser')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
loading={mutateLoading}
|
||||
disabled={mutateLoading || disabledActionButtons}
|
||||
icon="run-pipeline"
|
||||
variation={ButtonVariation.PRIMARY}
|
||||
intent="success"
|
||||
onClick={async () => {
|
||||
try {
|
||||
setStartPolling(GitspaceActionType.START)
|
||||
await actionMutate({ action: GitspaceActionType.START })
|
||||
await refetch()
|
||||
setStartPolling(undefined)
|
||||
updateQueryParams({ redirectFrom: undefined })
|
||||
} catch (err) {
|
||||
showError(getErrorMessage(err))
|
||||
setStartPolling(undefined)
|
||||
}
|
||||
}}>
|
||||
{getString('cde.details.startGitspace')}
|
||||
</Button>
|
||||
)}
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
</Layout.Horizontal>
|
||||
</Page.SubHeader>
|
||||
|
||||
<Page.Body
|
||||
loading={loading}
|
||||
error={getErrorMessage(error)}
|
||||
noData={{
|
||||
when: () => !data?.identifier,
|
||||
message: getString('cde.details.noData')
|
||||
}}
|
||||
className={css.pageMain}>
|
||||
<Container>
|
||||
<Card className={css.cardContainer}>
|
||||
<Text font={{ variation: FontVariation.CARD_TITLE }}>{getString('cde.gitspaceDetail')}</Text>
|
||||
<DetailsCard data={data} loading={mutateLoading} />
|
||||
</Card>
|
||||
<Card className={css.cardContainer}>
|
||||
<EventTimelineAccordion data={eventData} />
|
||||
</Card>
|
||||
|
||||
<Card className={css.cardContainer}>
|
||||
<Accordion activeId="logsCard">
|
||||
<Accordion.Panel
|
||||
shouldRender
|
||||
className={css.accordionnCustomSummary}
|
||||
summary={
|
||||
<Layout.Vertical spacing="small">
|
||||
<Text font={{ variation: FontVariation.CARD_TITLE }} margin={{ left: 'large' }}>
|
||||
{getString('cde.details.containerLogs')}
|
||||
</Text>
|
||||
<Text margin={{ left: 'large' }}>{getString('cde.details.containerLogsSubText')} </Text>
|
||||
</Layout.Vertical>
|
||||
}
|
||||
id="logsCard"
|
||||
details={
|
||||
<Container>
|
||||
<ContainerLogs data={formattedlogsdata.data} />
|
||||
</Container>
|
||||
}
|
||||
/>
|
||||
</Accordion>
|
||||
</Card>
|
||||
</Container>
|
||||
</Page.Body>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
.branchInput {
|
||||
span[icon='git-repo'],
|
||||
span[icon='git-branch'] {
|
||||
top: 20%;
|
||||
left: 2% !important;
|
||||
|
|
|
@ -22,18 +22,22 @@ import { GitspaceSelect } from 'cde/components/GitspaceSelect/GitspaceSelect'
|
|||
import { useStrings, type UseStringsReturn } from 'framework/strings'
|
||||
import { IDEType } from 'cde/constants'
|
||||
import type { OpenapiCreateGitspaceRequest } from 'services/cde'
|
||||
import { StandaloneIDEType } from 'cde-gitness/constants'
|
||||
import VSCode from '../../../../icons/VSCode.svg?url'
|
||||
|
||||
export const getIDESelectItems = (getString: UseStringsReturn['getString']) => [
|
||||
{ label: getString('cde.ide.desktop'), value: IDEType.VSCODE },
|
||||
{ label: getString('cde.ide.browser'), value: IDEType.VSCODEWEB }
|
||||
]
|
||||
export const getIDESelectItems = (getString: UseStringsReturn['getString'], standalone = false) => {
|
||||
const ideEnum = standalone ? StandaloneIDEType : IDEType
|
||||
return [
|
||||
{ label: getString('cde.ide.desktop'), value: ideEnum.VSCODE },
|
||||
{ label: getString('cde.ide.browser'), value: ideEnum.VSCODEWEB }
|
||||
]
|
||||
}
|
||||
|
||||
export const SelectIDE = () => {
|
||||
export const SelectIDE = ({ standalone = false }: { standalone?: boolean }) => {
|
||||
const { values, errors, setFieldValue: onChange } = useFormikContext<OpenapiCreateGitspaceRequest>()
|
||||
const { ide } = values
|
||||
const { getString } = useStrings()
|
||||
const IDESelectItems = getIDESelectItems(getString)
|
||||
const IDESelectItems = getIDESelectItems(getString, standalone)
|
||||
const IDELabel = IDESelectItems.find(item => item.value === ide)?.label
|
||||
return (
|
||||
<GitspaceSelect
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react'
|
||||
import { Bitbucket as BitbucketIcon, Code, GitLabFull, GithubCircle } from 'iconoir-react'
|
||||
import type { EnumCodeRepoType } from 'services/cde'
|
||||
import type { EnumGitspaceCodeRepoType } from 'cde-gitness/services'
|
||||
|
||||
export const isValidUrl = (url: string) => {
|
||||
const urlPattern = new RegExp(
|
||||
|
@ -37,17 +38,24 @@ export enum CodeRepoType {
|
|||
Unknown = 'unknown'
|
||||
}
|
||||
|
||||
export const getIconByRepoType = ({ repoType }: { repoType?: EnumCodeRepoType }): React.ReactNode => {
|
||||
export const getIconByRepoType = ({
|
||||
repoType,
|
||||
height = 40
|
||||
}: {
|
||||
repoType?: EnumCodeRepoType | EnumGitspaceCodeRepoType
|
||||
height?: number
|
||||
}): React.ReactNode => {
|
||||
switch (repoType) {
|
||||
case CodeRepoType.Github:
|
||||
return <GithubCircle height={40} />
|
||||
return <GithubCircle height={height} />
|
||||
case CodeRepoType.Gitlab:
|
||||
return <GitLabFull height={40} />
|
||||
return <GitLabFull height={height} />
|
||||
case CodeRepoType.Bitbucket:
|
||||
return <BitbucketIcon height={40} />
|
||||
return <BitbucketIcon height={height} />
|
||||
default:
|
||||
case CodeRepoType.Unknown:
|
||||
case CodeRepoType.HarnessCode:
|
||||
return <Code height={40} />
|
||||
case 'harness_code':
|
||||
return <Code height={height} />
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,10 @@ export enum GitspaceStatus {
|
|||
RUNNING = 'running',
|
||||
STOPPED = 'stopped',
|
||||
UNKNOWN = 'unknown',
|
||||
ERROR = 'error'
|
||||
ERROR = 'error',
|
||||
STARTING = 'starting',
|
||||
STOPPING = 'stopping',
|
||||
UNINITIALIZED = 'uninitialized'
|
||||
}
|
||||
|
||||
export enum IDEType {
|
||||
|
|
|
@ -49,7 +49,7 @@ import { useGet, useMutate } from 'restful-react'
|
|||
import { Render } from 'react-jsx-match'
|
||||
import { compact, get } from 'lodash-es'
|
||||
import { useModalHook } from 'hooks/useModalHook'
|
||||
import { String, useStrings } from 'framework/strings'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import {
|
||||
DEFAULT_BRANCH_NAME,
|
||||
getErrorMessage,
|
||||
|
@ -75,6 +75,7 @@ import type {
|
|||
OpenapiCreateRepositoryRequest
|
||||
} from 'services/code'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import type { TypesRepository } from 'cde-gitness/services'
|
||||
import ImportForm from './ImportForm/ImportForm'
|
||||
import ImportReposForm from './ImportReposForm/ImportReposForm'
|
||||
import Private from '../../icons/private.svg?url'
|
||||
|
@ -95,9 +96,9 @@ export interface NewRepoModalButtonProps extends Omit<ButtonProps, 'onClick' | '
|
|||
modalTitle: string
|
||||
submitButtonTitle?: string
|
||||
cancelButtonTitle?: string
|
||||
onSubmit: (data: RepoRepositoryOutput & SpaceImportRepositoriesOutput) => void
|
||||
newRepoModalOnly?: boolean
|
||||
notFoundRepoName?: string
|
||||
onSubmit: (data: TypesRepository & RepoRepositoryOutput & SpaceImportRepositoriesOutput) => void
|
||||
repoCreationType?: RepoCreationType
|
||||
customRenderer?: (onChange: (event: any) => void) => React.ReactNode
|
||||
}
|
||||
|
||||
export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
||||
|
@ -481,17 +482,15 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
|||
[space]
|
||||
)
|
||||
|
||||
return props?.newRepoModalOnly ? (
|
||||
<MenuItem
|
||||
icon="plus"
|
||||
text={<String stringID="cde.create.repoNotFound" vars={{ repo: props?.notFoundRepoName }} useRichText />}
|
||||
onClick={e => {
|
||||
return props?.repoCreationType ? (
|
||||
<>
|
||||
{props?.customRenderer?.(e => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setRepoOption(repoCreateOptions[0])
|
||||
setRepoOption(repoCreateOptions.find(option => option.type === props?.repoCreationType) || repoCreateOptions[0])
|
||||
setTimeout(() => openModal(), 0)
|
||||
}}
|
||||
/>
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<SplitButton
|
||||
{...props}
|
||||
|
|
|
@ -147,24 +147,30 @@ export interface StringsMap {
|
|||
'cde.create.gitnessRepositories': string
|
||||
'cde.create.gitspaceCreateFailed': string
|
||||
'cde.create.gitspaceCreateSuccess': string
|
||||
'cde.create.importWarning': string
|
||||
'cde.create.repoNotFound': string
|
||||
'cde.create.repositoryDetails': string
|
||||
'cde.create.searchBranchPlaceholder': string
|
||||
'cde.create.searchRepositoryPlaceholder': string
|
||||
'cde.create.selectBranchPlaceholder': string
|
||||
'cde.create.subtext': string
|
||||
'cde.create.thirdPartyGitRepositories': string
|
||||
'cde.create.unsaved.message': string
|
||||
'cde.create.unsaved.title': string
|
||||
'cde.createGitspace': string
|
||||
'cde.createImport': string
|
||||
'cde.createRepo': string
|
||||
'cde.deleteGitspace': string
|
||||
'cde.deleteGitspaceText': string
|
||||
'cde.deleteGitspaceTitle': string
|
||||
'cde.deleteSuccess': string
|
||||
'cde.details.actions': string
|
||||
'cde.details.containerLogs': string
|
||||
'cde.details.containerLogsSubText': string
|
||||
'cde.details.fetchingDetails': string
|
||||
'cde.details.fetchingGitspace': string
|
||||
'cde.details.fetchingLogs': string
|
||||
'cde.details.gitspaceActivity': string
|
||||
'cde.details.gitspaceRunning': string
|
||||
'cde.details.gitspaceStopped': string
|
||||
'cde.details.goToDashboard': string
|
||||
|
@ -181,6 +187,7 @@ export interface StringsMap {
|
|||
'cde.disk': string
|
||||
'cde.editGitspace': string
|
||||
'cde.eventTimeline': string
|
||||
'cde.getStarted': string
|
||||
'cde.gitspaceDetail': string
|
||||
'cde.gitspaceUpdateSuccess': string
|
||||
'cde.gitspaces': string
|
||||
|
@ -192,12 +199,17 @@ export interface StringsMap {
|
|||
'cde.ide.selectIDE': string
|
||||
'cde.ide.title': string
|
||||
'cde.ide.vsCode': string
|
||||
'cde.importInto': string
|
||||
'cde.introText1': string
|
||||
'cde.introText2': string
|
||||
'cde.introText3': string
|
||||
'cde.lastActivated': string
|
||||
'cde.lastUsed': string
|
||||
'cde.listing.error': string
|
||||
'cde.listing.offline': string
|
||||
'cde.listing.online': string
|
||||
'cde.listing.starting': string
|
||||
'cde.listing.stopping': string
|
||||
'cde.logs': string
|
||||
'cde.machine': string
|
||||
'cde.machineValidationMessage': string
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
declare const __DEV__: boolean
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
declare const __ENABLE_GITSPACE__: boolean
|
||||
|
||||
declare module '*.png' {
|
||||
const value: string
|
||||
export default value
|
||||
|
|
|
@ -1119,6 +1119,7 @@ checklist: Check list
|
|||
code: Code
|
||||
cde:
|
||||
branchPlaceholder: Branch
|
||||
lastUsed: Last Used
|
||||
introText1: Get ready to code in seconds
|
||||
introText2: Isolated cloud development environments with your favorite editor
|
||||
introText3: Connect in seconds with a repository
|
||||
|
@ -1126,7 +1127,7 @@ cde:
|
|||
createGitspace: Create Gitspace
|
||||
deleteGitspace: Delete Gitspace
|
||||
editGitspace: Edit Gitspace
|
||||
viewGitspace: View Gitspace Detail
|
||||
viewGitspace: View Gitspace Details
|
||||
updateGitspace: Update Gitspace
|
||||
gitspaces: Gitspaces
|
||||
noRepo: Don’t have a Git repo?
|
||||
|
@ -1158,19 +1159,25 @@ cde:
|
|||
startingGitspace: Starting Gitspace
|
||||
stopingGitspace: Stopping Gitspace
|
||||
sessionDuration: Last Started
|
||||
lastActivated: LAST ACTIVATED
|
||||
deleteGitspaceTitle: Delete Gitspace
|
||||
deleteGitspaceText: "Are you sure to delete the gitspace, '{{name}}'?"
|
||||
deleteSuccess: Gitspace deleted succesfully
|
||||
repositoryAndBranch: Repository & Branch
|
||||
importInto: Import Repository into Gitness
|
||||
status: Status
|
||||
getStarted: Get started with Gitspaces by
|
||||
createImport: creating or importing repositories
|
||||
listing:
|
||||
online: Online
|
||||
offline: Offline
|
||||
online: Active
|
||||
offline: Stopped
|
||||
error: Error
|
||||
starting: Starting...
|
||||
stopping: Stopping...
|
||||
create:
|
||||
repositoryDetails: Repository Details
|
||||
gitnessRepositories: Gitness Repositories
|
||||
thirdPartyGitRepositories: Third Party Git Repositories
|
||||
thirdPartyGitRepositories: Other Public Git Repositories
|
||||
branchPlaceholder: Enter the Branch name
|
||||
selectBranchPlaceholder: Select branch
|
||||
searchRepositoryPlaceholder: Search for a repository
|
||||
|
@ -1178,6 +1185,10 @@ cde:
|
|||
repoNotFound: 'Create a repo <strong>{{repo}}</strong> in Gitness'
|
||||
gitspaceCreateSuccess: Gitspace created successfully
|
||||
gitspaceCreateFailed: Gitspace creation failed
|
||||
subtext: Start Coding, no setup required. Dedicated cloud development environments with your favorite editor
|
||||
importWarning:
|
||||
Currently we support only third party public repositories. In order to use a private git repository, please
|
||||
import the repository into Gitness.
|
||||
unsaved:
|
||||
title: Unsaved Changes
|
||||
message: 'You have unsaved changes, On switching all unsaved changes will be lost'
|
||||
|
@ -1189,6 +1200,7 @@ cde:
|
|||
startGitspace: Start Gitspace
|
||||
stopProvising: Stop Provising
|
||||
noData: No Data for Gitspace
|
||||
gitspaceActivity: Gitspace Activity
|
||||
provisioningGitspace: Provisioning the Gitspace...
|
||||
gitspaceStopped: Gitspace is stopped
|
||||
gitspaceRunning: Gitspace is running
|
||||
|
@ -1198,7 +1210,9 @@ cde:
|
|||
logsFailed: Gitspace logs api failed
|
||||
noLogsFound: No gitspace logs found
|
||||
wrongIdentifier: Invalid gitspace id or project params
|
||||
fetchingDetails: Fetching Gitspace Details ....
|
||||
fetchingDetails: Fetching Gitspace Events ....
|
||||
containerLogs: Container Logs
|
||||
containerLogsSubText: Logs showing activity inside container
|
||||
repository:
|
||||
repo: Repository
|
||||
selectRepository: Select Repository
|
||||
|
|
|
@ -172,6 +172,17 @@ export const DefaultMenu: React.FC = () => {
|
|||
</Container>
|
||||
</Render>
|
||||
|
||||
{__ENABLE_GITSPACE__ && (
|
||||
<Render when={selectedSpace}>
|
||||
<NavMenuItem
|
||||
className=""
|
||||
label={getString('cde.gitspaces')}
|
||||
to={routes.toCDEGitspaces({ space: selectedSpace?.path as string })}
|
||||
icon="gitspace"
|
||||
/>
|
||||
</Render>
|
||||
)}
|
||||
|
||||
<Render when={!standalone && selectedSpace}>
|
||||
<NavMenuItem
|
||||
icon="thinner-search"
|
||||
|
|
|
@ -1649,10 +1649,10 @@
|
|||
resolved "https://registry.npmjs.org/@harnessio/design-system/-/design-system-2.1.1.tgz#2da3036602ed9b9446d8139c72009e6dc1e25642"
|
||||
integrity sha512-ZwAGM1srOZ49/6YkwyjkczUv4v91CN0rCecRYnV3/g+xdSV5ycrUvkJjl9nHub6jw2eCGC0GdyNgAtAJnLmGfQ==
|
||||
|
||||
"@harnessio/icons@^2.1.3":
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@harnessio/icons/-/icons-2.1.3.tgz#abe777ff94491dd1647693fc18e74f956c42fd52"
|
||||
integrity sha512-hlYwjlS8sv46SvXyydnykNu0ZM89+xyGp3MAuOfdKLgxOn1wMMu/88VwP7tPhHmCRhkGhn7xAPY/JLkYT24pgg==
|
||||
"@harnessio/icons@^2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@harnessio/icons/-/icons-2.1.5.tgz#07ad9417e79a2fef6ccc79df7f21758c2a85feb9"
|
||||
integrity sha512-fFVXoPXnEXAM6lwfT8r7qhR265Pn79u99i/0EijbA0CgxPvtrHaIldcDoKBCk963VFXNIdjqJps405TzHwJSeA==
|
||||
|
||||
"@harnessio/uicore@^4.1.2":
|
||||
version "4.1.2"
|
||||
|
|