Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upcoming: [DI-18311] - Adding CloudPulse section to Cloud Manager #10397

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/api-v4/src/account/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export type BillingSource = 'linode' | 'akamai';

export type AccountCapability =
| 'Akamai Cloud Load Balancer'
| 'CloudPulse'
| 'Block Storage'
| 'Cloud Firewall'
| 'Disk Encryption'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add Dashboard Global Filters and Dashboards Tab to the CloudPulse component ([#10397](https://github.com/linode/manager/pull/10397))
6 changes: 4 additions & 2 deletions packages/manager/src/MainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,12 @@ export const MainContent = () => {
const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled();

const { data: accountSettings } = useAccountSettings();
const defaultRoot = accountSettings?.managed ? '/managed' : '/linodes';

const showCloudPulse = Boolean(flags.aclp?.enabled);

const defaultRoot = accountSettings?.managed ? '/managed' : '/linodes';
// the followed comment is for later use, the showCloudPulse will be removed and isACLPEnabled will be used
// const { isACLPEnabled } = useIsACLPEnabled();


/**
* this is the case where the user has successfully completed signup
Expand Down
6 changes: 4 additions & 2 deletions packages/manager/src/components/PrimaryNav/PrimaryNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,11 @@ export const PrimaryNav = (props: PrimaryNavProps) => {

const allowMarketplacePrefetch =
!oneClickApps && !oneClickAppsLoading && !oneClickAppsError;
;
const showCloudPulse = Boolean(flags.aclp?.enabled);

const showCloudPulse = Boolean(flags.aclp?.enabled);
jaalah-akamai marked this conversation as resolved.
Show resolved Hide resolved
// the followed comment is for later use, the showCloudPulse will be removed and isACLPEnabled will be used
// const { isACLPEnabled } = useIsACLPEnabled();

const { isACLBEnabled } = useIsACLBEnabled();
const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled();
const { isDatabasesEnabled } = useIsDatabasesEnabled();
Expand Down
1 change: 0 additions & 1 deletion packages/manager/src/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ interface AclpFlag {
interface gpuV2 {
planDivider: boolean;
}

type OneClickApp = Record<string, string>;

export interface Flags {
Expand Down
16 changes: 13 additions & 3 deletions packages/manager/src/features/CloudPulse/CloudPulseLanding.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import * as React from 'react';
import { Route, Switch } from 'react-router-dom';

import { LandingHeader } from 'src/components/LandingHeader/LandingHeader';
import { Paper } from 'src/components/Paper';
import { SuspenseLoader } from 'src/components/SuspenseLoader';

import { CloudPulseTabs } from './CloudPulseTabs';
export const CloudPulseLanding = () => {
return (
<>
<LandingHeader removeCrumbX={1} title="Akamai Cloud Pulse" />
<Paper></Paper>
<LandingHeader
breadcrumbProps={{ pathname: '/Akamai Cloud Pulse' }}
docsLabel="Getting Started"
docsLink="https://www.linode.com/docs/"
/>
<React.Suspense fallback={<SuspenseLoader />}>
<Switch>
<Route component={CloudPulseTabs} />
</Switch>
</React.Suspense>
</>
);
};
55 changes: 55 additions & 0 deletions packages/manager/src/features/CloudPulse/CloudPulseTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { styled } from '@mui/material/styles';
import * as React from 'react';
import { RouteComponentProps, matchPath } from 'react-router-dom';

import { SuspenseLoader } from 'src/components/SuspenseLoader';
import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel';
import { TabLinkList } from 'src/components/Tabs/TabLinkList';
import { TabPanels } from 'src/components/Tabs/TabPanels';
import { Tabs } from 'src/components/Tabs/Tabs';

import { DashboardLanding } from './Dashboard/DashboardLanding';
type Props = RouteComponentProps<{}>;

export const CloudPulseTabs = React.memo((props: Props) => {
const tabs = [
{
routeName: `${props.match.url}/dashboards`,
title: 'Dashboards',
},
];

const matches = (p: string) => {
return Boolean(matchPath(p, { path: props.location.pathname }));
};

const navToURL = (index: number) => {
props.history.push(tabs[index].routeName);
};

return (
<StyledTabs
index={Math.max(
tabs.findIndex((tab) => matches(tab.routeName)),
0
)}
onChange={navToURL}
>
<TabLinkList tabs={tabs} />

<React.Suspense fallback={<SuspenseLoader />}>
<TabPanels>
<SafeTabPanel index={0}>
<DashboardLanding />
</SafeTabPanel>
</TabPanels>
</React.Suspense>
</StyledTabs>
);
});

const StyledTabs = styled(Tabs, {
label: 'StyledTabs',
})(() => ({
marginTop: 0,
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Paper } from '@mui/material';
import * as React from 'react';

import { FiltersObject, GlobalFilters } from '../Overview/GlobalFilters';

export const DashboardLanding = () => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
const onFilterChange = (_filters: FiltersObject) => {};
return (
<Paper>
<div style={{ display: 'flex' }}>
jaalah-akamai marked this conversation as resolved.
Show resolved Hide resolved
<div style={{ width: '100%' }}>
<GlobalFilters handleAnyFilterChange={onFilterChange}></GlobalFilters>
</div>
</div>
</Paper>
);
};
102 changes: 102 additions & 0 deletions packages/manager/src/features/CloudPulse/Overview/GlobalFilters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { styled } from '@mui/material/styles';
import Grid from '@mui/material/Unstable_Grid2';
import * as React from 'react';

import { WithStartAndEnd } from 'src/features/Longview/request.types';

import { CloudPulseRegionSelect } from '../shared/RegionSelect';
import { CloudPulseTimeRangeSelect } from '../shared/TimeRangeSelect';

export interface GlobalFilterProperties {
handleAnyFilterChange(filters: FiltersObject): undefined | void;
}

export interface FiltersObject {
interval: string;
region: string;
resource: string[];
serviceType?: string;
timeRange: WithStartAndEnd;
}

export const GlobalFilters = React.memo((props: GlobalFilterProperties) => {
const [time, setTimeBox] = React.useState<WithStartAndEnd>({
end: 0,
start: 0,
});

const [selectedRegion, setRegion] = React.useState<string>();
React.useEffect(() => {
const triggerGlobalFilterChange = () => {
const globalFilters: FiltersObject = {
interval: '',
region: '',
resource: [],
timeRange: time,
};
if (selectedRegion) {
globalFilters.region = selectedRegion;
}
props.handleAnyFilterChange(globalFilters);
};
triggerGlobalFilterChange();
santoshp210-akamai marked this conversation as resolved.
Show resolved Hide resolved
// eslint-disable-next-line react-hooks/exhaustive-deps
jaalah-akamai marked this conversation as resolved.
Show resolved Hide resolved
}, [time, selectedRegion]); // if anything changes, emit an event to parent component

const handleTimeRangeChange = React.useCallback(
(start: number, end: number) => {
setTimeBox({ end, start });
},
[]
);

const handleRegionChange = React.useCallback((region: string | undefined) => {
setRegion(region);
}, []);

return (
<Grid container sx={{ ...itemSpacing, padding: '8px' }}>
<StyledGrid xs={12}>
<Grid sx={{ marginLeft: 2, width: 250 }}>
<StyledCloudPulseRegionSelect
handleRegionChange={handleRegionChange}
/>
</Grid>
<Grid sx={{ marginLeft: 12, width: 250 }}>
<StyledCloudPulseTimeRangeSelect
defaultValue={'Past 30 Minutes'}
handleStatsChange={handleTimeRangeChange}
hideLabel
label="Select Time Range"
/>
</Grid>
</StyledGrid>
</Grid>
);
});

const StyledCloudPulseRegionSelect = styled(CloudPulseRegionSelect, {
label: 'StyledCloudPulseRegionSelect',
})({
width: 150,
});

const StyledCloudPulseTimeRangeSelect = styled(CloudPulseTimeRangeSelect, {
label: 'StyledCloudPulseTimeRangeSelect',
})({
width: 150,
});

const StyledGrid = styled(Grid, { label: 'StyledGrid' })(({ theme }) => ({
alignItems: 'end',
boxSizing: 'border-box',
display: 'flex',
flexDirection: 'row',
justifyContent: 'start',
marginBottom: theme.spacing(1.25),
}));

const itemSpacing = {
boxSizing: 'border-box',
margin: '0',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';

import { CloudPulseRegionSelectProps } from './RegionSelect';
import { CloudPulseRegionSelect } from './RegionSelect';

const props: CloudPulseRegionSelectProps = {
handleRegionChange: vi.fn(),
};

describe('CloudViewRegionSelect', () => {
it('should render a Region Select component', () => {
const { getByTestId } = renderWithTheme(
<CloudPulseRegionSelect {...props} />
);
expect(getByTestId('region-select')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable no-console */
import * as React from 'react';

import { RegionSelect } from 'src/components/RegionSelect/RegionSelect';
import { useRegionsQuery } from 'src/queries/regions/regions';

export interface CloudPulseRegionSelectProps {
handleRegionChange: (region: string | undefined) => void;
}

export const CloudPulseRegionSelect = React.memo(
(props: CloudPulseRegionSelectProps) => {
const { data: regions } = useRegionsQuery();
const [selectedRegion, setRegion] = React.useState<string>();

React.useEffect(() => {
props.handleRegionChange(selectedRegion);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedRegion]);

return (
<RegionSelect
handleSelection={(value) => {
setRegion(value);
}}
currentCapability={undefined}
fullWidth
isClearable={false}
label=""
noMarginTop
regions={regions ? regions : []}
selectedId={null}
/>
);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { DateTime } from 'luxon';

import { generateStartTime } from './TimeRangeSelect';

describe('Utility Functions', () => {
it('should create values as functions that return the correct datetime', () => {
const GMT_november_20_2019_849PM = 1574282998;

expect(
generateStartTime('Past 30 Minutes', GMT_november_20_2019_849PM)
).toEqual(
DateTime.fromSeconds(GMT_november_20_2019_849PM)
.minus({ minutes: 30 })
.toSeconds()
);

expect(
generateStartTime('Past 12 Hours', GMT_november_20_2019_849PM)
).toEqual(
DateTime.fromSeconds(GMT_november_20_2019_849PM)
.minus({ hours: 12 })
.toSeconds()
);

expect(
generateStartTime('Past 24 Hours', GMT_november_20_2019_849PM)
).toEqual(
DateTime.fromSeconds(GMT_november_20_2019_849PM)
.minus({ hours: 24 })
.toSeconds()
);

expect(
generateStartTime('Past 7 Days', GMT_november_20_2019_849PM)
).toEqual(
DateTime.fromSeconds(GMT_november_20_2019_849PM)
.minus({ days: 7 })
.toSeconds()
);

expect(
generateStartTime('Past 30 Days', GMT_november_20_2019_849PM)
).toEqual(
DateTime.fromSeconds(GMT_november_20_2019_849PM)
.minus({ hours: 24 * 30 })
.toSeconds()
);
});
});
Loading
Loading