diff --git a/spikes/compose-demo/.eslintrc b/spikes/compose-demo/.eslintrc new file mode 100644 index 00000000..7187c914 --- /dev/null +++ b/spikes/compose-demo/.eslintrc @@ -0,0 +1,34 @@ +{ + "parser": "babel-eslint", + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "xo-space/esnext", + "prettier", + "prettier/react" + ], + "plugins": [ + "react", + "prettier" + ], + "rules": { + "react/react-in-jsx-scope": 0, + "react/display-name": 0, + "react/prop-types": 0, + "prettier/prettier": ["error", { + "useTabs": false, + "printWidth": 80, + "tabWidth": 2, + "singleQuote": true, + "trailingComma": "none", + "bracketSpacing": true, + "jsxBracketSameLine": false, + "semi": true + }] + }, + "env": { + "browser": true, + "es6": true, + "node": true + } +} \ No newline at end of file diff --git a/spikes/compose-demo/components/article.js b/spikes/compose-demo/components/article.js new file mode 100644 index 00000000..f5ab29e7 --- /dev/null +++ b/spikes/compose-demo/components/article.js @@ -0,0 +1,13 @@ +import styled from 'styled-components'; + +const Article = styled.article` + font-family: Helvetica; + padding: 10px; + color: dimgray; +`; + +export default ({ children }) => ( +
+ {children} +
+); diff --git a/spikes/compose-demo/components/editor.js b/spikes/compose-demo/components/editor.js new file mode 100644 index 00000000..20e4ecb4 --- /dev/null +++ b/spikes/compose-demo/components/editor.js @@ -0,0 +1,90 @@ +import React, { Component } from 'react'; +import ReactCodeMirror from 'react-codemirror'; + +import YamlMode from 'codemirror/mode/yaml/yaml'; +// eslint-disable-next-line no-unused-vars +import KeyMap from 'codemirror/keymap/sublime'; +// eslint-disable-next-line no-unused-vars +// import Lint from 'codemirror/addon/edit/lint'; +// eslint-disable-next-line no-unused-vars +import CloseBrackets from 'codemirror/addon/edit/closebrackets'; +// eslint-disable-next-line no-unused-vars +import MatchBrackets from 'codemirror/addon/edit/matchbrackets'; +// eslint-disable-next-line no-unused-vars +import FoldCode from 'codemirror/addon/fold/foldcode'; +// eslint-disable-next-line no-unused-vars +import FoldGutter from 'codemirror/addon/fold/foldgutter'; +// eslint-disable-next-line no-unused-vars +import BraceFold from 'codemirror/addon/fold/brace-fold'; +// eslint-disable-next-line no-unused-vars +import IndentFold from 'codemirror/addon/fold/indent-fold'; +// eslint-disable-next-line no-unused-vars +import CommentFold from 'codemirror/addon/fold/comment-fold'; +// eslint-disable-next-line no-unused-vars +import ActiveLine from 'codemirror/addon/selection/active-line'; +// eslint-disable-next-line no-unused-vars +import CloseTag from 'codemirror/addon/edit/closetag'; + +const options = { + mode: { + name: 'javascript', + json: true + }, + theme: 'eclipse', + indentUnit: 2, + smartIndent: true, + tabSize: 2, + indentWithTabs: false, + electricChars: true, + keyMap: 'sublime', + lineNumbers: true, + inputStyle: 'contenteditable', + readOnly: false, + autoCloseBrackets: true, + styleActiveLine: true, + matchBrackets: true, + lineWrapping: true, + foldGutter: true, + autoCloseTags: true, + viewportMargin: Infinity, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'] +}; + +class Editor extends Component { + constructor() { + super(); + + this._refs = {}; + + this.state = { + code: manifest.trim() + }; + + this.handleOnChange = this.handleOnChange.bind(this); + } + handleOnChange(newCode) { + this.setState({ + code: newCode + }); + } + ref(name) { + return ref => { + this._refs[name] = ref; + }; + } + render() { + return ( + + ); + } +} + +export default Editor; diff --git a/spikes/compose-demo/components/layout.js b/spikes/compose-demo/components/layout.js new file mode 100644 index 00000000..d65d8fa5 --- /dev/null +++ b/spikes/compose-demo/components/layout.js @@ -0,0 +1,34 @@ +import NProgress from 'nprogress'; +import Head from 'next/head'; +import Router from 'next/router'; +import Article from './article'; + +Router.onRouteChangeStart = () => NProgress.start(); +Router.onRouteChangeComplete = () => NProgress.done(); +Router.onRouteChangeError = () => NProgress.done(); + +export default ({ children, title = '' }) => ( +
+ + {title} + + + + + + + + + + +
+ {children} +
+
+); diff --git a/spikes/compose-demo/package.json b/spikes/compose-demo/package.json new file mode 100644 index 00000000..4c90fbc8 --- /dev/null +++ b/spikes/compose-demo/package.json @@ -0,0 +1,43 @@ +{ + "name": "compose-demo", + "version": "1.0.0", + "private": true, + "description": "", + "license": "Apache-2.0", + "author": "Sérgio Ramos ", + "keywords": [], + "main": "src/server.js", + "scripts": { + "fmt": "prettier --write --single-quote", + "format": "fmt {components,pages}/**/*.js", + "lint": "eslint {components,pages}/**/*.js --fix", + "build": "next build", + "start": "next start", + "dev": "next" + }, + "dependencies": { + "body-parser": "^1.17.1", + "force-array": "^3.1.0", + "next": "^2.3.1", + "nprogress": "^0.2.0", + "react": "^15.5.4", + "react-codemirror": "^0.3.0", + "react-dom": "^15.5.4", + "router": "^1.3.0", + "simple-json-form-viewer": "^1.0.2", + "styled-components": "^1.4.6", + "triton-watch": "^1.0.0" + }, + "devDependencies": { + "babel-eslint": "^7.2.3", + "eslint": "3.19.0", + "eslint-config-prettier": "2.0.0", + "eslint-config-xo-space": "^0.16.0", + "eslint-plugin-flowtype": "2.32.1", + "eslint-plugin-flowtype-errors": "3.2.0", + "eslint-plugin-prettier": "^2.0.1", + "eslint-plugin-react": "^7.0.0", + "prettier": "1.3.1", + "react-no-ssr": "^1.1.0" + } +} diff --git a/spikes/compose-demo/pages/index.js b/spikes/compose-demo/pages/index.js new file mode 100644 index 00000000..babed31d --- /dev/null +++ b/spikes/compose-demo/pages/index.js @@ -0,0 +1,155 @@ +import { Component } from 'react'; +import Layout from '../components/layout'; +// import Editor from '../components/editor'; +import NoSSR from 'react-no-ssr'; +import styled from 'styled-components'; +import JSONViewer from 'simple-json-form-viewer'; +import forceArray from 'force-array'; + +const tags = { + hash: 'docker:label:com.docker.compose.config-hash', + project: 'docker:label:com.docker.compose.project', + service: 'docker:label:com.docker.compose.service' +}; + +const Panel = styled.div` + width: 50%; + float: ${props => props['float']}; + margin: 0 -10px 0 -10px; +`; + +const Textarea = styled.textarea` + width: 100%; + height: 100px; +`; + +// {JSON.stringify(this.state.scale, null, 2)} +// {JSON.stringify(this.state.provision, null, 2)} +// const ProvisionResult = (res) => { +// const services = Object.keys(res).map((name) => { +// return ( +// +// ); +// }); +// }; + +export default class App extends Component { + constructor() { + super(); + + this.state = { + manifest: ` +hello: + image: hello-world:latest +world: + image: consul:latest +`.trim(), + name: 'compose-demo' + }; + + this.handleProvision = this.handleProvision.bind(this); + this.handleScale = this.handleScale.bind(this); + this.handleManifestChange = this.handleManifestChange.bind(this); + this.poll = this.poll.bind(this); + } + + componentDidMount() { + this.poll(); + } + + async poll() { + const res = await fetch('/api/status', { + method: 'GET' + }); + + const containers = await res.json(); + + const status = Object.values(containers) + .filter( + container => + container.tags[tags.hash] && + container.tags[tags.project] === this.state.name + ) + .reduce((sum, container) => { + const name = container.tags[tags.service]; + const prevInstances = forceArray(sum[name]); + + return Object.assign(sum, { + [name]: prevInstances.concat([ + { + name: container.name, + state: container.state + } + ]) + }); + }, {}); + + console.log(status); + + this.setState({ + status + }); + + setTimeout(() => this.poll(), 16); + } + + async handleProvision() { + const body = JSON.stringify({ + manifest: this.state.manifest, + name: this.state.name + }); + + const headers = new Headers({ + 'Content-Type': 'application/json', + 'Content-Length': body.length.toString() + }); + + const res = await fetch('/api/provision', { + method: 'POST', + headers: headers, + body: body + }); + + this.setState({ + provision: await res.json() + }); + } + + async handleScale() {} + + handleManifestChange(ev) { + this.setState({ + manifest: ev.target.value + }); + } + + render() { + return ( + + +