Sync UI template with latest updates

This commit is contained in:
Tan Nhu 2022-08-18 11:44:33 -07:00
parent 66fea2a730
commit 7c6da77a27
34 changed files with 318 additions and 1000 deletions

View File

@ -35,6 +35,12 @@ settings:
typescript:
alwaysTryTypes: true
rules:
'@typescript-eslint/ban-types':
- error
- extendDefaults: true
types:
'{}': false
# custom rules
no-document-body-snapshot: 2
duplicate-data-tooltip-id: 'warn'

View File

@ -1,48 +1,44 @@
const packageJSON = require('../package.json');
const { pick, omit, mapValues } = require('lodash');
const packageJSON = require('../package.json')
const { pick, omit, mapValues } = require('lodash')
/**
* These packages must be stricly shared with exact versions
*/
const ExactSharedPackages = [
'react',
'react-dom',
'react-router-dom',
'@harness/use-modal',
'@blueprintjs/core',
'@blueprintjs/select',
'@blueprintjs/datetime',
'restful-react',
'@harness/monaco-yaml',
'monaco-editor',
'monaco-editor-core',
'monaco-languages',
'monaco-plugin-helpers',
'react-monaco-editor'
]
const ExactSharedPackages = [
'react',
'react-dom',
'react-router-dom',
'@harness/use-modal',
'@blueprintjs/core',
'@blueprintjs/select',
'@blueprintjs/datetime',
'restful-react',
'@harness/monaco-yaml',
'monaco-editor',
'monaco-editor-core',
'monaco-languages',
'monaco-plugin-helpers',
'react-monaco-editor'
]
/**
* @type {import('webpack').ModuleFederationPluginOptions}
*/
module.exports = {
name: 'governance',
filename: 'remoteEntry.js',
library: {
type: 'var',
name: 'governance'
},
exposes: {
'./App': './src/App.tsx',
'./EvaluationModal': './src/modals/EvaluationModal/EvaluationModal.tsx',
'./PipelineGovernanceView': './src/views/PipelineGovernanceView/PipelineGovernanceView.tsx',
'./EvaluationView': './src/views/EvaluationView/EvaluationView.tsx',
'./PolicySetWizard': './src/pages/PolicySets/components/PolicySetWizard.tsx'
},
shared: {
formik: packageJSON.dependencies['formik'],
...mapValues(pick(packageJSON.dependencies, ExactSharedPackages), version => ({
singleton: true,
requiredVersion: version
}))
}
};
name: 'governance',
filename: 'remoteEntry.js',
library: {
type: 'var',
name: 'governance'
},
exposes: {
'./App': './src/App.tsx'
},
shared: {
formik: packageJSON.dependencies['formik'],
...mapValues(pick(packageJSON.dependencies, ExactSharedPackages), version => ({
singleton: true,
requiredVersion: version
}))
}
}

View File

@ -1,8 +1,8 @@
describe('dashboard', () => {
it('load the dashboard', () => {
cy.visit('/')
cy.contains('In Effect')
cy.contains('Policy Evaluations')
cy.contains('Failures Recorded')
})
it('load the dashboard', () => {
// cy.visit('/')
// cy.contains('In Effect')
// cy.contains('Policy Evaluations')
// cy.contains('Failures Recorded')
})
})

View File

@ -1,9 +0,0 @@
// disabling at the moment because of logic around Evaluations tab being removed in standalone mode (account in NG equivalent)
// describe('evaluations', () => {
// it('load the table', () => {
// cy.visit('/')
// cy.contains('Evaluations').click()
// cy.contains('Policy evaluations are created when policy sets are enforced on your Harness entities.')
// })
// })

View File

@ -1,7 +0,0 @@
describe('policies', () => {
it('load the table', () => {
cy.visit('/')
cy.contains('Policies').click()
cy.get('[class="TableV2--row TableV2--card TableV2--clickable"]').should('have.length', 12)
})
})

View File

@ -1,7 +0,0 @@
describe('policy sets', () => {
it('load the table', () => {
cy.visit('/')
cy.contains('Policy Set').click()
cy.contains('A harness policy set allows you to group policies and configure where they will be enforced.')
})
})

View File

@ -141,8 +141,8 @@
"@types/testing-library__user-event": "^4.1.1",
"@types/uuid": "^8.3.0",
"@types/yup": "^0.29.0",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"@typescript-eslint/eslint-plugin": "^5.33.1",
"@typescript-eslint/parser": "^5.33.1",
"@urql/devtools": "^2.0.3",
"@zerollup/ts-transform-paths": "^1.7.18",
"assert": "^2.0.0",
@ -199,7 +199,7 @@
"ts-loader": "^9.2.6",
"ts-node": "^10.2.1",
"tsconfig-paths-webpack-plugin": "^3.5.1",
"typescript": "^4.2.4",
"typescript": "^4.7.4",
"url-loader": "^4.1.1",
"webpack": "^5.58.0",
"webpack-bugsnag-plugins": "^1.8.0",

View File

