diff --git a/client/package-lock.json b/client/package-lock.json index 87855d8..8c5daad 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1891,6 +1891,24 @@ "@types/react": "*" } }, + "@types/react-select": { + "version": "3.0.16", + "resolved": "https://nexus.corp.indeed.com/repository/npm/@types/react-select/-/react-select-3.0.16.tgz", + "integrity": "sha512-qXCfHprExDzB+ZYCqzH80D2TE+F17MMAY5/GrHm74xSsmS6B6G8nm3gZiTod/u82JSzDO193qJiP7ZxunT8oBg==", + "requires": { + "@types/react": "*", + "@types/react-dom": "*", + "@types/react-transition-group": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://nexus.corp.indeed.com/repository/npm/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "requires": { + "@types/react": "*" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", diff --git a/client/package.json b/client/package.json index 42cd9c5..de24361 100644 --- a/client/package.json +++ b/client/package.json @@ -11,6 +11,7 @@ "@types/react": "^16.9.44", "@types/react-dom": "^16.9.8", "@types/react-modal": "1.6.8", + "@types/react-select": "^3.0.16", "ansi-to-react": "^5.1.1", "humanize-duration": "^3.23.1", "js-base64": "^2.6.4", diff --git a/client/src/components/field.tsx b/client/src/components/field.tsx index ee179b5..1d5874d 100644 --- a/client/src/components/field.tsx +++ b/client/src/components/field.tsx @@ -3,8 +3,8 @@ import React from 'react'; type FieldProps = { name: string; - value: string | null; - children: React.ReactNode[] | null; + value?: string | null; + children?: React.ReactNode[] | null | string; } const Field = ({name, value, children}: FieldProps) => ( diff --git a/client/src/components/itemHeader.js b/client/src/components/itemHeader.tsx similarity index 68% rename from client/src/components/itemHeader.js rename to client/src/components/itemHeader.tsx index a16086c..5537e44 100644 --- a/client/src/components/itemHeader.js +++ b/client/src/components/itemHeader.tsx @@ -1,7 +1,12 @@ import React from 'react'; import Base from './base'; -export default class ItemHeader extends Base { +interface ItemHeaderProps { + title: string[]; + ready: boolean; +} + +export default class ItemHeader extends Base { render() { const {title, ready, children} = this.props; @@ -11,7 +16,7 @@ export default class ItemHeader extends Base { {title.join(' • ')} -
+
{children}
diff --git a/client/src/components/listViewHelpers.js b/client/src/components/listViewHelpers.tsx similarity index 86% rename from client/src/components/listViewHelpers.js rename to client/src/components/listViewHelpers.tsx index b36e4a0..6d407aa 100644 --- a/client/src/components/listViewHelpers.js +++ b/client/src/components/listViewHelpers.tsx @@ -5,8 +5,9 @@ import {isAValidURL} from '../utils/string'; import Loading from './loading'; import Sorter, {sortByDate} from './sorter'; import ResourceSvg from '../art/resourceSvg'; +import {TODO} from "../utils/types"; -export function objectMap(items = {}) { +export function objectMap(items: {[key: string]: string} = {}) { return Object.entries(items).map(([key, value]) => { const substrValue = value.length <= 50 ? value : `${value.substr(0, 50)}...`; @@ -29,7 +30,7 @@ export function objectMap(items = {}) { }); } -export function TableBody({items, filter, colSpan, sort, row}) { +export function TableBody({items, filter, colSpan, sort, row}: { items: TODO; filter: TODO; colSpan: number; sort: TODO; row: TODO }) { if (items && sort) { const {field, direction} = sort; items = _.orderBy(items, [field], [direction]); // eslint-disable-line no-param-reassign @@ -44,7 +45,7 @@ export function TableBody({items, filter, colSpan, sort, row}) { ); } -export function MetadataHeaders({includeNamespace, sort}) { +export function MetadataHeaders({includeNamespace, sort}: {includeNamespace: boolean, sort: TODO}) { return ( <> @@ -65,7 +66,7 @@ export function MetadataHeaders({includeNamespace, sort}) { ); } -export function MetadataColumns({item, href, includeNamespace, resourceClass}) { +export function MetadataColumns({item, href, includeNamespace, resourceClass}: {item: TODO, href: string, includeNamespace: boolean, resourceClass?: string}) { return ( <> @@ -98,7 +99,7 @@ export function MetadataColumns({item, href, includeNamespace, resourceClass}) { ); } -function NoResults({items, filter, colSpan}) { +function NoResults({items, filter, colSpan}: {items: TODO, filter: TODO, colSpan: number}) { if (!items) { return ( @@ -122,4 +123,8 @@ function NoResults({items, filter, colSpan}) { ); } + + return ( + + ) } diff --git a/client/src/components/loadingChart.js b/client/src/components/loadingChart.tsx similarity index 100% rename from client/src/components/loadingChart.js rename to client/src/components/loadingChart.tsx diff --git a/client/src/components/loadingEllipsis.js b/client/src/components/loadingEllipsis.tsx similarity index 100% rename from client/src/components/loadingEllipsis.js rename to client/src/components/loadingEllipsis.tsx diff --git a/client/src/components/metadataFields.js b/client/src/components/metadataFields.tsx similarity index 93% rename from client/src/components/metadataFields.js rename to client/src/components/metadataFields.tsx index a85ddef..fb00f8b 100644 --- a/client/src/components/metadataFields.js +++ b/client/src/components/metadataFields.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Field from './field'; import {objectMap} from './listViewHelpers'; -const MetadataFields = ({item}) => ( +const MetadataFields = ({item}: {[key: string]: any}) => ( <> diff --git a/client/src/components/namespaceFilter.js b/client/src/components/namespaceFilter.tsx similarity index 60% rename from client/src/components/namespaceFilter.js rename to client/src/components/namespaceFilter.tsx index 2791299..a5c5b24 100644 --- a/client/src/components/namespaceFilter.js +++ b/client/src/components/namespaceFilter.tsx @@ -1,22 +1,34 @@ import React from 'react'; -import Select from 'react-select'; +import Select, {ValueType} from 'react-select'; import Base from './base'; import api from '../services/api'; +import {TODO} from "../utils/types"; -export default class NamespaceFilter extends Base { - constructor({onChange}) { - super(); +interface NamespaceFilterProps { + onChange?: Function; +} + +interface NamespaceFilterStates { + namespace: {}; + namespaces?: any[]; +} + +export default class NamespaceFilter extends Base { + private onChange: Function; + + constructor({onChange}: { onChange: Function }) { + super({}); const {namespace} = localStorage; this.state = {namespace}; this.onChange = onChange; onChange(namespace); this.registerApi({ - namespaces: api.namespace.list(namespaces => this.setState({namespaces})), + namespaces: api.namespace.list((namespaces: any[]) => this.setState({namespaces})), }); } - async setNamespace(namespace) { + async setNamespace(namespace: string) { localStorage.namespace = namespace; this.setState({namespace}); this.onChange(namespace); @@ -36,7 +48,7 @@ export default class NamespaceFilter extends Base { className="react-select" classNamePrefix="react-select" value={value} - onChange={x => this.setNamespace(x.value)} + onChange={(x:ValueType) => this.setNamespace(x.value)} options={options} /> diff --git a/client/src/components/nodeCpuChart.js b/client/src/components/nodeCpuChart.tsx similarity index 86% rename from client/src/components/nodeCpuChart.js rename to client/src/components/nodeCpuChart.tsx index 2fd5ab7..91990df 100644 --- a/client/src/components/nodeCpuChart.js +++ b/client/src/components/nodeCpuChart.tsx @@ -4,7 +4,7 @@ import Chart from './chart'; import LoadingChart from './loadingChart'; import {parseCpu, TO_ONE_CPU} from '../utils/unitHelpers'; -export default function NodeCpuChart({items, metrics}) { +export default function NodeCpuChart({items, metrics}: {items: any[], metrics: any[]}) { const totals = getNodeCpuTotals(items, metrics); return ( @@ -20,7 +20,7 @@ export default function NodeCpuChart({items, metrics}) { ); } -function getNodeCpuTotals(items, metrics) { +function getNodeCpuTotals(items: any[], metrics: any[]) { if (!items || !metrics) return null; const metricValues = Object.values(metrics); diff --git a/client/src/components/nodeRamChart.js b/client/src/components/nodeRamChart.tsx similarity index 86% rename from client/src/components/nodeRamChart.js rename to client/src/components/nodeRamChart.tsx index 051dbcb..0b7aec5 100644 --- a/client/src/components/nodeRamChart.js +++ b/client/src/components/nodeRamChart.tsx @@ -4,7 +4,7 @@ import Chart from './chart'; import LoadingChart from './loadingChart'; import {parseRam, TO_GB} from '../utils/unitHelpers'; -export default function NodeRamChart({items, metrics}) { +export default function NodeRamChart({items, metrics}: {items: any[], metrics: any[]}) { const totals = getNodeRamTotals(items, metrics); return (
@@ -19,7 +19,7 @@ export default function NodeRamChart({items, metrics}) { ); } -function getNodeRamTotals(items, metrics) { +function getNodeRamTotals(items: any[], metrics: any[]) { if (!items || !metrics) return null; const metricValues = Object.values(metrics); diff --git a/client/src/components/podCpuChart.js b/client/src/components/podCpuChart.tsx similarity index 89% rename from client/src/components/podCpuChart.js rename to client/src/components/podCpuChart.tsx index 584d061..331b91b 100644 --- a/client/src/components/podCpuChart.js +++ b/client/src/components/podCpuChart.tsx @@ -4,7 +4,7 @@ import Chart from './chart'; import LoadingChart from './loadingChart'; import {parseCpu, TO_ONE_CPU} from '../utils/unitHelpers'; -export default function PodCpuChart({items, metrics}) { +export default function PodCpuChart({items, metrics}: {items: any[], metrics: any[]}) { const totals = getPodCpuTotals(items, metrics); const decimals = totals && totals.used > 10 ? 1 : 2; @@ -25,7 +25,7 @@ export default function PodCpuChart({items, metrics}) { ); } -function getPodCpuTotals(pods, metrics) { +function getPodCpuTotals(pods: any[], metrics: any[]) { if (!pods || !metrics) return null; const used = _(metrics) diff --git a/client/src/components/podRamChart.js b/client/src/components/podRamChart.tsx similarity index 90% rename from client/src/components/podRamChart.js rename to client/src/components/podRamChart.tsx index 7c11220..6996b0c 100644 --- a/client/src/components/podRamChart.js +++ b/client/src/components/podRamChart.tsx @@ -4,7 +4,7 @@ import Chart from './chart'; import LoadingChart from './loadingChart'; import {parseRam, TO_GB} from '../utils/unitHelpers'; -export default function RamChart({items, metrics}) { +export default function RamChart({items, metrics}: {items: any[], metrics: any[]}) { const totals = getPodRamTotals(items, metrics); const decimals = totals && totals.used > 10 ? 1 : 2; @@ -27,7 +27,7 @@ export default function RamChart({items, metrics}) { ); } -export function getPodRamTotals(items, metrics) { +export function getPodRamTotals(items:any[], metrics:any[]) { if (!items || !metrics) return null; const metricsContainers = Object.values(metrics).flatMap(x => x.containers); diff --git a/client/src/components/replicaSetsPanel.js b/client/src/components/replicaSetsPanel.tsx similarity index 83% rename from client/src/components/replicaSetsPanel.js rename to client/src/components/replicaSetsPanel.tsx index 671a51b..1a99f2c 100644 --- a/client/src/components/replicaSetsPanel.js +++ b/client/src/components/replicaSetsPanel.tsx @@ -4,8 +4,20 @@ import Switch from 'react-switch'; import Base from './base'; import Sorter from './sorter'; import {MetadataHeaders, MetadataColumns, TableBody} from './listViewHelpers'; +import {TODO} from "../utils/types"; -export default class ReplicaSetsPanel extends Base { +interface ReplicaSetsPanelProps { + items: TODO[]; + sort: {field: string, direction: string, onSort: Function}; + filter: {}; + includeNamespace: boolean; +} + +interface ReplicaSetsPanelStates { + activeOnly: boolean; +} + +export default class ReplicaSetsPanel extends Base { state = {activeOnly: true}; render() { @@ -43,8 +55,8 @@ export default class ReplicaSetsPanel extends Base { items={filtered} filter={filter} sort={sort} - colSpan={5 + !!includeNamespace} - row={x => ( + colSpan={5 + Number(includeNamespace)} + row={(x: TODO) => ( !((!x.status.replicas || !x.spec.replicas) && activeOnly)); diff --git a/client/src/components/sorter.tsx b/client/src/components/sorter.tsx index f0855cd..d69218b 100644 --- a/client/src/components/sorter.tsx +++ b/client/src/components/sorter.tsx @@ -3,10 +3,11 @@ import React, {Component} from 'react'; import Base from './base'; import ArrowUpSvg from '../art/arrowUpSvg'; import ArrowDownSvg from '../art/arrowDownSvg'; +import {TODO} from "../utils/types"; interface SorterProps { sort: {field: string, direction: string, onSort: Function}; - field: string; + field: TODO; } interface SorterStates { diff --git a/client/src/utils/filterHelper.ts b/client/src/utils/filterHelper.ts index 03b297a..79cfafb 100644 --- a/client/src/utils/filterHelper.ts +++ b/client/src/utils/filterHelper.ts @@ -1,3 +1,4 @@ +import {TODO} from "./types"; export default function test(filter = '', ...values: string[]) { const value = filter.toLowerCase(); @@ -6,7 +7,7 @@ export default function test(filter = '', ...values: string[]) { .some(x => x.toLowerCase().includes(value)); } -export function filterByOwner(items: any[], owner: {[key: string]: any}) { +export function filterByOwner(items: TODO[], owner: TODO) { if (!items || !owner) return null; const {uid} = owner.metadata; @@ -19,7 +20,7 @@ export function filterByOwner(items: any[], owner: {[key: string]: any}) { }); } -export function filterByOwners(items: any[] , owners: {[key: string]: any}) { +export function filterByOwners(items: TODO[] , owners: TODO) { if (!items || !owners) return null; const uids = owners.map((x: any) => x.metadata.uid); diff --git a/client/src/utils/metricsHelpers.tsx b/client/src/utils/metricsHelpers.tsx index d449dcf..2209258 100644 --- a/client/src/utils/metricsHelpers.tsx +++ b/client/src/utils/metricsHelpers.tsx @@ -1,18 +1,19 @@ import _ from 'lodash'; import {parseRam, parseCpu} from './unitHelpers'; +import {TODO} from "./types"; -export default function getMetrics(items: any[], metrics: any[]) { +export default function getMetrics(items: TODO[], metrics: TODO) { if (!items || !metrics) return null; const names = _.map(items, x => x.metadata.name); - const filteredMetrics = metrics.filter(x => names.includes(x.metadata.name)); + const filteredMetrics = metrics.filter((x: TODO) => names.includes(x.metadata.name)); return _.keyBy(filteredMetrics, 'metadata.name'); } // Node helpers -export function getNodeResourceValue(node: {[key: string]: any}, pods: any[], resource: string, type: string, phases: string[]) { +export function getNodeResourceValue(node: TODO, pods: TODO, resource: string, type: string, phases: string[]) { if (!node || !pods) return null; return _(pods) @@ -21,23 +22,23 @@ export function getNodeResourceValue(node: {[key: string]: any}, pods: any[], re .sumBy(x => getPodResourceValue(x, resource, type)); } -export function getNodeResourcePercent(node: {[key: string]: any}, pods: any[], resource: string, type: string) { +export function getNodeResourcePercent(node: TODO, pods: TODO, resource: string, type: string) { const used = getNodeResourceValue(node, pods, resource, type, ['Running']); const available = getNodeResourcesAvailable(node, resource); return used && available ? used / available: null; } -export function getNodeUsagePercent(node: {[key: string]: any}, metrics: {[key: string]: any}, resource: string) { +export function getNodeUsagePercent(node: TODO, metrics: TODO, resource: string) { const used = getNodeUsage(node, metrics, resource); const available = getNodeResourcesAvailable(node, resource); return used && available ? used / available: null; } -export function getNodeResourcesAvailable(node: {[key: string]: any}, resource: string) { +export function getNodeResourcesAvailable(node: TODO, resource: string) { return node ? parse(resource, node.status.capacity) : null; } -export function getNodeUsage(node: {[key: string]: any}, metrics: {[key: string]: any}, resource: string) { +export function getNodeUsage(node: TODO, metrics: TODO, resource: string) { if (!node || !metrics) return null; const result = metrics[node.metadata.name]; @@ -46,20 +47,20 @@ export function getNodeUsage(node: {[key: string]: any}, metrics: {[key: string] // Pod helpers -export function getPodResourcePercent(item: any, metrics: {[key: string]: any}, resource: string, type: string) { +export function getPodResourcePercent(item: TODO, metrics: TODO, resource: string, type: string) { const actual = getPodUsage(item, metrics, resource); const request = getPodResourceValue(item, resource, type); return actual ? actual / request: null; } -export function getPodUsage(pod: {[key: string]: any}, metrics: {[key: string]: any}, resource: string) { +export function getPodUsage(pod: TODO, metrics: TODO, resource: string) { if (!pod || !metrics) return null; const metric = metrics[pod.metadata.name] || {}; - return _.sumBy(metric.containers, (x: any) => parse(resource, x.usage)); + return _.sumBy(metric.containers, (x: TODO) => parse(resource, x.usage)); } -export function getPodResourceValue(pod: {[key: string]: any}, resource: string, type: string) { +export function getPodResourceValue(pod: TODO, resource: string, type: string) { return _(pod.spec.containers) .filter(x => x.resources && x.resources[type]) .sumBy(x => parse(resource, x.resources[type])); diff --git a/client/src/utils/nodeHelpers.ts b/client/src/utils/nodeHelpers.ts index 45ef479..4c81586 100644 --- a/client/src/utils/nodeHelpers.ts +++ b/client/src/utils/nodeHelpers.ts @@ -1,8 +1,10 @@ +import {TODO} from "./types"; + /** * @param {*status* object with a status field (most likely the row from the TableBody)} * @returns the status text, as defined in https://kubernetes.io/docs/concepts/architecture/nodes/#condition */ -function getReadyStatus({status}: {status: {[key: string]: any}}) { +function getReadyStatus({status}: TODO) { if (!status.conditions) return null; const ready = status.conditions.find((y: {type: string}) => y.type === 'Ready'); return ready && ready.status; diff --git a/client/src/utils/types.ts b/client/src/utils/types.ts new file mode 100644 index 0000000..6f8b6d2 --- /dev/null +++ b/client/src/utils/types.ts @@ -0,0 +1 @@ +export type TODO = any; \ No newline at end of file