diff --git a/src/App.tsx b/src/App.tsx index 87a112a10..e8c5dd7e0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React, {Suspense, useContext, useEffect, useMemo} from 'react'; +import React, {Suspense, useContext, useEffect} from 'react'; import {Navigate, Route, Routes, useLocation} from 'react-router-dom'; import {useUpdate} from 'react-use'; @@ -23,10 +23,6 @@ import { Webhooks, } from '@pages'; -import PluginsContext from '@plugins/context'; -import createPluginManager from '@plugins/manager'; -import {Plugin} from '@plugins/types'; - import {getApiDetails, getApiEndpoint, isApiEndpointLocked, useApiEndpoint} from '@services/apiEndpoint'; import {useGetExecutorsQuery} from '@services/executors'; import {useGetSourcesQuery} from '@services/sources'; @@ -42,11 +38,9 @@ import {PollingIntervals} from '@utils/numbers'; import {MessagePanelWrapper} from './App.styled'; -export interface AppProps { - plugins: Plugin[]; -} +export interface AppProps {} -const App: React.FC = ({plugins}) => { +const App: React.FC = () => { const location = useLocation(); const apiEndpoint = useApiEndpoint(); const {isClusterAvailable} = useContext(MainContext); @@ -117,20 +111,10 @@ const App: React.FC = ({plugins}) => { }); }, [apiEndpoint, isClusterAvailable]); - const scope = useMemo(() => { - const pluginManager = createPluginManager(); - plugins.forEach(plugin => pluginManager.add(plugin)); - return pluginManager.setup(); - }, [plugins]); - return composeProviders() .append(Suspense, {fallback: }) .append(LogOutputProvider, {}) - .append(PluginsContext.Provider, { - value: { - scope, - }, - }) + .render( }> {!isTestkubeCloudLaunchBannerHidden && showTestkubeCloudBanner ? ( diff --git a/src/AppRoot.tsx b/src/AppRoot.tsx index 8cf1c31c4..8703ed104 100644 --- a/src/AppRoot.tsx +++ b/src/AppRoot.tsx @@ -22,7 +22,9 @@ import {ErrorBoundary} from '@pages'; import {BasePermissionsResolver, PermissionsProvider} from '@permissions/base'; +import PluginsContext from '@plugins/context'; import createAiInsightsPlugin from '@plugins/definitions/ai-insights'; +import createPluginManager from '@plugins/manager'; import {Plugin} from '@plugins/types'; import {resetRtkCache, store} from '@redux/store'; @@ -134,7 +136,18 @@ const AppRoot: React.FC = () => { const plugins: Plugin[] = useMemo(() => [createAiInsightsPlugin()], []); + const scope = useMemo(() => { + const pluginManager = createPluginManager(); + plugins.forEach(plugin => pluginManager.add(plugin)); + return pluginManager.setup(); + }, [plugins]); + return composeProviders() + .append(PluginsContext.Provider, { + value: { + scope, + }, + }) .append(FeatureFlagsProvider, {}) .append(ConfigContext.Provider, {value: config}) .append(DashboardContext.Provider, {value: dashboardValue}) @@ -156,7 +169,7 @@ const AppRoot: React.FC = () => { - + diff --git a/src/antd-theme/my-antd-theme.less b/src/antd-theme/my-antd-theme.less index d72a5d7ad..829b74fba 100644 --- a/src/antd-theme/my-antd-theme.less +++ b/src/antd-theme/my-antd-theme.less @@ -1,4 +1,5 @@ @primary-color: #7984f4; +@primary-1: #7984f4; @error-color: #ec4899; @heading-color: #fff; @text-color: #fff; @@ -64,9 +65,10 @@ @picker-border-color: #1e293b; @picker-panel-bg: #1e293b; -@calendar-full-panel-bg: #475569; -@calendar-full-bg: #475569; -@calendar-bg: #475569; +@calendar-full-panel-bg: #0f172a; +@calendar-full-bg: #0f172a; +@calendar-bg: #0f172a; +@calendar-border-color: #1e293b; // notification @notification-bg: #1e293b; @notification-padding-vertical: 0; diff --git a/src/assets/images/status-page-icon.svg b/src/assets/images/status-page-icon.svg new file mode 100644 index 000000000..459e2bf18 --- /dev/null +++ b/src/assets/images/status-page-icon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/components/atoms/Icon/Icon.styled.tsx b/src/components/atoms/Icon/Icon.styled.tsx index aeff7f414..9576d6836 100644 --- a/src/components/atoms/Icon/Icon.styled.tsx +++ b/src/components/atoms/Icon/Icon.styled.tsx @@ -7,5 +7,6 @@ export const StyledAntdIcon = styled(AntdIcon)` height: 100%; width: 100%; fill: currentcolor; + color: currentcolor; } `; diff --git a/src/components/atoms/Icon/Icon.tsx b/src/components/atoms/Icon/Icon.tsx index 030336ce8..4453b23ac 100644 --- a/src/components/atoms/Icon/Icon.tsx +++ b/src/components/atoms/Icon/Icon.tsx @@ -19,6 +19,7 @@ const { DelayIcon, AbortingStatusIcon, CloudMigrateIcon, + StatusPageIcon, } = Icons; const iconsMap: Record = { @@ -40,6 +41,11 @@ const iconsMap: Record = { aborting: AbortingStatusIcon, success: PassedStatusIcon, cloudMigrate: CloudMigrateIcon, + statusPage: StatusPageIcon, + operational: PassedStatusIcon, + major_outage: FailedStatusIcon, + partial_outage: FailedStatusIcon, + unknown: FailedStatusIcon, }; const Icon: React.FC = props => { diff --git a/src/components/atoms/Icon/icons.tsx b/src/components/atoms/Icon/icons.tsx index 59aa22b95..7553fab03 100644 --- a/src/components/atoms/Icon/icons.tsx +++ b/src/components/atoms/Icon/icons.tsx @@ -54,7 +54,7 @@ const FailedStatusIcon: React.FC = () => { ); @@ -99,6 +99,17 @@ const CloudMigrateIcon: React.FC = () => { ); }; +const StatusPageIcon: React.FC = () => { + return ( + + + + ); +}; + export default { CogIcon, DocumentationIcon, @@ -111,4 +122,5 @@ export default { DelayIcon, AbortingStatusIcon, CloudMigrateIcon, + StatusPageIcon, }; diff --git a/src/components/atoms/Icon/types.ts b/src/components/atoms/Icon/types.ts index 02eb20ab4..9a706a6b1 100644 --- a/src/components/atoms/Icon/types.ts +++ b/src/components/atoms/Icon/types.ts @@ -18,7 +18,12 @@ export type IconProps = { | 'error' | 'queued' | 'success' - | 'cloudMigrate'; + | 'cloudMigrate' + | 'statusPage' + | 'operational' + | 'major_outage' + | 'partial_outage' + | 'unknown'; component?: IconComponentProps['component']; style?: React.CSSProperties; }; diff --git a/src/components/atoms/StatusIcon/StatusIcon.tsx b/src/components/atoms/StatusIcon/StatusIcon.tsx index 7f4c554c0..73b3ffb62 100644 --- a/src/components/atoms/StatusIcon/StatusIcon.tsx +++ b/src/components/atoms/StatusIcon/StatusIcon.tsx @@ -14,22 +14,27 @@ interface IconStyle { const iconStyles: Record = { failed: { + color: Colors.pink400, borderColor: Colors.pink600, background: Colors.pink900, }, error: { + color: Colors.pink400, borderColor: Colors.pink600, background: Colors.pink900, }, passed: { + color: Colors.lime300, borderColor: Colors.lime600, background: Colors.lime900, }, success: { + color: Colors.lime300, borderColor: Colors.lime600, background: Colors.lime900, }, running: { + color: Colors.sky400, borderColor: Colors.sky600, background: Colors.sky900, }, @@ -39,25 +44,51 @@ const iconStyles: Record = { color: Colors.slate400, }, queued: { + color: Colors.slate400, borderColor: Colors.slate600, background: Colors.slate900, }, cancelled: { + color: Colors.pink400, borderColor: Colors.pink600, background: Colors.pink900, }, timeout: { + color: Colors.pink400, borderColor: Colors.pink600, background: Colors.pink900, }, aborted: { + color: Colors.pink400, borderColor: Colors.pink600, background: Colors.pink900, }, aborting: { + color: Colors.pink400, borderColor: Colors.pink600, background: Colors.pink900, }, + + partial_outage: { + color: Colors.amber400, + borderColor: Colors.amber600, + background: Colors.amber900, + }, + major_outage: { + color: Colors.pink400, + borderColor: Colors.pink600, + background: Colors.pink900, + }, + operational: { + color: Colors.lime400, + borderColor: Colors.lime600, + background: Colors.lime900, + }, + unknown: { + color: Colors.slate400, + borderColor: Colors.slate600, + background: Colors.slate900, + }, }; type StatusIconProps = { diff --git a/src/components/atoms/Tag/Tag.styled.tsx b/src/components/atoms/Tag/Tag.styled.tsx index 9023593df..ce86838d9 100644 --- a/src/components/atoms/Tag/Tag.styled.tsx +++ b/src/components/atoms/Tag/Tag.styled.tsx @@ -48,4 +48,28 @@ export const TagContainer = styled.div` border: 1px solid ${Colors.sky600}; color: ${Colors.sky300}; } + + &.critical { + background: ${Colors.pink900}; + border: 1px solid ${Colors.pink600}; + color: ${Colors.pink300}; + } + + &.major { + background: ${Colors.rose900}; + border: 1px solid ${Colors.rose600}; + color: ${Colors.rose300}; + } + + &.minor { + background: ${Colors.amber900}; + border: 1px solid ${Colors.amber600}; + color: ${Colors.amber300}; + } + + &.low { + background: ${Colors.yellow900}; + border: 1px solid ${Colors.yellow600}; + color: ${Colors.yellow300}; + } `; diff --git a/src/components/atoms/Tag/Tag.tsx b/src/components/atoms/Tag/Tag.tsx index 4ebeaf161..749398ab4 100644 --- a/src/components/atoms/Tag/Tag.tsx +++ b/src/components/atoms/Tag/Tag.tsx @@ -8,7 +8,7 @@ import {TagContainer} from './Tag.styled'; export interface TagProps { title: string; - type?: 'success' | 'warning' | 'error' | 'info'; + type?: 'success' | 'warning' | 'error' | 'info' | 'critical' | 'major' | 'minor' | 'low'; tooltipMessage?: string; icon?: ReactNode; } diff --git a/src/components/organisms/Sider/Sider.tsx b/src/components/organisms/Sider/Sider.tsx index 193478d64..6bffd09d2 100644 --- a/src/components/organisms/Sider/Sider.tsx +++ b/src/components/organisms/Sider/Sider.tsx @@ -21,6 +21,8 @@ import {useDashboardNavigate} from '@hooks/useDashboardNavigate'; import {ReactComponent as SettingIcon} from '@icons/setting.svg'; +import {usePluginSlotList} from '@plugins/hooks'; + import Colors from '@styles/Colors'; import {externalLinks} from '@utils/externalLinks'; @@ -43,73 +45,112 @@ const DEFAULT_ICON_STYLE = { const getRoutes = (showSocialLinksInSider: boolean) => [ { - path: '/tests', - icon: TestsIcon, - title: 'Tests', - transition: { - classNames: 'item2', + value: { + path: '/tests', + icon: TestsIcon, + title: 'Tests', + transition: { + classNames: 'item2', + }, + }, + metadata: { + order: 7, + visible: () => true, }, }, { - path: '/test-suites', - icon: TestSuitesIcon, - title: 'Test Suites', - transition: { - classNames: 'item', + value: { + path: '/test-suites', + icon: TestSuitesIcon, + title: 'Test Suites', + transition: { + classNames: 'item', + }, + }, + metadata: { + order: 6, + visible: () => true, + }, + }, + { + value: { + path: '/executors', + icon: ExecutorsIcon, + title: 'Executors', + transition: { + classNames: 'item', + }, + }, + metadata: { + order: 5, + visible: () => true, }, }, { - path: '/executors', - icon: ExecutorsIcon, - title: 'Executors', - transition: { - classNames: 'item', + value: { + path: '/triggers', + icon: TriggersIcon, + title: 'Triggers', + transition: { + classNames: 'item', + }, + }, + metadata: { + order: 4, + visible: () => true, }, }, { - path: '/triggers', - icon: TriggersIcon, - title: 'Triggers', - transition: { - classNames: 'item', + value: { + path: '/webhooks', + icon: WebhooksIcon, + title: 'Webhooks', + transition: { + classNames: 'item', + }, + }, + metadata: { + order: 3, + visible: () => true, }, }, { - path: '/webhooks', - icon: WebhooksIcon, - title: 'Webhooks', - transition: { - classNames: 'item', + value: { + path: '/sources', + icon: SourcesIcon, + title: 'Sources', + transition: { + classNames: 'item', + }, + }, + metadata: { + order: 2, + visible: () => true, }, }, + { - path: '/sources', - icon: SourcesIcon, - title: 'Sources', - transition: { - classNames: 'item', + value: { + path: '/settings', + icon: SettingIcon, + title: 'Settings', + transition: { + classNames: 'item', + }, + additionalClassName: 'settings-icon', + active: /environment-management/, + }, + metadata: { + order: -Infinity, + visible: () => !showSocialLinksInSider, }, }, - ...(showSocialLinksInSider - ? [] - : [ - { - path: '/settings', - icon: SettingIcon, - title: 'Settings', - transition: { - classNames: 'item', - }, - additionalClassName: 'settings-icon', - active: /environment-management/, - }, - ]), ]; const Sider: React.FC = () => { const {showLogoInSider, showSocialLinksInSider} = useContext(DashboardContext); const openSettings = useDashboardNavigate('/settings'); - + const siderMenuList = usePluginSlotList('statusPageSiderMenuItem', getRoutes(showSocialLinksInSider)); const otherMenuItems = [ { icon: 'cog', @@ -162,7 +203,7 @@ const Sider: React.FC = () => { ]; const renderedMenuItems = useMemo(() => { - return getRoutes(showSocialLinksInSider).map(route => { + return siderMenuList.map(route => { const {icon: MenuIcon, path, title, active} = route; return ( @@ -181,7 +222,7 @@ const Sider: React.FC = () => { ); }); - }, [showSocialLinksInSider]); + }, [showSocialLinksInSider, siderMenuList]); const renderedOtherMenuItems = useMemo(() => { return otherMenuItems.map(otherMenuItem => { diff --git a/src/styles/Colors.ts b/src/styles/Colors.ts index 2a68b145c..1d5e83737 100644 --- a/src/styles/Colors.ts +++ b/src/styles/Colors.ts @@ -28,6 +28,7 @@ enum Colors { pink700 = '#BE185D', pink600 = '#DB2777', pink500 = '#EC4899', + pink400 = '#F472B6', pink300 = '#F9A8D4', lime900 = '#365314', @@ -69,6 +70,7 @@ enum Colors { amber400 = '#fbbf24', amber500 = '#F59E0B', amber600 = '#D97706', + amber800 = '#92400E', amber900 = '#78350F', amber90099 = '#78350F99', @@ -167,6 +169,16 @@ export enum StatusColors { timeout = Colors.pink600, aborted = Colors.pink600, aborting = Colors.pink600, + operational = Colors.lime400, + partial_outage = Colors.amber500, + major_outage = Colors.pink600, + unavailable = Colors.slate700, + unknown = Colors.slate700, + critical = Colors.rose500, + major = Colors.rose500, + minor = Colors.amber400, + low = Colors.yellow400, + info = Colors.slate50, } export enum SecondaryStatusColors { @@ -179,6 +191,11 @@ export enum SecondaryStatusColors { timeout = Colors.pink800, aborted = Colors.pink800, aborting = Colors.pink800, + operational = Colors.lime700, + partial_outage = Colors.amber800, + major_outage = Colors.pink800, + unavailable = Colors.slate900, + unknown = Colors.slate900, } export enum BorderColors { diff --git a/src/styles/globalStyles.ts b/src/styles/globalStyles.ts index e651278b5..b7272f04d 100644 --- a/src/styles/globalStyles.ts +++ b/src/styles/globalStyles.ts @@ -484,4 +484,13 @@ export const GlobalStyle = createGlobalStyle` fill: ${Colors.whitePure}; } } + + /* Calendar */ + .ant-picker-panel-container { + border: 1px solid ${Colors.slate800}; + } + + .ant-picker-time-panel-column > li.ant-picker-time-panel-cell .ant-picker-time-panel-cell-inner:hover { + background: ${Colors.slate400}; + } `;