diff --git a/client/src/app.js b/client/src/app.js index abae90a..18fcdbc 100755 --- a/client/src/app.js +++ b/client/src/app.js @@ -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 = (
@@ -24,19 +26,28 @@ class App extends Component { } render() { - const {content, hasError} = this.state || {}; + const {content, hasError, menuToggled} = this.state || {}; return ( <> - +
+ + + + + +
+
- + this.setState({menuToggled: false})} /> {hasError ? genericError : content} -
+ ); } diff --git a/client/src/art/hamburgerSvg.js b/client/src/art/hamburgerSvg.js new file mode 100644 index 0000000..6dd1de1 --- /dev/null +++ b/client/src/art/hamburgerSvg.js @@ -0,0 +1,9 @@ +import React from 'react'; + +const HamburgerSvg = props => ( + + + +); + +export default HamburgerSvg; diff --git a/client/src/components/containersPanel.js b/client/src/components/containersPanel.js index ccc1b6d..0dbeb55 100644 --- a/client/src/components/containersPanel.js +++ b/client/src/components/containersPanel.js @@ -5,63 +5,52 @@ import Field from './field'; const ContainersPanel = ({spec}) => ( <> {spec && _.map(spec.containers, item => ( -
- {item.name} - {item.image} + <> +
Container
+
- {item.args && ( - {item.args.join(' ')} - )} + {item.name} + {item.image} - {item.env && ( - - {item.env.map(x => ( -
- {x.name}: {getVariableValue(x)} -
- ))} -
- )} + {item.args && ( + {item.args.join(' ')} + )} - {item.resources && item.resources.requests && ( - <> - {item.resources.requests.cpu} - {item.resources.requests.memory} - - )} + {item.env && ( + + {item.env.map(x => ( +
+ {x.name}: {getVariableValue(x)} +
+ ))} +
+ )} - {item.resources && item.resources.limits && ( - <> - {item.resources.limits.cpu} - {item.resources.limits.memory} - - )} + {item.resources && item.resources.requests && ( + <> + {item.resources.requests.cpu} + {item.resources.requests.memory} + + )} - {item.ports && ( - - - - - - - - - - - - {item.ports.map((x, i) => ( - - - - - - - ))} - -
NameContainer PortHost PortProtocol
{x.name}{x.containerPort}{x.hostPort}{x.protocol}
-
- )} -
+ {item.resources && item.resources.limits && ( + <> + {item.resources.limits.cpu} + {item.resources.limits.memory} + + )} + + {item.ports && ( + + {item.ports.map((x, i) => ( +
+ {[x.name, x.containerPort, x.hostPort, x.protocol].filter(y => !!y).join(' • ')} +
+ ))} +
+ )} +
+ ))} ); diff --git a/client/src/components/eventsPanel.js b/client/src/components/eventsPanel.js index 7b79b86..45a57fb 100644 --- a/client/src/components/eventsPanel.js +++ b/client/src/components/eventsPanel.js @@ -8,10 +8,10 @@ import Sorter, {sortByDate} from './sorter'; export default function EventsPanel({items, filter, shortList, sort}) { return (
- +
- )} - - )} - + )} diff --git a/client/src/components/field.scss b/client/src/components/field.scss index 6320706..87fe8f4 100644 --- a/client/src/components/field.scss +++ b/client/src/components/field.scss @@ -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; + } +} \ No newline at end of file diff --git a/client/src/components/inputFilter.js b/client/src/components/inputFilter.js index 0580e03..a377dc9 100644 --- a/client/src/components/inputFilter.js +++ b/client/src/components/inputFilter.js @@ -5,7 +5,7 @@ import Cancel from '../art/cancelSvg'; const InputFilter = ({filter, onChange}) => ( <> ( /> {filter.length === 0 ? ( - + ) : ( - onChange('')} /> + onChange('')} /> )} ); diff --git a/client/src/components/listViewHelpers.js b/client/src/components/listViewHelpers.js index f0d8753..0aa1318 100644 --- a/client/src/components/listViewHelpers.js +++ b/client/src/components/listViewHelpers.js @@ -31,18 +31,18 @@ export function TableBody({items, filter, colSpan, sort, row}) { export function MetadataHeaders({includeNamespace, sort}) { return ( <> - {includeNamespace && ( - )} - @@ -52,7 +52,7 @@ export function MetadataHeaders({includeNamespace, sort}) { export function MetadataColumns({item, href, includeNamespace, isError}) { return ( <> - )} - diff --git a/client/src/components/menu.js b/client/src/components/menu.js index a3a1f05..43484b2 100644 --- a/client/src/components/menu.js +++ b/client/src/components/menu.js @@ -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 ( - - - + + @@ -47,8 +47,8 @@ export default class NodesPanel extends Base { ( - - + + diff --git a/client/src/components/podsPanel.js b/client/src/components/podsPanel.js index db847d8..954f7a5 100644 --- a/client/src/components/podsPanel.js +++ b/client/src/components/podsPanel.js @@ -36,11 +36,9 @@ export default class PodsPanel extends Base { {!skipNodeName && ( - + )} - - - + } - - - + {!skipNodeName && } + @@ -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'); } diff --git a/client/src/components/replicaSetsPanel.js b/client/src/components/replicaSetsPanel.js index 0e504cc..671a51b 100644 --- a/client/src/components/replicaSetsPanel.js +++ b/client/src/components/replicaSetsPanel.js @@ -19,12 +19,12 @@ export default class ReplicaSetsPanel extends Base { - + )} diff --git a/client/src/components/scaleButton.js b/client/src/components/scaleButton.js index 10f5e90..9e3fb5d 100644 --- a/client/src/components/scaleButton.js +++ b/client/src/components/scaleButton.js @@ -18,12 +18,12 @@ export default class ScaleButton extends Base { {scaleInfo && ( this.close()}> -
-
Desired count
+
+
Desired Count
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); } } diff --git a/client/src/components/scaleButton.scss b/client/src/components/scaleButton.scss index afab782..b57609e 100644 --- a/client/src/components/scaleButton.scss +++ b/client/src/components/scaleButton.scss @@ -1,5 +1,10 @@ @import '../scss/settings.scss'; +.scaleButton { + display: flex; + flex-direction: column; +} + .scaleButton_label { color: $color-light; } diff --git a/client/src/components/titleBar.js b/client/src/components/titleBar.js deleted file mode 100644 index b06629d..0000000 --- a/client/src/components/titleBar.js +++ /dev/null @@ -1,14 +0,0 @@ -import './titleBar.scss'; -import React from 'react'; -import LogoSvg from '../art/logoSvg'; - -const TitleBar = () => ( -
- - - - -
-); - -export default TitleBar; diff --git a/client/src/components/titleBar.scss b/client/src/components/titleBar.scss deleted file mode 100644 index 9c57d4a..0000000 --- a/client/src/components/titleBar.scss +++ /dev/null @@ -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; -} diff --git a/client/src/scss/elements/charts.scss b/client/src/scss/elements/charts.scss index 5fa5d9c..33591fa 100644 --- a/client/src/scss/elements/charts.scss +++ b/client/src/scss/elements/charts.scss @@ -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; + } +} diff --git a/client/src/scss/elements/contentPanel.scss b/client/src/scss/elements/contentPanel.scss index bc29ec9..2db0637 100644 --- a/client/src/scss/elements/contentPanel.scss +++ b/client/src/scss/elements/contentPanel.scss @@ -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; + } +} diff --git a/client/src/scss/elements/header.scss b/client/src/scss/elements/header.scss index 61a41da..b07211a 100644 --- a/client/src/scss/elements/header.scss +++ b/client/src/scss/elements/header.scss @@ -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; + } +} \ No newline at end of file diff --git a/client/src/scss/elements/modal.scss b/client/src/scss/elements/modal.scss index 562ce8b..d640183 100644 --- a/client/src/scss/elements/modal.scss +++ b/client/src/scss/elements/modal.scss @@ -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; + } +} \ No newline at end of file diff --git a/client/src/scss/elements/table.scss b/client/src/scss/elements/table.scss index 1e85ae6..13e9e08 100644 --- a/client/src/scss/elements/table.scss +++ b/client/src/scss/elements/table.scss @@ -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; + } +} diff --git a/client/src/scss/index.scss b/client/src/scss/index.scss index 0efb8f4..c9cae30 100755 --- a/client/src/scss/index.scss +++ b/client/src/scss/index.scss @@ -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; + } +} diff --git a/client/src/scss/settings.scss b/client/src/scss/settings.scss index 644aff4..0740576 100755 --- a/client/src/scss/settings.scss +++ b/client/src/scss/settings.scss @@ -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; \ No newline at end of file diff --git a/client/src/views/auth.js b/client/src/views/auth.js index 86a939d..290fac8 100644 --- a/client/src/views/auth.js +++ b/client/src/views/auth.js @@ -44,7 +44,7 @@ export default class Auth extends Base {
{!useTokenLogin ? : ( <> - + +
Rules
+ Type @@ -22,7 +22,7 @@ export default function EventsPanel({items, filter, shortList, sort}) { Name + Reason @@ -38,7 +38,7 @@ export default function EventsPanel({items, filter, shortList, sort}) { sort={sort} row={x => (
+ {x.involvedObject.namespace}:{x.involvedObject.name}{x.reason}{x.reason} {x.message}
+ Type Name + Namespace + Age + {includeNamespace && ( - + {item.metadata.namespace || 'All Namespaces'} + {moment(item.metadata.creationTimestamp).fromNow(true)}
LabelsReadyLabelsReady Cpu Ram
{objectMap(x.metadata.labels)}{getReadyStatus(x)}{objectMap(x.metadata.labels)}{getReadyStatus(x)}
NodeNodeStatusContainersRestartsRestarts Cpu @@ -68,10 +66,8 @@ export default class PodsPanel extends Base { includeNamespace={!skipNamespace} href={`#!pod/${x.metadata.namespace}/${x.metadata.name}`} /> - {!skipNodeName && {x.spec.nodeName}{x.status.phase}{getContainerCount(x)}{getRestartCount(x)}{x.spec.nodeName}{getRestartCount(x)}
+ Generations Replicas - {x.status.observedGeneration}{x.status.observedGeneration} {x.spec.replicas} / {x.status.replicas}
diff --git a/client/src/views/clusterRoleBinding.js b/client/src/views/clusterRoleBinding.js index 9a55432..ecca0ea 100644 --- a/client/src/views/clusterRoleBinding.js +++ b/client/src/views/clusterRoleBinding.js @@ -61,20 +61,21 @@ export default class ClusterRoleBinding extends Base { )} +
Subjects
- - - @@ -82,15 +83,15 @@ export default class ClusterRoleBinding extends Base { ( - - - + + )} />
+ Type Name + Namespace + Api Group
+
{x.kind}
{x.kind === 'ServiceAccount' ? ({x.name}) : x.name} {x.namespace}{x.apiGroup}{x.namespace}{x.apiGroup}
diff --git a/client/src/views/cronJob.js b/client/src/views/cronJob.js index c258dff..ea0e270 100644 --- a/client/src/views/cronJob.js +++ b/client/src/views/cronJob.js @@ -82,6 +82,7 @@ export default class CronJob extends Base { {/* TODO: this actually need to be a list of jobs */} +
Pods
+
Events
+ +
Pods
+ +
Events
); diff --git a/client/src/views/deployment.js b/client/src/views/deployment.js index 9b39c5d..6e7fe01 100644 --- a/client/src/views/deployment.js +++ b/client/src/views/deployment.js @@ -94,12 +94,14 @@ export default class Deployment extends Base { +
Replica Sets
+
Pods
+
Events
+
Rules
diff --git a/client/src/views/job.js b/client/src/views/job.js index 5ef24cb..33ce789 100644 --- a/client/src/views/job.js +++ b/client/src/views/job.js @@ -85,6 +85,7 @@ export default class Job extends Base { +
Pods
+
Events
+
Events
); diff --git a/client/src/views/node.js b/client/src/views/node.js index 3ad46d0..f689a0f 100644 --- a/client/src/views/node.js +++ b/client/src/views/node.js @@ -71,9 +71,9 @@ export default class Node extends Base {
- - - + + + @@ -81,9 +81,9 @@ export default class Node extends Base { - - - + + + ))} @@ -91,7 +91,7 @@ export default class Node extends Base { )} - +
Pods
)} -
Cpu Cores Used
+
Cores
); } @@ -78,11 +78,11 @@ function RamTotalsChart({items, metrics}) { return (
{totals ? ( - + ) : ( )} -
GB Ram Used
+
Ram
); } diff --git a/client/src/views/persistentVolumeClaims.js b/client/src/views/persistentVolumeClaims.js index b76945c..8e23404 100644 --- a/client/src/views/persistentVolumeClaims.js +++ b/client/src/views/persistentVolumeClaims.js @@ -39,18 +39,18 @@ export default class PersistentVolumeClaims extends Base {
- - - - + @@ -64,10 +64,10 @@ export default class PersistentVolumeClaims extends Base { includeNamespace={true} href={`#!persistentvolumeclaim/${x.metadata.namespace}/${x.metadata.name}`} /> - - - - + + + + )} /> diff --git a/client/src/views/persistentVolumes.js b/client/src/views/persistentVolumes.js index 70c3609..dffe948 100644 --- a/client/src/views/persistentVolumes.js +++ b/client/src/views/persistentVolumes.js @@ -36,7 +36,7 @@ export default class PersistentVolumes extends Base { - + )} /> diff --git a/client/src/views/pod.js b/client/src/views/pod.js index a1fc296..6a68ffa 100644 --- a/client/src/views/pod.js +++ b/client/src/views/pod.js @@ -117,6 +117,8 @@ export default class Pod extends Base { + +
Events
); diff --git a/client/src/views/replicaSet.js b/client/src/views/replicaSet.js index 2aeb17d..8178969 100644 --- a/client/src/views/replicaSet.js +++ b/client/src/views/replicaSet.js @@ -93,6 +93,7 @@ export default class ReplicaSet extends Base { +
Pods
+
Events
+
Rules
Condition StatusTransitionReasonMessageTransitionReasonMessage
{x.type} {x.status}{moment(x.lastTransitionTime).fromNow()}{x.reason}{x.message}{moment(x.lastTransitionTime).fromNow()}{x.reason}{x.message}
+ Status + Class Name - Access Modes - + Volume + Access Modes + Capacity {x.status.phase}{x.spec.storageClassName}{getAccessModes(x, ' • ')}{x.spec.volumeName}{x.status.phase}{x.spec.storageClassName}{x.spec.volumeName}{getAccessModes(x, ' • ')} {x.status.capacity && x.status.capacity.storage}
+ Status @@ -51,7 +51,7 @@ export default class PersistentVolumes extends Base { item={x} href={`#!persistentvolume/${x.metadata.name}`} /> - {x.status.phase}{x.status.phase} {x.spec.capacity && x.spec.capacity.storage}
diff --git a/client/src/views/roleBinding.js b/client/src/views/roleBinding.js index f4959c4..cc6f184 100644 --- a/client/src/views/roleBinding.js +++ b/client/src/views/roleBinding.js @@ -59,28 +59,31 @@ export default class RoleBinding extends Base { )} +
Subjects
- + - - + + ( - - - + + )} />
Type + Type + NameNamespaceApi GroupNamespaceApi Group
+
{x.kind}
{x.kind === 'ServiceAccount' ? ({x.name}) : x.name} {x.namespace}{x.apiGroup}{x.namespace}{x.apiGroup}
diff --git a/client/src/views/service.js b/client/src/views/service.js index fb40579..62b0518 100644 --- a/client/src/views/service.js +++ b/client/src/views/service.js @@ -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 { - - - - - - - - - - - ( - - - - - - - )} /> -
NamePortProtocolTarget
{x.name}{x.port}{x.protocol}{x.targetPort}
+ {_.map(item.spec.ports, x => ( +
+ {[x.name, x.port, x.targetPort, x.protocol].filter(y => !!y).join(' • ')} +
+ ))}
)}
+
Events
); diff --git a/client/src/views/statefulSet.js b/client/src/views/statefulSet.js index c124480..b77bf9b 100644 --- a/client/src/views/statefulSet.js +++ b/client/src/views/statefulSet.js @@ -81,6 +81,7 @@ export default class StatefulSet extends Base { +
Pods
+
Events
Reclaim Policy - Mode + Mode @@ -47,7 +47,7 @@ export default class StorageClasses extends Base { href={`#!storageclass/${x.metadata.name}`} /> {x.reclaimPolicy} - {x.volumeBindingMode} + {x.volumeBindingMode} )} /> diff --git a/server/index.js b/server/index.js index 5a38c24..400f390 100644 --- a/server/index.js +++ b/server/index.js @@ -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); }