@ -13,7 +13,10 @@ const AppContext = React.createContext<AppContextProps>({
components: {}
})
export const AppContextProvider: React.FC<{ value: AppProps }> = React.memo(({ value: initialValue, children }) => {
export const AppContextProvider: React.FC<{ value: AppProps }> = React.memo(function AppContextProvider({
value: initialValue,
children
}) {
const [appStates, setAppStates] = useState<AppProps>(initialValue)
return (

View File

@ -1,7 +1,6 @@
import type React from 'react'
import type * as History from 'history'
import type { PermissionOptionsMenuButtonProps } from 'components/Permissions/PermissionsOptionsMenuButton'
import type { OverviewChartsWithToggleProps } from 'components/OverviewChartsWithToggle/OverviewChartsWithToggle'
import type { LangLocale } from './framework/strings/languageLoader'
import type { FeatureFlagMap, GitFiltersProps } from './utils/GovernanceUtils'
@ -106,5 +105,4 @@ export interface AppPropsComponent {
navigate: (path: string) => void
shouldBlockNavigation?: (location: History.Location) => boolean
}>
OverviewChartsWithToggle: React.FC<OverviewChartsWithToggleProps>
}

View File

@ -3,12 +3,14 @@ import type { AppPathProps } from 'AppProps'
export enum RoutePath {
SIGNIN = '/signin',
SIGNUP = '/signup',
REGISTER = '/register',
POLICY_DASHBOARD = '/dashboard',
POLICY_LISTING = '/policies',
POLICY_NEW = '/policies/new',
POLICY_VIEW = '/policies/view/:policyIdentifier',
//POLICY_EDIT = '/policies/edit/:policyIdentifier',
POLICY_EDIT= '/policies/edit/:policyIdentifier/:repo?/:branch?',
POLICY_EDIT = '/policies/edit/:policyIdentifier/:repo?/:branch?',
POLICY_SETS_LISTING = '/policy-sets',
POLICY_SETS_DETAIL = '/policy-sets/:policySetIdentifier',
POLICY_EVALUATIONS_LISTING = '/policy-evaluations',
@ -17,10 +19,12 @@ export enum RoutePath {
export default {
toSignIn: (): string => toRouteURL(RoutePath.SIGNIN),
toSignUp: (): string => toRouteURL(RoutePath.SIGNUP),
toRegister: (): string => toRouteURL(RoutePath.REGISTER),
toPolicyDashboard: (): string => toRouteURL(RoutePath.POLICY_DASHBOARD),
toPolicyListing: (): string => toRouteURL(RoutePath.POLICY_LISTING),
toPolicyNew: (): string => toRouteURL(RoutePath.POLICY_NEW),
toPolicyView: ({ policyIdentifier }: Required<Pick<AppPathProps, 'policyIdentifier'>>): string =>
toPolicyView: ({ policyIdentifier }: Required<Pick<AppPathProps, 'policyIdentifier'>>): string =>
toRouteURL(RoutePath.POLICY_VIEW, { policyIdentifier }),
toPolicyEdit: ({ policyIdentifier }: Required<Pick<AppPathProps, 'policyIdentifier'>>): string =>
toRouteURL(RoutePath.POLICY_EDIT, { policyIdentifier }),

View File

@ -1,105 +1,76 @@
/* eslint-disable react/display-name */
import React, { useCallback } from 'react'
import { HashRouter, Route, Switch, Redirect } from 'react-router-dom'
import { SignInPage } from 'pages/signin/SignInPage'
// import { SignInPage } from 'pages/signin/SignInPage'
import { NotFoundPage } from 'pages/404/NotFoundPage'
import { SignIn } from 'pages/SignIn/SignIn'
import { Register } from 'pages/Register/Register'
import { routePath, standaloneRoutePath } from './RouteUtils'
import { RoutePath } from './RouteDefinitions'
import PolicyControlPage from './pages/PolicyControl/PolicyControlPage'
import Policies from './pages/Policies/Policies'
import PolicyDashboard from './pages/PolicyDashboard/PolicyDashboard'
import PolicySets from './pages/PolicySets/PolicySets'
import PolicyEvaluations from './pages/PolicyEvaluations/PolicyEvaluations'
import { EditPolicy } from './pages/EditPolicy/EditPolicy'
import { ViewPolicy } from './pages/ViewPolicy/ViewPolicy'
import { PolicySetDetail } from './pages/PolicySetDetail/PolicySetDetail'
import { EvaluationDetail } from './pages/EvaluationDetail/EvaluationDetail'
export const RouteDestinations: React.FC<{ standalone: boolean }> = React.memo(
({ standalone }) => {
// TODO: Add Auth wrapper
const Destinations: React.FC = useCallback(
() => (
<Switch>
{standalone && (
export const RouteDestinations: React.FC<{ standalone: boolean }> = React.memo(({ standalone }) => {
const Destinations: React.FC = useCallback(
() => (
<Switch>
{standalone && (
<>
<Route path={routePath(RoutePath.SIGNIN)}>
<SignInPage />
<SignIn />
</Route>
)}
<Route path={routePath(RoutePath.SIGNUP)}>
<SignIn />
</Route>
<Route path={routePath(RoutePath.REGISTER)}>
<Register />
</Route>
</>
)}
<Route path={routePath(RoutePath.POLICY_DASHBOARD)}>
<PolicyControlPage titleKey="overview">
<PolicyDashboard />
</PolicyControlPage>
</Route>
<Route path={routePath(RoutePath.POLICY_DASHBOARD)}>
<h1>Overview</h1>
</Route>
<Route path={routePath(RoutePath.POLICY_NEW)}>
<PolicyControlPage titleKey="common.policy.newPolicy">
<EditPolicy />
</PolicyControlPage>
</Route>
<Route path={routePath(RoutePath.POLICY_NEW)}>
<h1>New</h1>
</Route>
<Route path={routePath(RoutePath.POLICY_VIEW)}>
<PolicyControlPage titleKey="governance.viewPolicy">
<ViewPolicy />
</PolicyControlPage>
</Route>
<Route path={routePath(RoutePath.POLICY_VIEW)}>
<h1>View</h1>
</Route>
<Route exact path={routePath(RoutePath.POLICY_EDIT)}>
<PolicyControlPage titleKey="governance.editPolicy">
<EditPolicy />
</PolicyControlPage>
</Route>
<Route exact path={routePath(RoutePath.POLICY_EDIT)}>
<h1>Edit</h1>
</Route>
<Route path={routePath(RoutePath.POLICY_LISTING)}>
<PolicyControlPage titleKey="common.policies">
<Policies />
</PolicyControlPage>
</Route>
<Route path={routePath(RoutePath.POLICY_LISTING)}>
<h1>Listing</h1>
</Route>
<Route exact path={routePath(RoutePath.POLICY_SETS_LISTING)}>
<PolicyControlPage titleKey="common.policy.policysets">
<PolicySets />
</PolicyControlPage>
</Route>
<Route exact path={routePath(RoutePath.POLICY_SETS_LISTING)}>
<h1>Listing 2</h1>
</Route>
<Route path={routePath(RoutePath.POLICY_SETS_DETAIL)}>
<PolicyControlPage titleKey="common.policy.policysets">
<PolicySetDetail />
</PolicyControlPage>
</Route>
<Route path={routePath(RoutePath.POLICY_SETS_DETAIL)}>
<h1>Detail 1</h1>
</Route>
<Route path={routePath(RoutePath.POLICY_EVALUATION_DETAIL)}>
<PolicyControlPage titleKey="governance.evaluations">
<EvaluationDetail />
</PolicyControlPage>
</Route>
<Route path={routePath(RoutePath.POLICY_EVALUATION_DETAIL)}>
<h1>Detail 2</h1>
</Route>
<Route path={routePath(RoutePath.POLICY_EVALUATIONS_LISTING)}>
<PolicyControlPage titleKey="governance.evaluations">
<PolicyEvaluations />
</PolicyControlPage>
</Route>
<Route path="/">
{standalone ? <Redirect to={standaloneRoutePath(RoutePath.POLICY_DASHBOARD)} /> : <NotFoundPage />}
</Route>
</Switch>
),
[standalone]
)
<Route path="/">
{standalone ? (
<Redirect to={standaloneRoutePath(RoutePath.POLICY_DASHBOARD)} />
) : (
<NotFoundPage />
)}
</Route>
</Switch>
),
[standalone]
)
return standalone ? (
<HashRouter>
<Destinations />
</HashRouter>
) : (
return standalone ? (
<HashRouter>
<Destinations />
)
}
)
</HashRouter>
) : (
<Destinations />
)
})

View File

@ -6,7 +6,7 @@ import './App.scss'
// This flag is used in services/config.ts to customize API path when app is run
// in multiple modes (standalone vs. embedded).
// Also being used in when generating proper URLs inside the app.
window.STRIP_PM_PREFIX = true
window.STRIP_SCM_PREFIX = true
ReactDOM.render(
<App

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react'
import { Classes, Menu } from '@blueprintjs/core'
import { Classes, IMenuItemProps, Menu } from '@blueprintjs/core'
import { Button, ButtonProps } from '@harness/uicore'
import type { PopoverProps } from '@harness/uicore/dist/components/Popover/Popover'
@ -23,7 +23,7 @@ export const OptionsMenuButton = ({ items, ...props }: OptionsMenuButtonProps):
<Menu.Item
key={(item as React.ComponentProps<typeof Menu.Item>)?.text as string}
className={Classes.POPOVER_DISMISS}
{...item}
{...(item as IMenuItemProps & React.AnchorHTMLAttributes<HTMLAnchorElement>)}
/>
)
)}

View File

@ -1,7 +1,6 @@
import React from 'react'
import mustache from 'mustache'
import { get } from 'lodash-es'
import { useStringsContext, StringKeys } from './StringsContext'
export interface UseStringsReturn {
@ -46,9 +45,9 @@ export function String(props: StringProps): React.ReactElement | null {
const text = getString(stringID, vars)
return useRichText ? (
<Tag {...(rest as unknown)} dangerouslySetInnerHTML={{ __html: text }} />
<Tag {...(rest as unknown as {})} dangerouslySetInnerHTML={{ __html: text }} />
) : (
<Tag {...(rest as unknown)}>{text}</Tag>
<Tag {...(rest as unknown as {})}>{text}</Tag>
)
} catch (e) {
if (process.env.NODE_ENV !== 'production') {

View File

@ -56,10 +56,12 @@ export interface StringsMap {
descriptionPlaceholder: string
details: string
edit: string
email: string
entity: string
'evaluation.evaluatedPoliciesCount': string
'evaluation.onePolicyEvaluated': string
executionsText: string
existingAccount: string
failed: string
fileOverwrite: string
finish: string
@ -122,13 +124,18 @@ export interface StringsMap {
navigationCheckText: string
navigationCheckTitle: string
no: string
noAccount: string
noSearchResultsFound: string
optionalField: string
outputLabel: string
overview: string
pageNotFound: string
password: string
samplePolicies: string
saveOverwrite: string
search: string
signIn: string
signUp: string
source: string
status: string
success: string

2
web/src/global.d.ts vendored
View File

@ -45,7 +45,7 @@ declare module '*.gql' {
declare interface Window {
apiUrl: string
bugsnagClient?: any
STRIP_PM_PREFIX?: boolean
STRIP_SCM_PREFIX?: boolean
}
declare const monaco: any

View File

@ -1,3 +1,10 @@
pageNotFound: Page Not Found
signIn: Sign In
signUp: Sign Up
email: Email
password: Password
noAccount: No Account
existingAccount: Existing Account
failed: Failed
status: Status
success: Success

View File

@ -1,15 +0,0 @@
/* eslint-disable */
/**
* Copyright 2021 Harness Inc. All rights reserved.
* Use of this source code is governed by the PolyForm Shield 1.0.0 license
* that can be found in the licenses directory at the root of this repository, also available at
* https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt.
**/
// this is an auto-generated file, do not update this manually
declare const styles: {
readonly container: string
readonly input: string
readonly minWidth: string
readonly root: string
}
export default styles

View File

@ -1,153 +0,0 @@
import React, { useState, useEffect } from 'react'
import { useHistory } from 'react-router-dom'
import {
Container,
Button,
Formik,
FormikForm,
FormInput,
Text,
Color,
Layout,
ButtonVariation,
Page,
useToaster
} from '@harness/uicore'
import { useUpdateUser, useGetUser } from 'services/pm'
import { useStrings } from 'framework/strings'
import routes from 'RouteDefinitions'
import styles from './Account.module.scss'
interface UserFormProps {
name: string
email: string
password1: string
password2: string
}
export const Account = () => {
const history = useHistory()
const { getString } = useStrings()
const { showSuccess, showError } = useToaster()
const { data, loading, error, refetch } = useGetUser({})
const { mutate } = useUpdateUser({})
const [editDetails, setEditDetails] = useState(false)
const [name, setName] = useState<string | undefined>('')
const [email, setEmail] = useState<string | undefined>('')
useEffect(() => {
setName(data?.name)
setEmail(data?.email)
}, [data])
if (error) {
history.push(routes.toLogin())
}
const updateUserDetails = async ({ email, name, password1 }: UserFormProps) => {
try {
await mutate({ email, name, password: password1 })
showSuccess(getString('common.itemUpdated'))
refetch()
} catch (err) {
showError(`Error: ${err}`)
console.error({ err })
}
}
const handleSubmit = (data: UserFormProps): void => {
setEditDetails(false)
updateUserDetails(data)
}
const editUserForm = (
<Formik<UserFormProps>
initialValues={{ name: name as string, email: email as string, password1: '', password2: '' }}
formName="newPipelineForm"
onSubmit={handleSubmit}>
<FormikForm>
<Layout.Horizontal flex={{ alignItems: 'center', justifyContent: 'flex-start' }} margin={{ bottom: 'large' }}>
<Text color={Color.GREY_600} className={styles.minWidth}>
{getString('common.name')}
</Text>
<FormInput.Text name="name" className={styles.input} />
</Layout.Horizontal>
<Layout.Horizontal flex={{ alignItems: 'center', justifyContent: 'flex-start' }} margin={{ bottom: 'large' }}>
<Text color={Color.GREY_600} className={styles.minWidth}>
{getString('common.email')}
</Text>
<FormInput.Text name="email" className={styles.input} />
</Layout.Horizontal>
<Layout.Horizontal flex={{ alignItems: 'center', justifyContent: 'flex-start' }} margin={{ bottom: 'large' }}>
<Text color={Color.GREY_600} className={styles.minWidth}>
{getString('password')}
</Text>
<FormInput.Text
name="password1"
label="Password"
inputGroup={{ type: 'password' }}
className={styles.input}
/>
<FormInput.Text
name="password2"
label="Re-type your Password"
inputGroup={{ type: 'password' }}
className={styles.input}
/>
</Layout.Horizontal>
<Button variation={ButtonVariation.LINK} icon="updated" text={getString('common.save')} type="submit" />
</FormikForm>
</Formik>
)
return (
<Container className={styles.root} height="inherit">
<Page.Header title={getString('common.accountOverview')} />
<Page.Body
loading={loading}
retryOnError={() => refetch()}
error={(error?.data as Error)?.message || error?.message}>
<Container margin="xlarge" padding="xlarge" className={styles.container} background="white">
<Text color={Color.BLACK} font={{ weight: 'semi-bold', size: 'medium' }} margin={{ bottom: 'xlarge' }}>
{getString('common.accountDetails')}
</Text>
{editDetails ? (
editUserForm
) : (
<>
<Layout.Horizontal
flex={{ alignItems: 'center', justifyContent: 'flex-start' }}
margin={{ bottom: 'large' }}>
<Text color={Color.GREY_600} className={styles.minWidth}>
{getString('common.name')}
</Text>
<Text color={Color.GREY_800}>{name}</Text>
</Layout.Horizontal>
<Layout.Horizontal
flex={{ alignItems: 'center', justifyContent: 'flex-start' }}
margin={{ bottom: 'large' }}>
<Text className={styles.minWidth}>{getString('common.email')}</Text>
<Text color={Color.GREY_800}>{email}</Text>
</Layout.Horizontal>
<Layout.Horizontal
flex={{ alignItems: 'center', justifyContent: 'flex-start' }}
margin={{ bottom: 'large' }}>
<Text className={styles.minWidth}>{getString('password')}</Text>
<Text padding={{ right: 'small' }} color={Color.GREY_800}>
*********
</Text>
</Layout.Horizontal>
<Button
variation={ButtonVariation.LINK}
icon="Edit"
text={getString('common.edit')}
onClick={() => setEditDetails(true)}
/>
</>
)}
</Container>
</Page.Body>
</Container>
)
}

View File

@ -1,17 +0,0 @@
.root {
display: flex;
flex-direction: column;
flex: 1 auto;
}
.container {
box-shadow: var(--card-shadow);
}
.minWidth {
min-width: 400px;
}
.input {
margin-bottom: unset !important;
}

View File

@ -1,100 +0,0 @@
import React, { useState, useEffect } from 'react'
import { useParams, useHistory } from 'react-router-dom'
import { startCase, camelCase } from 'lodash'
import { useToaster, useConfirmationDialog, Text, Color } from '@harness/uicore'
import { Intent } from '@blueprintjs/core'
import { useStrings } from 'framework/strings'
import { useGetExecution, useDeleteExecution, useUpdateExecution } from 'services/pm'
import { Settings } from '../../components/Settings/Settings'
import routes from 'RouteDefinitions'
interface PathProps {
pipeline: string
execution: string
}
interface ExecutionProps {
name?: string
desc?: string
}
export const ExecutionSettings = () => {
const history = useHistory()
const { getString } = useStrings()
const { showError, showSuccess } = useToaster()
const { pipeline, execution } = useParams<PathProps>()
const [name, setName] = useState<string | undefined>('')
const [desc, setDesc] = useState<string | undefined>('')
const { data, loading, error, refetch } = useGetExecution({ pipeline, execution })
const { mutate: deleteExecution } = useDeleteExecution({ pipeline })
const { mutate: updateExecution } = useUpdateExecution({ pipeline, execution })
const title = `${startCase(camelCase(data?.name!.replace(/-/g, ' ')))} Settings`
useEffect(() => {
if (data) {
setName(data.name)
setDesc(data.desc)
}
}, [data])
const handleUpdate = async ({ name, desc }: ExecutionProps) => {
try {
await updateExecution({ name, desc })
showSuccess(getString('common.itemUpdated'))
refetch()
} catch (err) {
showError(`Error: ${err}`)
console.error(err)
}
}
const handleDelete = async () => {
try {
await deleteExecution(execution)
history.push(routes.toPipeline({ pipeline }))
} catch (err) {
showError(`Error: ${err}`)
console.error(err)
}
}
const { openDialog } = useConfirmationDialog({
titleText: getString('common.delete'),
contentText: <Text color={Color.GREY_800}>Are you sure you want to delete this?</Text>,
confirmButtonText: getString('common.delete'),
cancelButtonText: getString('common.cancel'),
intent: Intent.DANGER,
buttonIntent: Intent.DANGER,
onCloseDialog: async (isConfirmed: boolean) => {
if (isConfirmed) {
try {
await handleDelete()
showSuccess(getString('common.itemDeleted'))
refetch()
} catch (err) {
showError(`Error: ${JSON.stringify(err)}`)
console.error({ err })
}
}
}
})
const handleSubmit = (data: ExecutionProps): void => {
handleUpdate(data)
}
return (
<Settings
name={name}
desc={desc}
handleDelete={openDialog}
loading={loading}
handleSubmit={handleSubmit}
refetch={refetch}
title={title}
error={error}
/>
)
}

View File

@ -1,30 +0,0 @@
.root {
display: flex;
flex-direction: column;
flex: 1 auto;
.filterTab {
text-align: center;
padding: 21px;
border-bottom: 3px solid transparent;
&.selected {
border-bottom-color: var(--primary-7);
}
&:hover {
text-decoration: none;
}
}
.header {
padding: var(--spacing-large) var(--spacing-xlarge) !important;
border-bottom: 1px solid var(--grey-200);
background: var(--white) !important;
.headerLayout {
align-items: center;
justify-content: flex-end;
}
}
}

View File

@ -1,16 +0,0 @@
/* eslint-disable */
/**
* Copyright 2021 Harness Inc. All rights reserved.
* Use of this source code is governed by the PolyForm Shield 1.0.0 license
* that can be found in the licenses directory at the root of this repository, also available at
* https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt.
**/
// this is an auto-generated file, do not update this manually
declare const styles: {
readonly filterTab: string
readonly header: string
readonly headerLayout: string
readonly root: string
readonly selected: string
}
export default styles

View File

@ -1,118 +0,0 @@
import React from 'react'
import { useParams, useHistory } from 'react-router-dom'
import {
Button,
ButtonVariation,
Container,
Layout,
Page,
useModalHook,
Formik,
FormikForm,
FormInput,
useToaster
} from '@harness/uicore'
import { Dialog } from '@blueprintjs/core'
import { useListExecutions, useCreateExecution, useDeleteExecution } from 'services/pm'
import { startCase, camelCase } from 'lodash'
import { useStrings } from 'framework/strings'
import Table from '../../components/Table/Table'
import routes from 'RouteDefinitions'
import styles from './Executions.module.scss'
export interface ExecutionsParams {
pipeline: string
}
interface ExecutionForm {
name: string
desc: string
}
export const Executions: React.FC = () => {
const history = useHistory()
const { getString } = useStrings()
const { showSuccess, showError } = useToaster()
const { pipeline } = useParams<ExecutionsParams>()
const { mutate: deleteExecution } = useDeleteExecution({ pipeline: pipeline })
const { mutate: createExecution } = useCreateExecution({ pipeline })
const { data: executionList, loading, error, refetch } = useListExecutions({ pipeline })
const title = `${startCase(camelCase(pipeline.replace(/-/g, ' ')))} ${getString('executions')}`
const handleCreate = async ({ name, desc }: ExecutionForm) => {
try {
await createExecution({ name, desc })
showSuccess(getString('common.itemCreated'))
refetch()
} catch (err) {
showError(`Error: ${err}`)
console.error({ error })
}
}
const modalProps = {
isOpen: true,
usePortal: true,
autoFocus: true,
canEscapeKeyClose: true,
canOutsideClickClose: true,
enforceFocus: true,
title: getString('addExecution'),
style: { width: 400, height: 300 }
}
const handleSubmit = (data: ExecutionForm): void => {
handleCreate(data)
hideModal()
}
const onRowClick = (execution: string) => {
history.push(routes.toPipelineExecutionSettings({ pipeline, execution }))
}
const onSettingsClick = (execution: string) => {
history.push(routes.toPipelineExecutionSettings({ pipeline, execution }))
}
const [openModal, hideModal] = useModalHook(() => (
<Dialog onClose={hideModal} {...modalProps}>
<Container margin={{ top: 'large' }} flex={{ alignItems: 'center', justifyContent: 'space-around' }}>
<Formik<ExecutionForm>
initialValues={{ name: '', desc: '' }}
formName="newExecutionForm"
onSubmit={handleSubmit}>
<FormikForm>
<FormInput.Text name="name" label={getString('common.name')} />
<FormInput.Text name="desc" label={getString('common.description')} />
<Button type="submit" intent="primary" width="100%">
Create
</Button>
</FormikForm>
</Formik>
</Container>
</Dialog>
))
return (
<Container className={styles.root} height="inherit">
<Page.Header title={title} />
<Layout.Horizontal spacing="large" className={styles.header}>
<Button variation={ButtonVariation.PRIMARY} text="New Execution" icon="plus" onClick={openModal} />
<div style={{ flex: 1 }} />
</Layout.Horizontal>
<Page.Body
loading={loading}
retryOnError={() => refetch()}
error={(error?.data as Error)?.message || error?.message}>
<Table
onRowClick={onRowClick}
refetch={refetch}
data={executionList}
onDelete={deleteExecution}
onSettingsClick={onSettingsClick}
/>
</Page.Body>
</Container>
)
}

View File

@ -12,7 +12,7 @@ import {
Layout,
useToaster
} from '@harness/uicore'
import { get } from 'lodash'
import { get } from 'lodash-es'
import { useAPIToken } from 'hooks/useAPIToken'
import { useOnLogin, useOnRegister } from 'services/pm'
import { useStrings } from 'framework/strings'
@ -43,23 +43,21 @@ export const Login: React.FC = () => {
if (pathname === '/login') {
mutate(formData as unknown as void)
.then(data => {
setToken(get(data, 'access_token' as string))
history.replace(routes.toPipelines())
.then(_data => {
setToken(get(_data, 'access_token' as string))
history.replace(routes.toPolicyDashboard())
})
.catch(error => {
showError(`Error: ${error}`)
console.error({ error })
})
} else {
mutateRegister(formData as unknown as void)
.then(data => {
setToken(get(data, 'access_token' as string))
history.replace(routes.toPipelines())
.then(_data => {
setToken(get(_data, 'access_token' as string))
history.replace(routes.toPolicyDashboard())
})
.catch(error => {
showError(`Error: ${error}`)
console.error({ error })
})
}
}
@ -76,7 +74,7 @@ export const Login: React.FC = () => {
<HarnessLogo height={25} />
</Container>
<Text font={{ size: 'large', weight: 'bold' }} color={Color.BLACK}>
{pathname === '/login' ? getString('signin') : getString('signUp')}
{pathname === '/login' ? getString('signIn') : getString('signUp')}
</Text>
<Text font={{ size: 'medium' }} color={Color.BLACK} margin={{ top: 'xsmall' }}>
and get ship done.
@ -88,10 +86,10 @@ export const Login: React.FC = () => {
formName="loginPageForm"
onSubmit={handleSubmit}>
<FormikForm>
<FormInput.Text name="email" label={getString('common.email')} />
<FormInput.Text name="email" label={getString('email')} />
<FormInput.Text name="password" label={getString('password')} inputGroup={{ type: 'password' }} />
<Button type="submit" intent="primary" width="100%">
{pathname === '/login' ? getString('signin') : getString('signUp')}
{pathname === '/login' ? getString('signIn') : getString('signUp')}
</Button>
</FormikForm>
</Formik>
@ -99,8 +97,8 @@ export const Login: React.FC = () => {
<Layout.Horizontal margin={{ top: 'xxxlarge' }} spacing="xsmall">
<Text>{pathname === '/login' ? getString('noAccount') : getString('existingAccount')}</Text>
<Link to={pathname === '/login' ? routes.toRegister() : routes.toLogin()}>
{pathname === '/login' ? getString('signUp') : getString('signin')}
<Link to={pathname === '/login' ? routes.toRegister() : routes.toSignIn()}>
{pathname === '/login' ? getString('signUp') : getString('signIn')}
</Link>
</Layout.Horizontal>
</div>

View File

@ -1,99 +0,0 @@
import React, { useState, useEffect } from 'react'
import { useParams, useHistory } from 'react-router-dom'
import { startCase, camelCase } from 'lodash'
import { useToaster, useConfirmationDialog, Text, Color } from '@harness/uicore'
import { Intent } from '@blueprintjs/core'
import { useStrings } from 'framework/strings'
import { useGetPipeline, useUpdatePipeline, useDeletePipeline } from 'services/pm'
import { Settings } from '../../components/Settings/Settings'
import routes from 'RouteDefinitions'
interface PathProps {
pipeline: string
}
interface PipelineProps {
name?: string
desc?: string
}
export const PipelineSettings = () => {
const history = useHistory()
const { getString } = useStrings()
const { showError, showSuccess } = useToaster()
const { pipeline } = useParams<PathProps>()
const [name, setName] = useState<string | undefined>('')
const [desc, setDesc] = useState<string | undefined>('')
const { data, loading, error, refetch } = useGetPipeline({ pipeline })
const { mutate: updatePipeline } = useUpdatePipeline({ pipeline })
const { mutate: deletePipeline } = useDeletePipeline({})
const title = `${startCase(camelCase(data?.name!.replace(/-/g, ' ')))} Settings`
useEffect(() => {
if (data) {
setName(data.name)
setDesc(data.desc)
}
}, [data])
const handleUpdate = async ({ name, desc }: PipelineProps) => {
try {
await updatePipeline({ name, desc })
showSuccess(getString('common.itemUpdated'))
refetch()
} catch (err) {
showError(`Error: ${err}`)
console.error(err)
}
}
const handleDelete = async () => {
try {
await deletePipeline(pipeline)
history.push(routes.toPipelines())
} catch (err) {
showError(`Error: ${err}`)
console.error(err)
}
}
const { openDialog } = useConfirmationDialog({
titleText: getString('common.delete'),
contentText: <Text color={Color.GREY_800}>Are you sure you want to delete this?</Text>,
confirmButtonText: getString('common.delete'),
cancelButtonText: getString('common.cancel'),
intent: Intent.DANGER,
buttonIntent: Intent.DANGER,
onCloseDialog: async (isConfirmed: boolean) => {
if (isConfirmed) {
try {
await handleDelete()
showSuccess(getString('common.itemDeleted'))
refetch()
} catch (err) {
showError(`Error: ${JSON.stringify(err)}`)
console.error({ err })
}
}
}
})
const handleSubmit = (data: PipelineProps): void => {
handleUpdate(data)
}
return (
<Settings
name={name}
desc={desc}
handleDelete={openDialog}
loading={loading}
handleSubmit={handleSubmit}
refetch={refetch}
title={title}
error={error}
/>
)
}

View File

@ -1,16 +0,0 @@
/* eslint-disable */
/**
* Copyright 2021 Harness Inc. All rights reserved.
* Use of this source code is governed by the PolyForm Shield 1.0.0 license
* that can be found in the licenses directory at the root of this repository, also available at
* https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt.
**/
// this is an auto-generated file, do not update this manually
declare const styles: {
readonly filterTab: string
readonly header: string
readonly headerLayout: string
readonly root: string
readonly selected: string
}
export default styles

View File

@ -1,112 +0,0 @@
import React from 'react'
import { useHistory } from 'react-router-dom'
import {
Button,
ButtonVariation,
Container,
Layout,
Page,
useModalHook,
Formik,
FormikForm,
FormInput,
useToaster
} from '@harness/uicore'
import { Dialog } from '@blueprintjs/core'
import { useListPipelines, useCreatePipeline, useDeletePipeline } from 'services/pm'
import { useStrings } from 'framework/strings'
import Table from '../../components/Table/Table'
import routes from 'RouteDefinitions'
import styles from './Pipelines.module.scss'
interface PipelineForm {
name: string
desc: string
}
export const Home: React.FC = () => {
const { getString } = useStrings()
const history = useHistory()
const { showError, showSuccess } = useToaster()
const { mutate: createPipeline } = useCreatePipeline({})
const { mutate: deletePipeline } = useDeletePipeline({})
const { data: pipelineList, loading, error, refetch } = useListPipelines({})
const modalProps = {
isOpen: true,
usePortal: true,
autoFocus: true,
canEscapeKeyClose: true,
canOutsideClickClose: true,
enforceFocus: true,
title: 'Add New Pipeline',
style: { width: 400, height: 300 }
}
const onRowClick = (pipeline: string) => {
history.push(routes.toPipeline({ pipeline }))
}
const onSettingsClick = (pipeline: string) => {
history.push(routes.toPipelineSettings({ pipeline }))
}
const [openModal, hideModal] = useModalHook(() => (
<Dialog onClose={hideModal} {...modalProps}>
<Container margin={{ top: 'large' }} flex={{ alignItems: 'center', justifyContent: 'space-around' }}>
<Formik<PipelineForm> initialValues={{ name: '', desc: '' }} formName="newPipelineForm" onSubmit={handleSubmit}>
<FormikForm>
<FormInput.Text name="name" label={getString('common.name')} />
<FormInput.Text name="desc" label={getString('common.description')} />
<Button type="submit" intent="primary" width="100%">
Create
</Button>
</FormikForm>
</Formik>
</Container>
</Dialog>
))
const handleCreate = async (data: PipelineForm) => {
const { name, desc } = data
try {
await createPipeline({ name, desc })
showSuccess(getString('common.itemCreated'))
refetch()
} catch (err) {
showError(`Error: ${err}`)
console.error({ err })
}
}
const handleSubmit = (data: PipelineForm): void => {
handleCreate(data)
hideModal()
}
if (error) {
history.push(routes.toLogin())
}
return (
<Container className={styles.root} height="inherit">
<Page.Header title={getString('pipelines')} />
<Layout.Horizontal spacing="large" className={styles.header}>
<Button variation={ButtonVariation.PRIMARY} text="New Pipeline" icon="plus" onClick={openModal} />
<div style={{ flex: 1 }} />
</Layout.Horizontal>
<Page.Body
loading={loading}
retryOnError={() => refetch()}
error={(error?.data as Error)?.message || error?.message}>
<Table
onRowClick={onRowClick}
refetch={refetch}
data={pipelineList}
onDelete={deletePipeline}
onSettingsClick={onSettingsClick}
/>
</Page.Body>
</Container>
)
}

View File

@ -1,30 +0,0 @@
.root {
display: flex;
flex-direction: column;
flex: 1 auto;
.filterTab {
text-align: center;
padding: 21px;
border-bottom: 3px solid transparent;
&.selected {
border-bottom-color: var(--primary-7);
}
&:hover {
text-decoration: none;
}
}
.header {
padding: var(--spacing-large) var(--spacing-xlarge) !important;
border-bottom: 1px solid var(--grey-200);
background: var(--white) !important;
.headerLayout {
align-items: center;
justify-content: flex-end;
}
}
}

View File

@ -1,94 +1,91 @@
import React, { useRef, useState, useCallback } from "react";
import React, { useRef, useState, useCallback } from 'react'
import { useHistory } from 'react-router-dom'
import styles from "./Register.module.scss";
import { useOnRegister } from 'services/pm'
import routes from 'RouteDefinitions'
import Link from "../../components/Link/Link";
import Input from "../../components/Input/input";
import Button from "../../components/Button/button";
import logo from "../../logo.svg"
import Link from '../../components/Link/Link'
import Input from '../../components/Input/input'
import Button from '../../components/Button/button'
import logo from '../../logo.svg'
import styles from './Register.module.scss'
// Renders the Register page.
export const Register = () => {
const history = useHistory()
const [error, setError] = useState(null);
const [fullname, setFullname] = useState('')
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const { mutate } = useOnRegister({})
const history = useHistory()
const [error, setError] = useState(null)
const [fullname, setFullname] = useState('')
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const { mutate } = useOnRegister({})
const handleRegister = useCallback(() => {
const formData = new FormData()
formData.append("fullname", fullname);
formData.append("password", password);
formData.append("username", username);
mutate(formData)
.then(() => {
history.replace(routes.toLogin())
})
.catch(error => {
// TODO: Use toaster to show error
// eslint-disable-next-line no-console
console.error({ error })
setError(error);
})
}, [mutate, username, password, fullname, history])
const handleRegister = useCallback(() => {
const formData = new FormData()
const alert =
error && error.message ? (
<div class="alert">{error.message}</div>
) : undefined;
formData.append('fullname', fullname)
formData.append('password', password)
formData.append('username', username)
return (
<div className={styles.root}>
<div className={styles.logo}>
<img src={logo} />
</div>
<h2>Sign up for a new account</h2>
{alert}
<div className={styles.field}>
<label>Full Name</label>
<Input
type="text"
name="fullname"
placeholder="Full Name"
className={styles.input}
onChange={e => setFullname(e.target.value)}
/>
</div>
<div className={styles.field}>
<label>Email</label>
<Input
type="text"
name="username"
placeholder="Email"
className={styles.input}
onChange={e => setUsername(e.target.value)}
/>
</div>
<div className={styles.field}>
<label>Password</label>
<Input
type="password"
name="password"
placeholder="Password"
className={styles.input}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div>
<Button onClick={handleRegister} className={styles.submit}>
Sign Up
</Button>
</div>
<div className={styles.actions}>
<span>
Already have an account? <Link href="/login">Sign In</Link>
</span>
</div>
</div>
);
}
mutate(formData)
.then(() => {
history.replace(routes.toLogin())
})
.catch(error => {
// TODO: Use toaster to show error
// eslint-disable-next-line no-console
console.error({ error })
setError(error)
})
}, [mutate, username, password, fullname, history])
const alert = error && error.message ? <div class="alert">{error.message}</div> : undefined
return (
<div className={styles.root}>
<div className={styles.logo}>
<img src={logo} />
</div>
<h2>Sign up for a new account</h2>
{alert}
<div className={styles.field}>
<label>Full Name</label>
<Input
type="text"
name="fullname"
placeholder="Full Name"
className={styles.input}
onChange={e => setFullname(e.target.value)}
/>
</div>
<div className={styles.field}>
<label>Email</label>
<Input
type="text"
name="username"
placeholder="Email"
className={styles.input}
onChange={e => setUsername(e.target.value)}
/>
</div>
<div className={styles.field}>
<label>Password</label>
<Input
type="password"
name="password"
placeholder="Password"
className={styles.input}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div>
<Button onClick={handleRegister} className={styles.submit}>
Sign Up
</Button>
</div>
<div className={styles.actions}>
<span>
Already have an account? <Link href="/login">Sign In</Link>
</span>
</div>
</div>
)
}

View File

@ -34,7 +34,7 @@ export const SignIn: React.FC = () => {
return (
<Layout.Vertical>
<h1>{getString('signin')}</h1>
<h1>{getString('signIn')}</h1>
<Container>
<Layout.Horizontal>
<Text>Username</Text>
@ -55,7 +55,7 @@ export const SignIn: React.FC = () => {
</Layout.Horizontal>
</Container>
<Container>
<Button text={getString('signin')} onClick={() => onLogin()} />
<Button text={getString('signIn')} onClick={() => onLogin()} />
</Container>
</Layout.Vertical>
)

View File

@ -5,7 +5,7 @@ export const getConfig = (str: string): string => {
// NOTE: Replace /^pm\// with your service prefixes when running in standalone mode
// I.e: 'pm/api/v1' -> 'api/v1' (standalone)
// -> 'pm/api/v1' (embedded inside Harness platform)
if (window.APP_RUN_IN_STANDALONE_MODE) {
if (window.STRIP_SCM_PREFIX) {
str = str.replace(/^pm\//, '')
}

View File

@ -7,7 +7,7 @@ import { queryByAttribute } from '@testing-library/react'
import { compile } from 'path-to-regexp'
import { createMemoryHistory } from 'history'
import { Router, Route, Switch, useLocation, useHistory } from 'react-router-dom'
import { ModalProvider } from '@harness/uicore'
import { ModalProvider } from '@harness/use-modal'
import qs from 'qs'
import { enableMapSet } from 'immer'
import { StringsContext } from 'framework/strings'

View File

@ -4276,21 +4276,22 @@
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.14.tgz#754f1dccedcc66fc2bbe290c27f5323b407ceb00"
integrity sha512-Ynb/CjHhE/Xp/4bhHmQC4U1Ox+I2OpfRYF3dnNgQqn1cHa6LK3H1wJMNPT02tSVZA6FYuXE2ITORfbnb6zBCSA==
"@typescript-eslint/eslint-plugin@^4.22.0":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276"
integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==
"@typescript-eslint/eslint-plugin@^5.33.1":
version "5.33.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.1.tgz#c0a480d05211660221eda963cc844732fe9b1714"
integrity sha512-S1iZIxrTvKkU3+m63YUOxYPKaP+yWDQrdhxTglVDVEVBf+aCSw85+BmJnyUaQQsk5TXFG/LpBu9fa+LrAQ91fQ==
dependencies:
"@typescript-eslint/experimental-utils" "4.33.0"
"@typescript-eslint/scope-manager" "4.33.0"
debug "^4.3.1"
"@typescript-eslint/scope-manager" "5.33.1"
"@typescript-eslint/type-utils" "5.33.1"
"@typescript-eslint/utils" "5.33.1"
debug "^4.3.4"
functional-red-black-tree "^1.0.1"
ignore "^5.1.8"
regexpp "^3.1.0"
semver "^7.3.5"
ignore "^5.2.0"
regexpp "^3.2.0"
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/experimental-utils@4.33.0", "@typescript-eslint/experimental-utils@^4.0.1":
"@typescript-eslint/experimental-utils@^4.0.1":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd"
integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==
@ -4302,15 +4303,15 @@
eslint-scope "^5.1.1"
eslint-utils "^3.0.0"
"@typescript-eslint/parser@^4.22.0":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899"
integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==
"@typescript-eslint/parser@^5.33.1":
version "5.33.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.1.tgz#e4b253105b4d2a4362cfaa4e184e2d226c440ff3"
integrity sha512-IgLLtW7FOzoDlmaMoXdxG8HOCByTBXrB1V2ZQYSEV1ggMmJfAkMWTwUjjzagS6OkfpySyhKFkBw7A9jYmcHpZA==
dependencies:
"@typescript-eslint/scope-manager" "4.33.0"
"@typescript-eslint/types" "4.33.0"
"@typescript-eslint/typescript-estree" "4.33.0"
debug "^4.3.1"
"@typescript-eslint/scope-manager" "5.33.1"
"@typescript-eslint/types" "5.33.1"
"@typescript-eslint/typescript-estree" "5.33.1"
debug "^4.3.4"
"@typescript-eslint/scope-manager@4.33.0":
version "4.33.0"
@ -4320,11 +4321,33 @@
"@typescript-eslint/types" "4.33.0"
"@typescript-eslint/visitor-keys" "4.33.0"
"@typescript-eslint/scope-manager@5.33.1":
version "5.33.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.1.tgz#8d31553e1b874210018ca069b3d192c6d23bc493"
integrity sha512-8ibcZSqy4c5m69QpzJn8XQq9NnqAToC8OdH/W6IXPXv83vRyEDPYLdjAlUx8h/rbusq6MkW4YdQzURGOqsn3CA==
dependencies:
"@typescript-eslint/types" "5.33.1"
"@typescript-eslint/visitor-keys" "5.33.1"
"@typescript-eslint/type-utils@5.33.1":
version "5.33.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.1.tgz#1a14e94650a0ae39f6e3b77478baff002cec4367"
integrity sha512-X3pGsJsD8OiqhNa5fim41YtlnyiWMF/eKsEZGsHID2HcDqeSC5yr/uLOeph8rNF2/utwuI0IQoAK3fpoxcLl2g==
dependencies:
"@typescript-eslint/utils" "5.33.1"
debug "^4.3.4"
tsutils "^3.21.0"
"@typescript-eslint/types@4.33.0":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72"
integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==
"@typescript-eslint/types@5.33.1":
version "5.33.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.1.tgz#3faef41793d527a519e19ab2747c12d6f3741ff7"
integrity sha512-7K6MoQPQh6WVEkMrMW5QOA5FO+BOwzHSNd0j3+BlBwd6vtzfZceJ8xJ7Um2XDi/O3umS8/qDX6jdy2i7CijkwQ==
"@typescript-eslint/typescript-estree@4.33.0":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609"
@ -4338,6 +4361,31 @@
semver "^7.3.5"
tsutils "^3.21.0"
"@typescript-eslint/typescript-estree@5.33.1":
version "5.33.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.1.tgz#a573bd360790afdcba80844e962d8b2031984f34"
integrity sha512-JOAzJ4pJ+tHzA2pgsWQi4804XisPHOtbvwUyqsuuq8+y5B5GMZs7lI1xDWs6V2d7gE/Ez5bTGojSK12+IIPtXA==
dependencies:
"@typescript-eslint/types" "5.33.1"
"@typescript-eslint/visitor-keys" "5.33.1"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/utils@5.33.1":
version "5.33.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.1.tgz#171725f924fe1fe82bb776522bb85bc034e88575"
integrity sha512-uphZjkMaZ4fE8CR4dU7BquOV6u0doeQAr8n6cQenl/poMaIyJtBu8eys5uk6u5HiDH01Mj5lzbJ5SfeDz7oqMQ==
dependencies:
"@types/json-schema" "^7.0.9"
"@typescript-eslint/scope-manager" "5.33.1"
"@typescript-eslint/types" "5.33.1"
"@typescript-eslint/typescript-estree" "5.33.1"
eslint-scope "^5.1.1"
eslint-utils "^3.0.0"
"@typescript-eslint/visitor-keys@4.33.0":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd"
@ -4346,6 +4394,14 @@
"@typescript-eslint/types" "4.33.0"
eslint-visitor-keys "^2.0.0"
"@typescript-eslint/visitor-keys@5.33.1":
version "5.33.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.1.tgz#0155c7571c8cd08956580b880aea327d5c34a18b"
integrity sha512-nwIxOK8Z2MPWltLKMLOEZwmfBZReqUdbEoHQXeCpa+sRVARe5twpJGHCB4dk9903Yaf0nMAlGbQfaAH92F60eg==
dependencies:
"@typescript-eslint/types" "5.33.1"
eslint-visitor-keys "^3.3.0"
"@urql/core@>=2.3.6", "@urql/core@^2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@urql/core/-/core-2.6.1.tgz#c10ee972c5e81df6d7bf1e778ef1b5d30e2906e5"
@ -8198,6 +8254,11 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
eslint-visitor-keys@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
eslint@^7.27.0:
version "7.32.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
@ -9350,7 +9411,7 @@ globby@11.0.3:
merge2 "^1.3.0"
slash "^3.0.0"
globby@^11.0.1, globby@^11.0.2, globby@^11.0.3:
globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
@ -10104,7 +10165,7 @@ ignore@^4.0.3, ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
ignore@^5.1.4, ignore@^5.1.8, ignore@^5.2.0:
ignore@^5.1.4, ignore@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
@ -15305,7 +15366,7 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1, regexp.prototype.f
define-properties "^1.1.3"
functions-have-names "^1.2.2"
regexpp@^3.1.0:
regexpp@^3.1.0, regexpp@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
@ -15940,7 +16001,7 @@ semver@7.0.0, semver@~7.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
semver@7.3.7, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
semver@7.3.7, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
@ -17540,7 +17601,7 @@ typescript@^2.7.2:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
typescript@^4.2.4:
typescript@^4.7.4:
version "4.7.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==