-
Notifications
You must be signed in to change notification settings - Fork 2
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
HOSTSD-276 Storage Trend Size Type #121
Changes from all commits
c664049
3eb572b
062e734
b864b57
185f99d
511ad3e
508c8ac
071b9e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
'use client'; | ||
|
||
import { DateRangePicker } from '@/components'; | ||
import { IServerItemListModel } from '@/hooks'; | ||
import { useDashboard } from '@/hooks/dashboard'; | ||
import { useStorageTrendsStore } from '@/store'; | ||
import { | ||
|
@@ -31,8 +32,13 @@ interface LineChartProps { | |
loading?: boolean; | ||
/** Date range selected for the filter. */ | ||
dateRange?: string[]; | ||
/** An array of server items */ | ||
serverItems?: IServerItemListModel[]; | ||
/** Whether to show the export button. */ | ||
showExport?: boolean; | ||
/** Whether the export button is disabled. */ | ||
exportDisabled?: boolean; | ||
/** Event fires when export button is clicked. */ | ||
onExport?: (startDate?: string, endDate?: string) => void; | ||
} | ||
|
||
|
@@ -46,6 +52,7 @@ export const StorageTrendsChart: React.FC<LineChartProps> = ({ | |
loading, | ||
minColumns, | ||
dateRange: initDateRange, | ||
serverItems = [], | ||
showExport, | ||
exportDisabled, | ||
onExport, | ||
|
@@ -56,6 +63,8 @@ export const StorageTrendsChart: React.FC<LineChartProps> = ({ | |
const { isReady: serverHistoryItemsIsReady, findServerHistoryItems } = useServerHistoryItems(); | ||
const { tenantId, organizationId, operatingSystemItemId, serverItemKey } = useDashboard(); | ||
|
||
const [forceFetch, setForceFetch] = React.useState(0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clicking the Update button doesn't do anything if the date doesn't change. This allows it to force an update. |
||
|
||
React.useEffect(() => { | ||
const values = [ | ||
initDateRange?.length && initDateRange[0] | ||
|
@@ -68,22 +77,29 @@ export const StorageTrendsChart: React.FC<LineChartProps> = ({ | |
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [initDateRange?.[0], initDateRange?.[1], setDateRange]); | ||
|
||
const fetch = React.useCallback( | ||
(startDate?: string, endDate?: string) => { | ||
if (startDate) { | ||
// A single server was selected, fetch the history for this server. | ||
return findServerHistoryItems({ | ||
tenantId: tenantId, | ||
organizationId: organizationId, | ||
operatingSystemItemId: operatingSystemItemId, | ||
serviceNowKey: serverItemKey, | ||
startDate: startDate, | ||
endDate: endDate ? endDate : undefined, | ||
}).catch((ex) => { | ||
const error = ex as Error; | ||
toast.error(error.message); | ||
console.error(error); | ||
}); | ||
} | ||
}, | ||
[findServerHistoryItems, operatingSystemItemId, organizationId, serverItemKey, tenantId], | ||
); | ||
|
||
React.useEffect(() => { | ||
if (dateRange[0]) { | ||
// A single server was selected, fetch the history for this server. | ||
findServerHistoryItems({ | ||
tenantId: tenantId, | ||
organizationId: organizationId, | ||
operatingSystemItemId: operatingSystemItemId, | ||
serviceNowKey: serverItemKey, | ||
startDate: dateRange[0], | ||
endDate: dateRange[1] ? dateRange[1] : undefined, | ||
}).catch((ex) => { | ||
const error = ex as Error; | ||
toast.error(error.message); | ||
console.error(error); | ||
}); | ||
} | ||
fetch(dateRange[0], dateRange[1])?.catch(() => {}); | ||
// Values array will cause infinite loop, we're only interested in when values change. | ||
// findServerHistoryItems will cause infinite loop. | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
|
@@ -97,11 +113,12 @@ export const StorageTrendsChart: React.FC<LineChartProps> = ({ | |
dateRange[0], | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
dateRange[1], | ||
forceFetch, | ||
]); | ||
|
||
return ( | ||
<LineChart | ||
data={getStorageTrendsData(minColumns, dateRange)} | ||
data={getStorageTrendsData(minColumns, dateRange, serverItems)} | ||
label="Storage Trends" | ||
loading={loading || !serverHistoryItemsIsReady} | ||
large={large} | ||
|
@@ -115,8 +132,9 @@ export const StorageTrendsChart: React.FC<LineChartProps> = ({ | |
<div className={styles.date}> | ||
<DateRangePicker | ||
values={dateRange} | ||
onChange={async (values, e) => { | ||
setDateRange(values); | ||
onChange={(values, e) => { | ||
if (values[0] !== dateRange[0] || values[1] !== dateRange[1]) setDateRange(values); | ||
else setForceFetch(Date.now()); | ||
}} | ||
showButton | ||
/> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,11 @@ | ||
import { IServerHistoryItemModel } from '@/hooks'; | ||
import { IServerHistoryItemModel, IServerItemListModel } from '@/hooks'; | ||
import { useStorageTrendsStore } from '@/store'; | ||
import { convertToStorageSize } from '@/utils'; | ||
import { ChartData } from 'chart.js'; | ||
import moment from 'moment'; | ||
import React from 'react'; | ||
import { generateStorageHistoryForDateRange } from '../../utils'; | ||
import { convertStorageSize } from './../../../../utils/convertToStorageSize'; | ||
|
||
/** | ||
* Generates line chart data based on the current filtered server history items. | ||
|
@@ -14,13 +15,25 @@ import { generateStorageHistoryForDateRange } from '../../utils'; | |
export const useStorageTrendsData = (): (( | ||
minColumns?: number, | ||
dateRange?: string[], | ||
serverItems?: IServerItemListModel[], | ||
) => ChartData<'line', number[], string>) => { | ||
const serverHistoryItems = useStorageTrendsStore((state) => state.serverHistoryItems); | ||
|
||
return React.useCallback( | ||
(minColumns: number = 1, dateRange: string[] = []) => { | ||
( | ||
minColumns: number = 1, | ||
dateRange: string[] = [], | ||
serverItems: IServerItemListModel[] = [], | ||
) => { | ||
const groups = generateStorageHistoryForDateRange(minColumns, dateRange); | ||
|
||
// Determine the output size type to use based on the current total storage of the selected servers. | ||
const storageSizeType = convertStorageSize( | ||
serverItems.map((i) => i.capacity ?? 0).reduce((a, b) => a + b, 0), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the magic |
||
'B', | ||
'TB', | ||
).type; | ||
|
||
// server history is returned for each server, however some servers may lack history. | ||
// This process needs to group each month. | ||
const items = serverHistoryItems | ||
|
@@ -49,15 +62,15 @@ export const useStorageTrendsData = (): (( | |
.map((i) => i.capacity) | ||
.reduce((result, value) => (result ?? 0) + (value ?? 0), 0) ?? 0, | ||
'B', | ||
'TB', | ||
storageSizeType, | ||
{ type: 'number' }, | ||
); | ||
group.availableSpace = convertToStorageSize<number>( | ||
values | ||
.map((i) => i.availableSpace) | ||
.reduce((result, value) => (result ?? 0) + (value ?? 0), 0) ?? 0, | ||
'B', | ||
'TB', | ||
storageSizeType, | ||
{ type: 'number' }, | ||
); | ||
group.usedSpace = group.capacity - group.availableSpace; | ||
|
@@ -67,14 +80,14 @@ export const useStorageTrendsData = (): (( | |
labels: groups.map((i) => i.label), | ||
datasets: [ | ||
{ | ||
label: 'Total Used in TB', | ||
label: `Total Used in ${storageSizeType}`, | ||
data: groups.map((i) => i.usedSpace), | ||
borderColor: '#313132', | ||
backgroundColor: '#313132', | ||
fill: false, | ||
}, | ||
{ | ||
label: 'Total Allocated in TB', | ||
label: `Total Allocated in ${storageSizeType}`, | ||
data: groups.map((i) => i.capacity), | ||
borderColor: '#476E94', | ||
backgroundColor: '#476E94', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
* @param options Configuration options. | ||
* @returns A string representing the storage size. | ||
*/ | ||
export const convertToStorageSize = <T extends string | number>( | ||
export const convertStorageSize = <T extends string | number>( | ||
value: number, | ||
input: 'TB' | 'GB' | 'MB' | 'KB' | 'B' | '' = '', | ||
output: 'TB' | 'GB' | 'MB' | 'KB' | 'B' | '' = '', | ||
|
@@ -17,7 +17,7 @@ export const convertToStorageSize = <T extends string | number>( | |
locales?: Intl.LocalesArgument; | ||
} & Intl.NumberFormatOptions) | ||
| undefined, | ||
): T => { | ||
): { value: T; type: 'TB' | 'GB' | 'MB' | 'KB' | 'B' | '' } => { | ||
var result = value; | ||
if (input === output) result = value; | ||
else if (input === 'TB') { | ||
|
@@ -65,13 +65,37 @@ export const convertToStorageSize = <T extends string | number>( | |
} | ||
result = options?.formula?.(result) ?? result; | ||
|
||
return { value: result as T, type: output }; | ||
}; | ||
|
||
/** | ||
* Converts a storage value to a specified size type. | ||
* @param value The initial value to convert. | ||
* @param input The input size type. | ||
* @param output The output size type. | ||
* @param options Configuration options. | ||
* @returns A string representing the storage size. | ||
*/ | ||
export const convertToStorageSize = <T extends string | number>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Separated the function into two so that I have access to the logic without the string output. |
||
value: number, | ||
input: 'TB' | 'GB' | 'MB' | 'KB' | 'B' | '' = '', | ||
output: 'TB' | 'GB' | 'MB' | 'KB' | 'B' | '' = '', | ||
options?: | ||
| ({ | ||
formula?: (value: number) => number; | ||
type?: 'string' | 'number'; | ||
locales?: Intl.LocalesArgument; | ||
} & Intl.NumberFormatOptions) | ||
| undefined, | ||
): T => { | ||
const result = convertStorageSize<T>(value, input, output, options); | ||
|
||
return ( | ||
options?.type === 'number' | ||
? result | ||
: `${result.toLocaleString( | ||
options?.locales ?? navigator.language, | ||
options, | ||
)} ${output}`.trimEnd() | ||
? result.value | ||
: `${result.value.toLocaleString(options?.locales ?? navigator.language, options)} ${ | ||
result.type | ||
}`.trimEnd() | ||
) as T; | ||
}; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to pass the selected server items down so that it can be determine which storage size they are using.