mirror of
https://github.com/skooner-k8s/skooner.git
synced 2026-08-28 19:27:15 +00:00
Initial work on responsive/mobile design
This commit is contained in:
+16
-5
@@ -1,10 +1,12 @@
|
||||
import React, {Component, Fragment} from 'react';
|
||||
import TitleBar from './components/titleBar';
|
||||
import Menu from './components/menu';
|
||||
import {Notifier} from './components/notifier';
|
||||
import Error from './components/error';
|
||||
import {registerHandler, initRouter} from './router';
|
||||
import log from './utils/log';
|
||||
import Button from './components/button';
|
||||
import LogoSvg from './art/logoSvg';
|
||||
import HamburgerSvg from './art/hamburgerSvg';
|
||||
|
||||
const genericError = (
|
||||
<div id='content'>
|
||||
@@ -24,19 +26,28 @@ class App extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {content, hasError} = this.state || {};
|
||||
const {content, hasError, menuToggled} = this.state || {};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TitleBar />
|
||||
<div id='titleBar'>
|
||||
<Button className='button_clear titleBar_hamburger' onClick={() => this.setState({menuToggled: !menuToggled})}>
|
||||
<HamburgerSvg className='svg_button' />
|
||||
</Button>
|
||||
|
||||
<a href='#!'>
|
||||
<LogoSvg className='titleBar_logo' />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id='shell'>
|
||||
<Menu />
|
||||
<Menu toggled={menuToggled} onClick={() => this.setState({menuToggled: false})} />
|
||||
<Fragment key={Date.now()}>
|
||||
{hasError ? genericError : content}
|
||||
</Fragment>
|
||||
<Notifier />
|
||||
</div>
|
||||
|
||||
<Notifier />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
const HamburgerSvg = props => (
|
||||
<svg version="1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M4 10h24a2 2 0 0 0 0-4H4a2 2 0 0 0 0 4zm24 4H4a2 2 0 0 0 0 4h24a2 2 0 0 0 0-4zm0 8H4a2 2 0 0 0 0 4h24a2 2 0 0 0 0-4z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default HamburgerSvg;
|
||||
@@ -5,63 +5,52 @@ import Field from './field';
|
||||
const ContainersPanel = ({spec}) => (
|
||||
<>
|
||||
{spec && _.map(spec.containers, item => (
|
||||
<div key={item.name} className='contentPanel'>
|
||||
<Field name='Container'>{item.name}</Field>
|
||||
<Field name='Image'>{item.image}</Field>
|
||||
<>
|
||||
<div className='contentPanel_header'>Container</div>
|
||||
<div key={item.name} className='contentPanel'>
|
||||
|
||||
{item.args && (
|
||||
<Field name='Args'>{item.args.join(' ')}</Field>
|
||||
)}
|
||||
<Field name='Container'>{item.name}</Field>
|
||||
<Field name='Image'>{item.image}</Field>
|
||||
|
||||
{item.env && (
|
||||
<Field name='Env'>
|
||||
{item.env.map(x => (
|
||||
<div key={x.name}>
|
||||
{x.name}: {getVariableValue(x)}
|
||||
</div>
|
||||
))}
|
||||
</Field>
|
||||
)}
|
||||
{item.args && (
|
||||
<Field name='Args'>{item.args.join(' ')}</Field>
|
||||
)}
|
||||
|
||||
{item.resources && item.resources.requests && (
|
||||
<>
|
||||
<Field name='Cpu Request'>{item.resources.requests.cpu}</Field>
|
||||
<Field name='Memory Request'>{item.resources.requests.memory}</Field>
|
||||
</>
|
||||
)}
|
||||
{item.env && (
|
||||
<Field name='Env'>
|
||||
{item.env.map(x => (
|
||||
<div key={x.name}>
|
||||
{x.name}: {getVariableValue(x)}
|
||||
</div>
|
||||
))}
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{item.resources && item.resources.limits && (
|
||||
<>
|
||||
<Field name='Cpu Limit'>{item.resources.limits.cpu}</Field>
|
||||
<Field name='Memory Limit'>{item.resources.limits.memory}</Field>
|
||||
</>
|
||||
)}
|
||||
{item.resources && item.resources.requests && (
|
||||
<>
|
||||
<Field name='Cpu Request'>{item.resources.requests.cpu}</Field>
|
||||
<Field name='Memory Request'>{item.resources.requests.memory}</Field>
|
||||
</>
|
||||
)}
|
||||
|
||||
{item.ports && (
|
||||
<Field name='Ports'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Container Port</th>
|
||||
<th>Host Port</th>
|
||||
<th>Protocol</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{item.ports.map((x, i) => (
|
||||
<tr key={i}>
|
||||
<td>{x.name}</td>
|
||||
<td>{x.containerPort}</td>
|
||||
<td>{x.hostPort}</td>
|
||||
<td>{x.protocol}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Field>
|
||||
)}
|
||||
</div>
|
||||
{item.resources && item.resources.limits && (
|
||||
<>
|
||||
<Field name='Cpu Limit'>{item.resources.limits.cpu}</Field>
|
||||
<Field name='Memory Limit'>{item.resources.limits.memory}</Field>
|
||||
</>
|
||||
)}
|
||||
|
||||
{item.ports && (
|
||||
<Field name='Ports'>
|
||||
{item.ports.map((x, i) => (
|
||||
<div key={i}>
|
||||
{[x.name, x.containerPort, x.hostPort, x.protocol].filter(y => !!y).join(' • ')}
|
||||
</div>
|
||||
))}
|
||||
</Field>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -8,10 +8,10 @@ import Sorter, {sortByDate} from './sorter';
|
||||
export default function EventsPanel({items, filter, shortList, sort}) {
|
||||
return (
|
||||
<div className='contentPanel'>
|
||||
<table>
|
||||
<table className='wrapped'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className='th_icon'>
|
||||
<th className='th_icon optional_medium'>
|
||||
<Sorter field='involvedObject.kind' sort={sort}>Type</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
@@ -22,7 +22,7 @@ export default function EventsPanel({items, filter, shortList, sort}) {
|
||||
<Sorter field={sortByName} sort={sort}>Name</Sorter>
|
||||
</th>
|
||||
)}
|
||||
<th>
|
||||
<th className='optional_small'>
|
||||
<Sorter field='reason' sort={sort}>Reason</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
@@ -38,7 +38,7 @@ export default function EventsPanel({items, filter, shortList, sort}) {
|
||||
sort={sort}
|
||||
row={x => (
|
||||
<tr key={x.metadata.name}>
|
||||
<td className='td_icon'>
|
||||
<td className='td_icon optional_medium'>
|
||||
<ResourceSvg
|
||||
resource={x.involvedObject.kind}
|
||||
className={getTypeClass(x.type)}
|
||||
@@ -49,7 +49,7 @@ export default function EventsPanel({items, filter, shortList, sort}) {
|
||||
{!shortList && (
|
||||
<td>{x.involvedObject.namespace}:{x.involvedObject.name}</td>
|
||||
)}
|
||||
<td>{x.reason}</td>
|
||||
<td className='optional_small'>{x.reason}</td>
|
||||
<td>{x.message}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@import '../scss/settings.scss';
|
||||
|
||||
.field_set {
|
||||
margin: 20px 0;
|
||||
@@ -15,3 +16,20 @@
|
||||
margin-left: 20px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: $media-small) {
|
||||
.field_set {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.field_name {
|
||||
text-align: left;
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
.field_value {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import Cancel from '../art/cancelSvg';
|
||||
const InputFilter = ({filter, onChange}) => (
|
||||
<>
|
||||
<input
|
||||
className='header_filter header_fill'
|
||||
className='header_filter header_fill optional_xsmall'
|
||||
type='text'
|
||||
placeholder='type to filter'
|
||||
value={filter}
|
||||
@@ -17,9 +17,9 @@ const InputFilter = ({filter, onChange}) => (
|
||||
/>
|
||||
|
||||
{filter.length === 0 ? (
|
||||
<Search />
|
||||
<Search className='optional_xsmall' />
|
||||
) : (
|
||||
<Cancel className='svg_button svg_error' onClick={() => onChange('')} />
|
||||
<Cancel className='svg_button svg_error optional_xsmall' onClick={() => onChange('')} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -31,18 +31,18 @@ export function TableBody({items, filter, colSpan, sort, row}) {
|
||||
export function MetadataHeaders({includeNamespace, sort}) {
|
||||
return (
|
||||
<>
|
||||
<th className='th_icon'>
|
||||
<th className='th_icon optional_small'>
|
||||
<Sorter field='kind' sort={sort}>Type</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
<Sorter field='metadata.name' sort={sort}>Name</Sorter>
|
||||
</th>
|
||||
{includeNamespace && (
|
||||
<th>
|
||||
<th className='optional_medium'>
|
||||
<Sorter field='metadata.namespace' sort={sort}>Namespace</Sorter>
|
||||
</th>
|
||||
)}
|
||||
<th>
|
||||
<th className='optional_medium'>
|
||||
<Sorter field={sortByDate} sort={sort}>Age</Sorter>
|
||||
</th>
|
||||
</>
|
||||
@@ -52,7 +52,7 @@ export function MetadataHeaders({includeNamespace, sort}) {
|
||||
export function MetadataColumns({item, href, includeNamespace, isError}) {
|
||||
return (
|
||||
<>
|
||||
<td className='td_icon'>
|
||||
<td className='td_icon optional_small'>
|
||||
<ResourceSvg
|
||||
resource={item.kind}
|
||||
className={isError ? 'svg_error' : undefined}
|
||||
@@ -64,12 +64,12 @@ export function MetadataColumns({item, href, includeNamespace, isError}) {
|
||||
</td>
|
||||
|
||||
{includeNamespace && (
|
||||
<td>
|
||||
<td className='optional_medium'>
|
||||
{item.metadata.namespace || 'All Namespaces'}
|
||||
</td>
|
||||
)}
|
||||
|
||||
<td>
|
||||
<td className='optional_medium'>
|
||||
{moment(item.metadata.creationTimestamp).fromNow(true)}
|
||||
</td>
|
||||
</>
|
||||
|
||||
@@ -9,50 +9,51 @@ import AddSvg from '../art/addSvg';
|
||||
|
||||
export default class Menu extends Base {
|
||||
render() {
|
||||
const {onClick, toggled} = this.props;
|
||||
const {showAdd} = this.state || {};
|
||||
|
||||
return (
|
||||
<div id='menu'>
|
||||
<div id='menu' className={toggled ? 'menu_toggled' : undefined}>
|
||||
|
||||
{/* Cluster */}
|
||||
<Group>
|
||||
<MenuItem title='Cluster' path='' resource='Logo' />
|
||||
<MenuItem title='Nodes' path='node' resource='Node' />
|
||||
<MenuItem title='Namespaces' path='namespace' resource='Namespace' />
|
||||
<MenuItem title='Cluster' path='' resource='Logo' onClick={onClick} />
|
||||
<MenuItem title='Nodes' path='node' resource='Node' onClick={onClick} />
|
||||
<MenuItem title='Namespaces' path='namespace' resource='Namespace' onClick={onClick} />
|
||||
</Group>
|
||||
|
||||
{/* Workloads */}
|
||||
<Group>
|
||||
<MenuItem title='Workloads' path='workload' resource='Deployment' />
|
||||
<MenuItem title='Services' path='service' resource='Service' />
|
||||
<MenuItem title='Replicas' path='replicaset' resource='ReplicaSet' />
|
||||
<MenuItem title='Pods' path='pod' resource='Pod' />
|
||||
<MenuItem title='Ingresses' path='ingress' resource='Ingress' />
|
||||
<MenuItem title='Config' path='configmap' resource='ConfigMap' />
|
||||
<MenuItem title='Workloads' path='workload' resource='Deployment' onClick={onClick} />
|
||||
<MenuItem title='Services' path='service' resource='Service' onClick={onClick} />
|
||||
<MenuItem title='Replicas' path='replicaset' resource='ReplicaSet' onClick={onClick} />
|
||||
<MenuItem title='Pods' path='pod' resource='Pod' onClick={onClick} />
|
||||
<MenuItem title='Ingresses' path='ingress' resource='Ingress' onClick={onClick} />
|
||||
<MenuItem title='Config' path='configmap' resource='ConfigMap' onClick={onClick} />
|
||||
</Group>
|
||||
|
||||
{/* Storage */}
|
||||
<Group>
|
||||
<MenuItem title='Storage' path='storageclass' resource='StorageClass' />
|
||||
<MenuItem title='Volumes' path='persistentvolume' resource='PersistentVolume' />
|
||||
<MenuItem title='Claims' path='persistentvolumeclaim' resource='PersistentVolumeClaim' />
|
||||
<MenuItem title='Storage' path='storageclass' resource='StorageClass' onClick={onClick} />
|
||||
<MenuItem title='Volumes' path='persistentvolume' resource='PersistentVolume' onClick={onClick} />
|
||||
<MenuItem title='Claims' path='persistentvolumeclaim' resource='PersistentVolumeClaim' onClick={onClick} />
|
||||
</Group>
|
||||
|
||||
{/* Security */}
|
||||
<Group>
|
||||
<MenuItem title='Accounts' path='serviceaccount' resource='ServiceAccount' />
|
||||
<MenuItem title='Roles' path='role' resource='Role' additionalPaths={['clusterrole']} />
|
||||
<MenuItem title='Bindings' path='rolebinding' resource='Role' additionalPaths={['clusterrolebinding']} />
|
||||
<MenuItem title='Secrets' path='secret' resource='Secret' />
|
||||
<MenuItem title='Accounts' path='serviceaccount' resource='ServiceAccount' onClick={onClick} />
|
||||
<MenuItem title='Roles' path='role' resource='Role' additionalPaths={['clusterrole']} onClick={onClick} />
|
||||
<MenuItem title='Bindings' path='rolebinding' resource='Role' additionalPaths={['clusterrolebinding']} onClick={onClick} />
|
||||
<MenuItem title='Secrets' path='secret' resource='Secret' onClick={onClick} />
|
||||
</Group>
|
||||
|
||||
<Group>
|
||||
<MenuItem title='Profile' path='account' resource='User' />
|
||||
<MenuItem title='Profile' path='account' resource='User' onClick={onClick} />
|
||||
</Group>
|
||||
|
||||
<Group>
|
||||
<div className='menu_itemApply'>
|
||||
<button className='menu_item button_clear' onClick={() => this.setState({showAdd: true})}>
|
||||
<button className='menu_item button_clear' onClick={() => { this.setState({showAdd: true}); onClick(); }}>
|
||||
<AddSvg className='menu_icon' />
|
||||
<div className='menu_title'>Apply</div>
|
||||
</button>
|
||||
@@ -75,10 +76,10 @@ function MenuItem(item) {
|
||||
const currentPath = getRootPath();
|
||||
const paths = getPaths(item);
|
||||
const className = paths.includes(currentPath) ? 'menu_item menu_itemSelected' : 'menu_item';
|
||||
const {path, title, resource} = item;
|
||||
const {path, title, resource, onClick} = item;
|
||||
|
||||
return (
|
||||
<a href={`#!${path}`} title={title} className={className}>
|
||||
<a href={`#!${path}`} title={title} className={className} onClick={onClick}>
|
||||
<ResourceSvg className='menu_icon' resource={resource} />
|
||||
<span className='menu_title'>{title}</span>
|
||||
</a>
|
||||
|
||||
@@ -19,26 +19,6 @@
|
||||
transition: transform 0.33s;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(1) {
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(2) {
|
||||
transition-delay: 0.2s;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(3) {
|
||||
transition-delay: 0.3s;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(4) {
|
||||
transition-delay: 0.4s;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(5) {
|
||||
transition-delay: 0.5s;
|
||||
}
|
||||
|
||||
.menu_subMenuSelected {
|
||||
max-height: 1000px;
|
||||
}
|
||||
@@ -78,3 +58,46 @@ a.menu_itemSelected, a.menu_itemSelected:visited {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(1) {
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(2) {
|
||||
transition-delay: 0.2s;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(3) {
|
||||
transition-delay: 0.3s;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(4) {
|
||||
transition-delay: 0.4s;
|
||||
}
|
||||
|
||||
.menu_subMenu a:nth-child(5) {
|
||||
transition-delay: 0.5s;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-small) and (min-width: $media-xsmall) {
|
||||
button.menu_item, a.menu_item, a.menu_item:visited {
|
||||
svg {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.menu_title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-xsmall) {
|
||||
.menu_subMenu {
|
||||
max-height: initial;
|
||||
}
|
||||
|
||||
.menu_subMenu a {
|
||||
transform: initial;
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,8 @@ export default class NodesPanel extends Base {
|
||||
<thead>
|
||||
<tr>
|
||||
<MetadataHeaders sort={sort} />
|
||||
<th>Labels</th>
|
||||
<th><Sorter field={getReadyStatus} sort={sort}>Ready</Sorter></th>
|
||||
<th className='optional_medium'>Labels</th>
|
||||
<th className='optional_small'><Sorter field={getReadyStatus} sort={sort}>Ready</Sorter></th>
|
||||
<th><Sorter field={this.sortByCpu} sort={sort}>Cpu</Sorter></th>
|
||||
<th><Sorter field={this.sortByRam} sort={sort}>Ram</Sorter></th>
|
||||
</tr>
|
||||
@@ -47,8 +47,8 @@ export default class NodesPanel extends Base {
|
||||
<TableBody items={items} filter={filter} sort={sort} colSpan='8' row={x => (
|
||||
<tr key={x.metadata.uid}>
|
||||
<MetadataColumns item={x} href={`#!node/${x.metadata.name}`} />
|
||||
<td>{objectMap(x.metadata.labels)}</td>
|
||||
<td>{getReadyStatus(x)}</td>
|
||||
<td className='optional_medium'>{objectMap(x.metadata.labels)}</td>
|
||||
<td className='optional_small'>{getReadyStatus(x)}</td>
|
||||
<td><CpuPercent item={x} metrics={metrics} /></td>
|
||||
<td><RamPercent item={x} metrics={metrics} /></td>
|
||||
</tr>
|
||||
|
||||
@@ -36,11 +36,9 @@ export default class PodsPanel extends Base {
|
||||
<tr>
|
||||
<MetadataHeaders sort={sort} includeNamespace={!skipNamespace} />
|
||||
{!skipNodeName && (
|
||||
<th><Sorter field='spec.nodeName' sort={sort}>Node</Sorter></th>
|
||||
<th className='optional_small'><Sorter field='spec.nodeName' sort={sort}>Node</Sorter></th>
|
||||
)}
|
||||
<th><Sorter field='status.phase' sort={sort}>Status</Sorter></th>
|
||||
<th><Sorter field={getContainerCount} sort={sort}>Containers</Sorter></th>
|
||||
<th><Sorter field={getRestartCount} sort={sort}>Restarts</Sorter></th>
|
||||
<th className='optional_medium'><Sorter field={getRestartCount} sort={sort}>Restarts</Sorter></th>
|
||||
<th>
|
||||
<Sorter field={this.sortByCpu} sort={sort}>
|
||||
Cpu
|
||||
@@ -68,10 +66,8 @@ export default class PodsPanel extends Base {
|
||||
includeNamespace={!skipNamespace}
|
||||
href={`#!pod/${x.metadata.namespace}/${x.metadata.name}`}
|
||||
/>
|
||||
{!skipNodeName && <td>{x.spec.nodeName}</td>}
|
||||
<td>{x.status.phase}</td>
|
||||
<td>{getContainerCount(x)}</td>
|
||||
<td>{getRestartCount(x)}</td>
|
||||
{!skipNodeName && <td className='optional_small'>{x.spec.nodeName}</td>}
|
||||
<td className='optional_medium'>{getRestartCount(x)}</td>
|
||||
<td><Cpu item={x} metrics={metrics} /></td>
|
||||
<td><Ram item={x} metrics={metrics} /></td>
|
||||
</tr>
|
||||
@@ -82,10 +78,6 @@ export default class PodsPanel extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
function getContainerCount({spec}) {
|
||||
return spec.containers ? spec.containers.length : 0;
|
||||
}
|
||||
|
||||
function getRestartCount({status}) {
|
||||
return _.sumBy(status.containerStatuses, 'restartCount');
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ export default class ReplicaSetsPanel extends Base {
|
||||
<thead>
|
||||
<tr>
|
||||
<MetadataHeaders sort={sort} includeNamespace={includeNamespace} />
|
||||
<th>
|
||||
<th className='optional_small'>
|
||||
<Sorter field='status.observedGeneration' sort={sort}>Generations</Sorter>
|
||||
</th>
|
||||
<th className='replicaSetsPanel_replicas'>
|
||||
<Sorter field='spec.replicas' sort={sort}>Replicas</Sorter>
|
||||
<label className='replicaSetsPanel_switch'>
|
||||
<label className='replicaSetsPanel_switch optional_xsmall'>
|
||||
<Switch
|
||||
checked={activeOnly}
|
||||
onChange={x => this.setState({activeOnly: x})}
|
||||
@@ -51,7 +51,7 @@ export default class ReplicaSetsPanel extends Base {
|
||||
includeNamespace={includeNamespace}
|
||||
href={`#!replicaset/${x.metadata.namespace}/${x.metadata.name}`}
|
||||
/>
|
||||
<td>{x.status.observedGeneration}</td>
|
||||
<td className='optional_small'>{x.status.observedGeneration}</td>
|
||||
<td>{x.spec.replicas} / {x.status.replicas}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
@@ -18,12 +18,12 @@ export default class ScaleButton extends Base {
|
||||
|
||||
{scaleInfo && (
|
||||
<Modal isOpen={true} className='modal_modal' overlayClassName='modal_overlay' onRequestClose={() => this.close()}>
|
||||
<div>
|
||||
<div className='scaleButton_label'>Desired count</div>
|
||||
<div className='scaleButton'>
|
||||
<div className='scaleButton_label'>Desired Count</div>
|
||||
<input
|
||||
type='number'
|
||||
className='scaleButton_input'
|
||||
min="0"
|
||||
min='0'
|
||||
defaultValue={scaleInfo.spec.replicas || 0}
|
||||
onChange={x => this.setState({value: parseInt(x.target.value, 10)})}
|
||||
/>
|
||||
@@ -57,8 +57,8 @@ export default class ScaleButton extends Base {
|
||||
}
|
||||
|
||||
close() {
|
||||
// Use setTimeout to prevent the following React warning:
|
||||
// "Warning: Can't perform a React state update on an unmounted component."
|
||||
// Use setTimeout to prevent the following React warning:
|
||||
// "Warning: Can't perform a React state update on an unmounted component."
|
||||
setTimeout(() => this.setState({scaleInfo: null}), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
@import '../scss/settings.scss';
|
||||
|
||||
.scaleButton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.scaleButton_label {
|
||||
color: $color-light;
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import './titleBar.scss';
|
||||
import React from 'react';
|
||||
import LogoSvg from '../art/logoSvg';
|
||||
|
||||
const TitleBar = () => (
|
||||
<div id='titleBar'>
|
||||
<a href='#!'>
|
||||
<LogoSvg className='titleBar_logo' />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
export default TitleBar;
|
||||
@@ -1,17 +0,0 @@
|
||||
@import '../scss/settings.scss';
|
||||
|
||||
#titleBar {
|
||||
height: 55px;
|
||||
background-color: $color-accent;
|
||||
color: $color-white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
font-size: $font-size-large;
|
||||
justify-content: space-between;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.titleBar_addButton {
|
||||
fill: $color-lightest;
|
||||
}
|
||||
@@ -52,7 +52,15 @@ $ct-series-colors: (
|
||||
.chart_donutLabel {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
font-size: $font-size-large;
|
||||
}
|
||||
|
||||
.charts_itemLabel {
|
||||
font-size: $font-size-medium;
|
||||
color: $color-light;
|
||||
margin-top: 8px;
|
||||
text-align: right;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.chart_innerLabel {
|
||||
@@ -64,3 +72,13 @@ $ct-series-colors: (
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-small) {
|
||||
.chart_donutLabel {
|
||||
font-size: $font-size;
|
||||
}
|
||||
|
||||
.charts_itemLabel {
|
||||
font-size: $font-size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
.contentPanel {
|
||||
box-shadow: 0 0 5px 0 $color-light;
|
||||
margin: 35px 0;
|
||||
padding: 20px;
|
||||
padding: 0 20px 10px 20px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.contentPanel_header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.contentPanel_label {
|
||||
margin-right: 5px;
|
||||
}
|
||||
@@ -15,3 +19,24 @@
|
||||
color: $color-error;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: $media-xsmall) {
|
||||
.contentPanel {
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.contentPanel_header {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 25px;
|
||||
padding: 5px;
|
||||
font-size: $font-size-large;
|
||||
text-align: right;
|
||||
text-transform: uppercase;
|
||||
color: $color-dark;
|
||||
background-color: $color-lightest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
|
||||
#header > .button_headerAction {
|
||||
transform: translateY(-50px);
|
||||
transform: translateY(-80px);
|
||||
transition-duration: $animation-length;
|
||||
transition-property: transform;
|
||||
}
|
||||
@@ -89,3 +89,10 @@ input[type="text"].header_filter {
|
||||
#header .button_headerAction:nth-child(9) {
|
||||
transition-delay: 0.9s;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: $media-xsmall) {
|
||||
.header_label {
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,13 @@
|
||||
|
||||
.modal_overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
|
||||
.modal_modal {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
@@ -12,17 +21,24 @@
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.modal_overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
|
||||
.modal_actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-xsmall) {
|
||||
.modal_overlay {
|
||||
z-index: 300;
|
||||
}
|
||||
|
||||
.modal_modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
transform: initial;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ thead > tr > th {
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
text-align: left;
|
||||
padding: 1rem;
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid $color-light;
|
||||
}
|
||||
|
||||
@@ -30,3 +30,23 @@ td {
|
||||
margin-top: -2px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-xsmall) {
|
||||
thead > tr > th {
|
||||
padding: 0.75rem 0.25rem;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.75rem 0.25rem;
|
||||
}
|
||||
|
||||
.wrapped > thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wrapped > tbody > tr {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ html {
|
||||
font-size: $font-size;
|
||||
font-weight: 300;
|
||||
color: $color-font;
|
||||
min-width: 320px;
|
||||
}
|
||||
|
||||
#root {
|
||||
@@ -34,11 +35,6 @@ html {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.titleBar_logo {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
#shell {
|
||||
display: flex;
|
||||
min-height: calc(100vh - 55px);
|
||||
@@ -55,3 +51,81 @@ html {
|
||||
flex: 1;
|
||||
width: calc(100vw - 72px);
|
||||
}
|
||||
|
||||
#titleBar {
|
||||
height: 55px;
|
||||
background-color: $color-accent;
|
||||
color: $color-white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
font-size: $font-size-large;
|
||||
justify-content: space-between;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.titleBar_logo {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
button.titleBar_hamburger {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.titleBar_hamburger > svg {
|
||||
fill: $color-white;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-medium) {
|
||||
.optional_medium {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-small) {
|
||||
.optional_small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#menu {
|
||||
width: 44px
|
||||
}
|
||||
|
||||
#content {
|
||||
width: calc(100vw - 44px);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-xsmall) {
|
||||
#content {
|
||||
padding: 0 15px;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.optional_xsmall {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#menu {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 200;
|
||||
width: 72px;
|
||||
overflow-y: scroll;
|
||||
box-shadow: 0 0 5px 0 $color-light;
|
||||
|
||||
transform: translateX(-80px);
|
||||
transition: transform 0.33s;
|
||||
}
|
||||
|
||||
#menu.menu_toggled {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
button.titleBar_hamburger {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ $animation-length: .25s;
|
||||
$font: sans-serif;
|
||||
$font-size-small: 6pt;
|
||||
$font-size: 10pt;
|
||||
$font-size-medium: 16pt;
|
||||
$font-size-medium: 13pt;
|
||||
$font-size-large: 16pt;
|
||||
$font-size-xlarge: 100px;
|
||||
|
||||
$media-xsmall: 606px;
|
||||
$media-small: 812px;
|
||||
$media-medium: 1024px;
|
||||
@@ -44,7 +44,7 @@ export default class Auth extends Base {
|
||||
<div className='auth'>
|
||||
{!useTokenLogin ? <Loading /> : (
|
||||
<>
|
||||
<LogoSvg />
|
||||
<LogoSvg className='optional_small' />
|
||||
<input
|
||||
type='password'
|
||||
className='auth_input'
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
.auth_input {
|
||||
font-size: $font-size-medium;
|
||||
font-size: $font-size-large;
|
||||
padding: 0.5rem;
|
||||
width: 400px;
|
||||
margin: 0 5px;
|
||||
@@ -22,5 +22,15 @@
|
||||
|
||||
.auth_button {
|
||||
border: solid 1px $color-accent;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
font-size: $font-size-large;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-small) {
|
||||
.auth {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.auth_input {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ export default class ClusterRole extends Base {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='contentPanel_header'>Rules</div>
|
||||
<div className='contentPanel'>
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
@@ -61,20 +61,21 @@ export default class ClusterRoleBinding extends Base {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='contentPanel_header'>Subjects</div>
|
||||
<div className='contentPanel'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className='th_icon'>
|
||||
<th className='th_icon optional_small'>
|
||||
<Sorter sort={sort} field='kind'>Type</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
<Sorter sort={sort} field='name'>Name</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
<th className='optional_small'>
|
||||
<Sorter sort={sort} field='namespace'>Namespace</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
<th className='optional_small'>
|
||||
<Sorter sort={sort} field='apiGroup'>Api Group</Sorter>
|
||||
</th>
|
||||
</tr>
|
||||
@@ -82,15 +83,15 @@ export default class ClusterRoleBinding extends Base {
|
||||
|
||||
<TableBody items={subjects} colSpan='4' sort={sort} row={x => (
|
||||
<tr key={x.name}>
|
||||
<td>
|
||||
<td className='td_icon optional_small'>
|
||||
<ResourceSvg resource={x.kind} />
|
||||
<div className='td_iconLabel'>{x.kind}</div>
|
||||
</td>
|
||||
<td>
|
||||
{x.kind === 'ServiceAccount' ? (<a href={getSubjectHref(x)}>{x.name}</a>) : x.name}
|
||||
</td>
|
||||
<td>{x.namespace}</td>
|
||||
<td>{x.apiGroup}</td>
|
||||
<td className='optional_small'>{x.namespace}</td>
|
||||
<td className='optional_small'>{x.apiGroup}</td>
|
||||
</tr>
|
||||
)} />
|
||||
</table>
|
||||
|
||||
@@ -82,6 +82,7 @@ export default class CronJob extends Base {
|
||||
|
||||
{/* TODO: this actually need to be a list of jobs */}
|
||||
|
||||
<div className='contentPanel_header'>Pods</div>
|
||||
<PodsPanel
|
||||
items={filteredPods}
|
||||
sort={podsSort}
|
||||
@@ -89,6 +90,7 @@ export default class CronJob extends Base {
|
||||
skipNamespace={true}
|
||||
/>
|
||||
|
||||
<div className='contentPanel_header'>Events</div>
|
||||
<EventsPanel
|
||||
sort={eventsSort}
|
||||
shortList={true}
|
||||
|
||||
@@ -85,7 +85,11 @@ export default class DaemonSet extends Base {
|
||||
</div>
|
||||
|
||||
<ContainersPanel spec={item && item.spec.template.spec} />
|
||||
|
||||
<div className='contentPanel_header'>Pods</div>
|
||||
<PodsPanel items={filteredPods} sort={podsSort} metrics={filteredMetrics} skipNamespace={true} />
|
||||
|
||||
<div className='contentPanel_header'>Events</div>
|
||||
<EventsPanel sort={eventsSort} shortList={true} items={filteredEvents} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -94,12 +94,14 @@ export default class Deployment extends Base {
|
||||
|
||||
<ContainersPanel spec={item && item.spec.template.spec} />
|
||||
|
||||
<div className='contentPanel_header'>Replica Sets</div>
|
||||
<ReplicaSetsPanel
|
||||
items={filteredReplicaSets}
|
||||
sort={replicaSetsSort}
|
||||
includeNamespace={false}
|
||||
/>
|
||||
|
||||
<div className='contentPanel_header'>Pods</div>
|
||||
<PodsPanel
|
||||
items={filteredPods}
|
||||
sort={podsSort}
|
||||
@@ -107,6 +109,7 @@ export default class Deployment extends Base {
|
||||
skipNamespace={true}
|
||||
/>
|
||||
|
||||
<div className='contentPanel_header'>Events</div>
|
||||
<EventsPanel
|
||||
shortList={true}
|
||||
sort={eventsSort}
|
||||
|
||||
@@ -31,4 +31,13 @@
|
||||
|
||||
.editorModal_spacer {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $media-xsmall) {
|
||||
.editorModal {
|
||||
width: initial;
|
||||
max-width: initial;
|
||||
max-height: initial;
|
||||
height: calc(100vh - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ export default class Ingress extends Base {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='contentPanel_header'>Rules</div>
|
||||
<div className='contentPanel'>
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
@@ -85,6 +85,7 @@ export default class Job extends Base {
|
||||
|
||||
<ContainersPanel spec={item && item.spec.template.spec} />
|
||||
|
||||
<div className='contentPanel_header'>Pods</div>
|
||||
<PodsPanel
|
||||
items={filteredPods}
|
||||
sort={podsSort}
|
||||
@@ -92,6 +93,7 @@ export default class Job extends Base {
|
||||
skipNamespace={true}
|
||||
/>
|
||||
|
||||
<div className='contentPanel_header'>Events</div>
|
||||
<EventsPanel
|
||||
shortList={true}
|
||||
sort={eventsSort}
|
||||
|
||||
@@ -43,6 +43,7 @@ export default class Namespace extends Base {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='contentPanel_header'>Events</div>
|
||||
<EventsPanel items={events} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -71,9 +71,9 @@ export default class Node extends Base {
|
||||
<tr>
|
||||
<th>Condition</th>
|
||||
<th>Status</th>
|
||||
<th>Transition</th>
|
||||
<th>Reason</th>
|
||||
<th>Message</th>
|
||||
<th className='optional_medium'>Transition</th>
|
||||
<th className='optional_small'>Reason</th>
|
||||
<th className='optional_small'>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -81,9 +81,9 @@ export default class Node extends Base {
|
||||
<tr key={x.type}>
|
||||
<td>{x.type}</td>
|
||||
<td>{x.status}</td>
|
||||
<td>{moment(x.lastTransitionTime).fromNow()}</td>
|
||||
<td>{x.reason}</td>
|
||||
<td>{x.message}</td>
|
||||
<td className='optional_medium'>{moment(x.lastTransitionTime).fromNow()}</td>
|
||||
<td className='optional_small'>{x.reason}</td>
|
||||
<td className='optional_small'>{x.message}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@@ -91,7 +91,7 @@ export default class Node extends Base {
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
<div className='contentPanel_header'>Pods</div>
|
||||
<PodsPanel
|
||||
items={filteredPods}
|
||||
sort={podsSort}
|
||||
|
||||
@@ -68,7 +68,7 @@ function CpuTotalsChart({items, metrics}) {
|
||||
) : (
|
||||
<LoadingChart />
|
||||
)}
|
||||
<div className='charts_itemLabel'>Cpu Cores Used</div>
|
||||
<div className='charts_itemLabel'>Cores</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -78,11 +78,11 @@ function RamTotalsChart({items, metrics}) {
|
||||
return (
|
||||
<div className='charts_item'>
|
||||
{totals ? (
|
||||
<Chart used={totals.used} available={totals.available} />
|
||||
<Chart used={totals.used} usedSuffix='Gb' available={totals.available} availableSuffix='Gb' />
|
||||
) : (
|
||||
<LoadingChart />
|
||||
)}
|
||||
<div className='charts_itemLabel'>GB Ram Used</div>
|
||||
<div className='charts_itemLabel'>Ram</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,18 +39,18 @@ export default class PersistentVolumeClaims extends Base {
|
||||
<thead>
|
||||
<tr>
|
||||
<MetadataHeaders includeNamespace={true} sort={sort} />
|
||||
<th>
|
||||
<th className='optional_small'>
|
||||
<Sorter field='status.phase' sort={sort}>Status</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
<th className='optional_medium'>
|
||||
<Sorter field='spec.storageClassName' sort={sort}>Class Name</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
<Sorter field={getAccessModes} sort={sort}>Access Modes</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
<th className='optional_medium'>
|
||||
<Sorter field='spec.volumeName' sort={sort}>Volume</Sorter>
|
||||
</th>
|
||||
<th className='optional_medium'>
|
||||
<Sorter field={getAccessModes} sort={sort}>Access Modes</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
<Sorter field={getDiskSpace} sort={sort}>Capacity</Sorter>
|
||||
</th>
|
||||
@@ -64,10 +64,10 @@ export default class PersistentVolumeClaims extends Base {
|
||||
includeNamespace={true}
|
||||
href={`#!persistentvolumeclaim/${x.metadata.namespace}/${x.metadata.name}`}
|
||||
/>
|
||||
<td>{x.status.phase}</td>
|
||||
<td>{x.spec.storageClassName}</td>
|
||||
<td>{getAccessModes(x, ' • ')}</td>
|
||||
<td>{x.spec.volumeName}</td>
|
||||
<td className='optional_small'>{x.status.phase}</td>
|
||||
<td className='optional_medium'>{x.spec.storageClassName}</td>
|
||||
<td className='optional_medium'>{x.spec.volumeName}</td>
|
||||
<td className='optional_medium'>{getAccessModes(x, ' • ')}</td>
|
||||
<td>{x.status.capacity && x.status.capacity.storage}</td>
|
||||
</tr>
|
||||
)} />
|
||||
|
||||
@@ -36,7 +36,7 @@ export default class PersistentVolumes extends Base {
|
||||
<thead>
|
||||
<tr>
|
||||
<MetadataHeaders sort={sort} />
|
||||
<th>
|
||||
<th className='optional_small'>
|
||||
<Sorter field='status.phase' sort={sort}>Status</Sorter>
|
||||
</th>
|
||||
<th>
|
||||
@@ -51,7 +51,7 @@ export default class PersistentVolumes extends Base {
|
||||
item={x}
|
||||
href={`#!persistentvolume/${x.metadata.name}`}
|
||||
/>
|
||||
<td>{x.status.phase}</td>
|
||||
<td className='optional_small'>{x.status.phase}</td>
|
||||
<td>{x.spec.capacity && x.spec.capacity.storage}</td>
|
||||
</tr>
|
||||
)} />
|
||||
|
||||
@@ -117,6 +117,8 @@ export default class Pod extends Base {
|
||||
</div>
|
||||
|
||||
<ContainersPanel spec={item && item.spec} />
|
||||
|
||||
<div className='contentPanel_header'>Events</div>
|
||||
<EventsPanel items={filteredEvents} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -93,6 +93,7 @@ export default class ReplicaSet extends Base {
|
||||
|
||||
<ContainersPanel spec={item && item.spec.template.spec} />
|
||||
|
||||
<div className='contentPanel_header'>Pods</div>
|
||||
<PodsPanel
|
||||
items={filteredPods}
|
||||
sort={podsSort}
|
||||
@@ -100,6 +101,7 @@ export default class ReplicaSet extends Base {
|
||||
skipNamespace={true}
|
||||
/>
|
||||
|
||||
<div className='contentPanel_header'>Events</div>
|
||||
<EventsPanel
|
||||
shortList={true}
|
||||
sort={eventsSort}
|
||||
|
||||
@@ -48,6 +48,7 @@ export default class Role extends Base {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='contentPanel_header'>Rules</div>
|
||||
<div className='contentPanel'>
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
@@ -59,28 +59,31 @@ export default class RoleBinding extends Base {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='contentPanel_header'>Subjects</div>
|
||||
<div className='contentPanel'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className='th_icon'><Sorter filed='kind' sort={sort}>Type</Sorter></th>
|
||||
<th className='th_icon optional_small'>
|
||||
<Sorter filed='kind' sort={sort}>Type</Sorter>
|
||||
</th>
|
||||
<th><Sorter field='name' sort={sort}>Name</Sorter></th>
|
||||
<th><Sorter field='namespace' sort={sort}>Namespace</Sorter></th>
|
||||
<th><Sorter field='apiGroup' sort={sort}>Api Group</Sorter></th>
|
||||
<th className='optional_small'><Sorter field='namespace' sort={sort}>Namespace</Sorter></th>
|
||||
<th className='optional_small'><Sorter field='apiGroup' sort={sort}>Api Group</Sorter></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<TableBody items={subjects} sort={sort} colSpan='4' row={x => (
|
||||
<tr key={x.name}>
|
||||
<td>
|
||||
<td className='td_icon optional_small'>
|
||||
<ResourceSvg resource={x.kind} />
|
||||
<div className='td_iconLabel'>{x.kind}</div>
|
||||
</td>
|
||||
<td>
|
||||
{x.kind === 'ServiceAccount' ? (<a href={getSubjectHref(x)}>{x.name}</a>) : x.name}
|
||||
</td>
|
||||
<td>{x.namespace}</td>
|
||||
<td>{x.apiGroup}</td>
|
||||
<td className='optional_small'>{x.namespace}</td>
|
||||
<td className='optional_small'>{x.apiGroup}</td>
|
||||
</tr>
|
||||
)} />
|
||||
</table>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import api from '../services/api';
|
||||
import EventsPanel from '../components/eventsPanel';
|
||||
import {TableBody} from '../components/listViewHelpers';
|
||||
import Base from '../components/base';
|
||||
import DeleteButton from '../components/deleteButton';
|
||||
import Field from '../components/field';
|
||||
@@ -26,7 +26,6 @@ export default class Service extends Base {
|
||||
render() {
|
||||
const {namespace, name} = this.props;
|
||||
const {item, events} = this.state || {};
|
||||
const ports = item && item.spec.ports;
|
||||
|
||||
const filteredEvents = filterByOwner(events, item);
|
||||
|
||||
@@ -54,30 +53,17 @@ export default class Service extends Base {
|
||||
<Field name='Affinity' value={item.spec.sessionAffinity} />
|
||||
<Field name='Selector' value={item.spec.selector && item.spec.selector.app} />
|
||||
<Field name='Ports'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Port</th>
|
||||
<th>Protocol</th>
|
||||
<th>Target</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<TableBody items={ports} colSpan='4' row={x => (
|
||||
<tr key={x.port}>
|
||||
<td>{x.name}</td>
|
||||
<td>{x.port}</td>
|
||||
<td>{x.protocol}</td>
|
||||
<td>{x.targetPort}</td>
|
||||
</tr>
|
||||
)} />
|
||||
</table>
|
||||
{_.map(item.spec.ports, x => (
|
||||
<div key={x.port}>
|
||||
{[x.name, x.port, x.targetPort, x.protocol].filter(y => !!y).join(' • ')}
|
||||
</div>
|
||||
))}
|
||||
</Field>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='contentPanel_header'>Events</div>
|
||||
<EventsPanel shortList={true} items={filteredEvents} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -81,6 +81,7 @@ export default class StatefulSet extends Base {
|
||||
|
||||
<ContainersPanel spec={item && item.spec.template.spec} />
|
||||
|
||||
<div className='contentPanel_header'>Pods</div>
|
||||
<PodsPanel
|
||||
items={filteredPods}
|
||||
sort={podsSort}
|
||||
@@ -88,6 +89,7 @@ export default class StatefulSet extends Base {
|
||||
skipNamespace={true}
|
||||
/>
|
||||
|
||||
<div className='contentPanel_header'>Events</div>
|
||||
<EventsPanel
|
||||
shortList={true}
|
||||
sort={eventsSort}
|
||||
|
||||
@@ -36,7 +36,7 @@ export default class StorageClasses extends Base {
|
||||
<tr>
|
||||
<MetadataHeaders sort={sort} />
|
||||
<th><Sorter field='reclaimPolicy' sort={sort}>Reclaim Policy</Sorter></th>
|
||||
<th><Sorter field='volumeBindingMode' sort={sort}>Mode</Sorter></th>
|
||||
<th className='optional_small'><Sorter field='volumeBindingMode' sort={sort}>Mode</Sorter></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -47,7 +47,7 @@ export default class StorageClasses extends Base {
|
||||
href={`#!storageclass/${x.metadata.name}`}
|
||||
/>
|
||||
<td>{x.reclaimPolicy}</td>
|
||||
<td>{x.volumeBindingMode}</td>
|
||||
<td className='optional_small'>{x.volumeBindingMode}</td>
|
||||
</tr>
|
||||
)} />
|
||||
</table>
|
||||
|
||||
@@ -115,12 +115,6 @@ async function logClusterInfo() {
|
||||
const apis = apisResponse.body.groups.map(x => x.preferredVersion.groupVersion).sort();
|
||||
const apisJson = JSON.stringify(apis, null, 4);
|
||||
console.log('Available APIs: ', apisJson);
|
||||
|
||||
const authClient = kc.makeApiClient(k8s.Authorization_v1Api);
|
||||
const spec = {resourceAttributes: {}};
|
||||
const authResponse = await authClient.createSelfSubjectAccessReview({spec});
|
||||
const authJson = JSON.stringify(authResponse.body, null, 4);
|
||||
console.log('Auth Response: ', authJson);
|
||||
} catch (err) {
|
||||
console.error('Error getting cluster info', err);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user