From d161046303620f0b000adfc798c04416872f7723 Mon Sep 17 00:00:00 2001 From: "s.hermans" Date: Fri, 3 Feb 2017 16:20:19 +0100 Subject: [PATCH] refactor most part of the ui --- .gitignore | 1 + Dockerfile.dev | 9 + docker-compose-npm-cache.yml | 17 ++ docker-compose.yml | 22 ++ docs/assets/images/microdocs-semi-white.png | Bin 0 -> 9167 bytes microdocs-server/package.json | 2 +- microdocs-ui/.editorconfig | 13 ++ microdocs-ui/angular-cli.json | 59 ++++++ microdocs-ui/e2e/app.e2e-spec.ts | 14 ++ microdocs-ui/e2e/app.po.ts | 11 + microdocs-ui/e2e/tsconfig.json | 16 ++ microdocs-ui/karma.conf.js | 43 ++++ microdocs-ui/npm-debug.log.1186994542 | 0 microdocs-ui/package.json | 1 + microdocs-ui/protractor.conf.js | 32 +++ microdocs-ui/src/app/app.component.html | 22 ++ microdocs-ui/src/app/app.component.scss | 3 + microdocs-ui/src/app/app.component.spec.ts | 34 +++ microdocs-ui/src/app/app.component.ts | 63 ++++++ microdocs-ui/src/app/app.module.ts | 85 ++++++++ .../src/app/clients/project.client.ts | 35 ++++ .../src/app/clients/rest-project.client.ts | 87 ++++++++ .../app/clients/standalone-project.client.ts | 86 ++++++++ .../body-render/body-render.component.html | 14 ++ .../body-render/body-render.component.scss | 6 + .../body-render/body-render.component.ts | 32 +++ .../dashboard/dashboard.component.html | 10 + .../dashboard/dashboard.component.ts | 42 ++++ .../endpoint-group.component.html | 21 ++ .../endpoint-group.component.scss | 82 ++++++++ .../endpoint-group.component.ts | 21 ++ .../endpoint/endpoint.component.html | 62 ++++++ .../endpoint/endpoint.component.scss | 77 +++++++ .../components/endpoint/endpoint.component.ts | 35 ++++ .../icon-generator.component.html | 3 + .../icon-generator.component.scss | 29 +++ .../icon-generator.component.ts | 41 ++++ .../app/components/model/model.component.html | 10 + .../app/components/model/model.component.scss | 0 .../app/components/model/model.component.ts | 36 ++++ .../path-highlight.component.html | 1 + .../path-highlight.component.scss | 8 + .../path-highlight.component.ts | 26 +++ .../problem-box/problem-box.component.html | 12 ++ .../problem-box/problem-box.component.scss | 3 + .../problem-box/problem-box.component.ts | 16 ++ .../components/project/project.component.html | 127 ++++++++++++ .../components/project/project.component.scss | 195 ++++++++++++++++++ .../components/project/project.component.ts | 190 +++++++++++++++++ .../sidebar-list/sidebar-list.component.html | 34 +++ .../sidebar-list/sidebar-list.component.scss | 45 ++++ .../sidebar-list/sidebar-list.component.ts | 40 ++++ .../components/sidebar/sidebar.component.html | 47 +++++ .../components/sidebar/sidebar.component.scss | 176 ++++++++++++++++ .../components/sidebar/sidebar.component.ts | 152 ++++++++++++++ .../simple-card/simple-card.component.html | 17 ++ .../simple-card/simple-card.component.scss | 59 ++++++ .../simple-card/simple-card.component.ts | 42 ++++ .../src/app/domain/route-info.model.ts | 18 ++ microdocs-ui/src/app/helpers/string.util.ts | 32 +++ microdocs-ui/src/app/pipes/empty.pipe.ts | 63 ++++++ .../src/app/pipes/filter-by-field.pipe.ts | 67 ++++++ microdocs-ui/src/app/pipes/not-empty.pipe.ts | 46 +++++ .../src/app/pipes/object-iterator.pipe.ts | 31 +++ .../src/app/pipes/sort-by-http-method.pipe.ts | 40 ++++ .../src/app/pipes/sort-by-key.pipe.ts | 32 +++ .../src/app/services/project.service.ts | 116 +++++++++++ microdocs-ui/src/assets/.gitkeep | 0 microdocs-ui/src/assets/scss/_settings.scss | 4 +- .../src/environments/environment.prod.ts | 4 + .../environments/environment.standalone.ts | 4 + microdocs-ui/src/environments/environment.ts | 9 + microdocs-ui/src/favicon.ico | Bin 0 -> 5430 bytes microdocs-ui/src/index.html | 2 +- microdocs-ui/src/main.ts | 12 ++ microdocs-ui/src/polyfills.ts | 19 ++ microdocs-ui/src/styles.scss | 87 ++++++++ microdocs-ui/src/test.ts | 32 +++ microdocs-ui/tslint.json | 116 +++++++++++ npm-cache/conf/config.yaml | 48 +++++ package.json | 8 + 81 files changed, 3153 insertions(+), 3 deletions(-) create mode 100644 Dockerfile.dev create mode 100644 docker-compose-npm-cache.yml create mode 100644 docker-compose.yml create mode 100644 docs/assets/images/microdocs-semi-white.png create mode 100644 microdocs-ui/.editorconfig create mode 100644 microdocs-ui/angular-cli.json create mode 100644 microdocs-ui/e2e/app.e2e-spec.ts create mode 100644 microdocs-ui/e2e/app.po.ts create mode 100644 microdocs-ui/e2e/tsconfig.json create mode 100644 microdocs-ui/karma.conf.js create mode 100644 microdocs-ui/npm-debug.log.1186994542 create mode 100644 microdocs-ui/protractor.conf.js create mode 100644 microdocs-ui/src/app/app.component.html create mode 100644 microdocs-ui/src/app/app.component.scss create mode 100644 microdocs-ui/src/app/app.component.spec.ts create mode 100644 microdocs-ui/src/app/app.component.ts create mode 100644 microdocs-ui/src/app/app.module.ts create mode 100644 microdocs-ui/src/app/clients/project.client.ts create mode 100644 microdocs-ui/src/app/clients/rest-project.client.ts create mode 100644 microdocs-ui/src/app/clients/standalone-project.client.ts create mode 100644 microdocs-ui/src/app/components/body-render/body-render.component.html create mode 100644 microdocs-ui/src/app/components/body-render/body-render.component.scss create mode 100644 microdocs-ui/src/app/components/body-render/body-render.component.ts create mode 100644 microdocs-ui/src/app/components/dashboard/dashboard.component.html create mode 100644 microdocs-ui/src/app/components/dashboard/dashboard.component.ts create mode 100644 microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.html create mode 100644 microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.scss create mode 100644 microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.ts create mode 100644 microdocs-ui/src/app/components/endpoint/endpoint.component.html create mode 100644 microdocs-ui/src/app/components/endpoint/endpoint.component.scss create mode 100644 microdocs-ui/src/app/components/endpoint/endpoint.component.ts create mode 100644 microdocs-ui/src/app/components/icon-generator/icon-generator.component.html create mode 100644 microdocs-ui/src/app/components/icon-generator/icon-generator.component.scss create mode 100644 microdocs-ui/src/app/components/icon-generator/icon-generator.component.ts create mode 100644 microdocs-ui/src/app/components/model/model.component.html create mode 100644 microdocs-ui/src/app/components/model/model.component.scss create mode 100644 microdocs-ui/src/app/components/model/model.component.ts create mode 100644 microdocs-ui/src/app/components/path-highlight/path-highlight.component.html create mode 100644 microdocs-ui/src/app/components/path-highlight/path-highlight.component.scss create mode 100644 microdocs-ui/src/app/components/path-highlight/path-highlight.component.ts create mode 100644 microdocs-ui/src/app/components/problem-box/problem-box.component.html create mode 100644 microdocs-ui/src/app/components/problem-box/problem-box.component.scss create mode 100644 microdocs-ui/src/app/components/problem-box/problem-box.component.ts create mode 100644 microdocs-ui/src/app/components/project/project.component.html create mode 100644 microdocs-ui/src/app/components/project/project.component.scss create mode 100644 microdocs-ui/src/app/components/project/project.component.ts create mode 100644 microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.html create mode 100644 microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.scss create mode 100644 microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.ts create mode 100644 microdocs-ui/src/app/components/sidebar/sidebar.component.html create mode 100644 microdocs-ui/src/app/components/sidebar/sidebar.component.scss create mode 100644 microdocs-ui/src/app/components/sidebar/sidebar.component.ts create mode 100644 microdocs-ui/src/app/components/simple-card/simple-card.component.html create mode 100644 microdocs-ui/src/app/components/simple-card/simple-card.component.scss create mode 100644 microdocs-ui/src/app/components/simple-card/simple-card.component.ts create mode 100644 microdocs-ui/src/app/domain/route-info.model.ts create mode 100644 microdocs-ui/src/app/helpers/string.util.ts create mode 100644 microdocs-ui/src/app/pipes/empty.pipe.ts create mode 100644 microdocs-ui/src/app/pipes/filter-by-field.pipe.ts create mode 100644 microdocs-ui/src/app/pipes/not-empty.pipe.ts create mode 100644 microdocs-ui/src/app/pipes/object-iterator.pipe.ts create mode 100644 microdocs-ui/src/app/pipes/sort-by-http-method.pipe.ts create mode 100644 microdocs-ui/src/app/pipes/sort-by-key.pipe.ts create mode 100644 microdocs-ui/src/app/services/project.service.ts create mode 100644 microdocs-ui/src/assets/.gitkeep create mode 100644 microdocs-ui/src/environments/environment.prod.ts create mode 100644 microdocs-ui/src/environments/environment.standalone.ts create mode 100644 microdocs-ui/src/environments/environment.ts create mode 100644 microdocs-ui/src/favicon.ico create mode 100644 microdocs-ui/src/main.ts create mode 100644 microdocs-ui/src/polyfills.ts create mode 100644 microdocs-ui/src/styles.scss create mode 100644 microdocs-ui/src/test.ts create mode 100644 microdocs-ui/tslint.json create mode 100644 npm-cache/conf/config.yaml create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 9c1ca034..f646c4a2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ /npm-cache/conf/storage /npm-cache/local_storage /npm-cache/storage +/microdocs-server/npm-debug.log diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 00000000..80fe3c6e --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,9 @@ +FROM node:6 +EXPOSE 3000 +WORKDIR /microdocs/microdocs-server +RUN npm set registry http://192.168.252.165:4873/ +RUN echo @maxxton:registry=https://npm.maxxton.com > .npmrc +ADD microdocs-server/config.yml . +ADD microdocs-server/package.json . +RUN npm install --prod && npm install nodemon +CMD ./node_modules/.bin/nodemon --watch dist dist/index.js \ No newline at end of file diff --git a/docker-compose-npm-cache.yml b/docker-compose-npm-cache.yml new file mode 100644 index 00000000..590cdbf8 --- /dev/null +++ b/docker-compose-npm-cache.yml @@ -0,0 +1,17 @@ +version: '2' +services: + npm-registry: + image: verdaccio/verdaccio + volumes: + - ./npm-cache/conf:/verdaccio/conf + - ./npm-cache/storage:/verdaccio/storage + - ./npm-cache/local_storage :/verdaccio/local_storage + ports: + - 4873:4873 + networks: + - 'microdocs-server' + +networks: + microdocs-server: + external: + name: 'microdocs-server' \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..daa7e266 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +version: '2' +services: + microdocs-server: + container_name: microdocs-dev + image: microdocs:dev + build: + dockerfile: Dockerfile.dev + context: . + ports: + - 3000:3000 + volumes: + - ./microdocs-server/dist:/microdocs/microdocs-server/dist + - ./microdocs-server/data:/microdocs/microdocs-server/data + - ./microdocs-ui/dist:/microdocs/microdocs-ui/dist + - ../microdocs-core/dist:/microdocs/microdocs-server/dist/node_modules/@maxxton/microdocs-core + networks: + - 'microdocs-server' + +networks: + microdocs-server: + external: + name: 'microdocs-server' diff --git a/docs/assets/images/microdocs-semi-white.png b/docs/assets/images/microdocs-semi-white.png new file mode 100644 index 0000000000000000000000000000000000000000..68301e6c65c9514b51b76cb8cb0361fb375efa86 GIT binary patch literal 9167 zcmbt)30TtU_P^$xw8g0L&a}9YcSwh_tgOsjD5(rhovhF>C8u0*DHq(KCevxMoY1tg zTrx64)KoM@p|mnIb474LDltt35(N?je=j!o-v9lt&-44&<0HJwIp;m^_j}IyoR3$3 zadXmNzG1nRmX<#1$UzS+Eo~+EnblhgM)vVibiuEszq&Xb)LPK|FK8^z1S8ANA2}5T zc5pO5+KxZx#(_cIXq4+A-49Ea=&jf@;w4Dc(%PtnI(Xnj+<=fbS9LDX&*Z(lCmmL8 z83cp1>zOWF`MsWS$-}Pn?YMN)vXn@p^y90|_xhi=IXQ<6J1NYo2_Lijey#EIN*pvC;M7dk$l>^1RxP&+6}i zJ^zh=7eD`yt2IFBOTd$;D7O&SW}&O35jN-oR|-v6%brVqQ@f%Z`n%$Ds%%@C`jjAbkR~Hp-GDU_T1V{ zHg!KuKH(vCOKX=E9H**FV9a|+_};2QUW5gCzIRy14`RV&b6^W~SrUPfo1^*^@_htK zQj0gP&dHcA^eGGR!gK6?BTU)qx4kQ&g;!1jrPpf59@&1uk&I_BwH~d_D1zMxYWc>g zOWCyv8nvBc=yKlW3fG5i1yXu_DdE_NiZghcnz=Du@~0J+enD#Xfr2azl1lDJ^`QHQDgkuis#m&T3hw_ejPw z=$-1ltn4!8Ys_RnYR_&vL4eZ|y~J_45O|47%Nv^JaH3mO$>c02qa_=s3dKUT?zL*= zJA!@K&_u4NhW|lN%MUV;Tszo^)>w(7b0&b5J+;c?)d~B#Namm_fNBDCiSb0=w1LG% zK|yi^F7C+Ww=G^nF~$*c3^-FkxzLxQaVL|9lY&cN)Fe_hm#}~@XKOq=Ai`0{f;2OP zT4np+@HhP19#{$Zk{8$aD?ED`%Y{OT@qA|nS7mYmS+>3S`0;euxD7Z zqO8ighhcn|NF9(M%L4~Usp$3r->Pz&$sGZ5 zt50Kd=6vc7opJ871PsZ#HfeYH6WXes^3;UO!}Ls}zK_zu6FB?9FWS*>`c{>WTpht% zPU)VP#UCWKHC)g_hn}{l=}-wTvQ2B0roL4~_cm|UZ`WykRW5MII_Hbo^m@Pk9+b)b z=6xy*DMoj0!5n6TU>P?tn1T6D+qx&P&K_eGkUs0`L_bJ$9ebg}qcvl!!k2kdKw5SU zSTel(q3ujs-&8I}HG@)DXH`aAhrF?d&86cZvlVQFT}A|1+vt%l@ssgO?eN`zxoGN-DguaC` z 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 H`R%^|8tME! literal 0 HcmV?d00001 diff --git a/microdocs-server/package.json b/microdocs-server/package.json index 8d36a04b..729c0d11 100644 --- a/microdocs-server/package.json +++ b/microdocs-server/package.json @@ -9,7 +9,7 @@ "main": "index.js", "scripts": { "start": "node dist/index.js", - "debug": "./node_modules/.bin/nodemon --debug-brk dist/index.js", + "debug": "../node_modules/.bin/nodemon --ignore data/** --debug-brk index.js", "watch": "./node_modules/.bin/gulp", "test": "./node_modules/.bin/gulp test", "link": "cd dist && npm link", diff --git a/microdocs-ui/.editorconfig b/microdocs-ui/.editorconfig new file mode 100644 index 00000000..6e87a003 --- /dev/null +++ b/microdocs-ui/.editorconfig @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/microdocs-ui/angular-cli.json b/microdocs-ui/angular-cli.json new file mode 100644 index 00000000..8fe5e575 --- /dev/null +++ b/microdocs-ui/angular-cli.json @@ -0,0 +1,59 @@ +{ + "project": { + "version": "1.0.0-beta.25.5", + "name": "microdocs-ui" + }, + "apps": [ + { + "root": "src", + "outDir": "dist", + "assets": [ + "assets", + "favicon.ico" + ], + "index": "index.html", + "main": "main.ts", + "test": "test.ts", + "tsconfig": "tsconfig.json", + "prefix": "app", + "mobile": false, + "styles": [ + "styles.scss" + ], + "scripts": [], + "environments": { + "source": "environments/environment.ts", + "dev": "environments/environment.ts", + "prod": "environments/environment.prod.ts" + } + } + ], + "addons": [], + "packages": [], + "e2e": { + "protractor": { + "config": "./protractor.conf.js" + } + }, + "test": { + "karma": { + "config": "./karma.conf.js" + } + }, + "defaults": { + "styleExt": "scss", + "prefixInterfaces": false, + "inline": { + "style": false, + "template": false + }, + "spec": { + "class": false, + "component": true, + "directive": true, + "module": false, + "pipe": true, + "service": true + } + } +} diff --git a/microdocs-ui/e2e/app.e2e-spec.ts b/microdocs-ui/e2e/app.e2e-spec.ts new file mode 100644 index 00000000..6de06069 --- /dev/null +++ b/microdocs-ui/e2e/app.e2e-spec.ts @@ -0,0 +1,14 @@ +import { MicrodocsUiPage } from './app.po'; + +describe('microdocs-ui App', function() { + let page: MicrodocsUiPage; + + beforeEach(() => { + page = new MicrodocsUiPage(); + }); + + it('should display message saying app works', () => { + page.navigateTo(); + expect(page.getParagraphText()).toEqual('app works!'); + }); +}); diff --git a/microdocs-ui/e2e/app.po.ts b/microdocs-ui/e2e/app.po.ts new file mode 100644 index 00000000..44466641 --- /dev/null +++ b/microdocs-ui/e2e/app.po.ts @@ -0,0 +1,11 @@ +import { browser, element, by } from 'protractor'; + +export class MicrodocsUiPage { + navigateTo() { + return browser.get('/'); + } + + getParagraphText() { + return element(by.css('app-root h1')).getText(); + } +} diff --git a/microdocs-ui/e2e/tsconfig.json b/microdocs-ui/e2e/tsconfig.json new file mode 100644 index 00000000..656bdb14 --- /dev/null +++ b/microdocs-ui/e2e/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "declaration": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "module": "commonjs", + "moduleResolution": "node", + "outDir": "../dist/out-tsc-e2e", + "sourceMap": true, + "target": "es5", + "typeRoots": [ + "../node_modules/@types" + ] + } +} diff --git a/microdocs-ui/karma.conf.js b/microdocs-ui/karma.conf.js new file mode 100644 index 00000000..1f2613a3 --- /dev/null +++ b/microdocs-ui/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/0.13/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', 'angular-cli'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-remap-istanbul'), + require('angular-cli/plugins/karma') + ], + files: [ + { pattern: './src/test.ts', watched: false } + ], + preprocessors: { + './src/test.ts': ['angular-cli'] + }, + mime: { + 'text/x-typescript': ['ts','tsx'] + }, + remapIstanbulReporter: { + reports: { + html: 'coverage', + lcovonly: './coverage/coverage.lcov' + } + }, + angularCli: { + config: './angular-cli.json', + environment: 'dev' + }, + reporters: config.angularCli && config.angularCli.codeCoverage + ? ['progress', 'karma-remap-istanbul'] + : ['progress'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false + }); +}; diff --git a/microdocs-ui/npm-debug.log.1186994542 b/microdocs-ui/npm-debug.log.1186994542 new file mode 100644 index 00000000..e69de29b diff --git a/microdocs-ui/package.json b/microdocs-ui/package.json index 3bc09fc5..0550e462 100644 --- a/microdocs-ui/package.json +++ b/microdocs-ui/package.json @@ -24,6 +24,7 @@ "@angular/router": "^3.3.1", "@maxxton/angular-rest": "^1.0.0", "@maxxton/microdocs-core": "^1.7.0", + "angular2-prettyjson": "^2.0.3", "core-js": "^2.4.1", "hammerjs": "^2.0.8", "rxjs": "^5.0.1", diff --git a/microdocs-ui/protractor.conf.js b/microdocs-ui/protractor.conf.js new file mode 100644 index 00000000..ffded701 --- /dev/null +++ b/microdocs-ui/protractor.conf.js @@ -0,0 +1,32 @@ +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +/*global jasmine */ +var SpecReporter = require('jasmine-spec-reporter'); + +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './e2e/**/*.e2e-spec.ts' + ], + capabilities: { + 'browserName': 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + useAllAngular2AppRoots: true, + beforeLaunch: function() { + require('ts-node').register({ + project: 'e2e' + }); + }, + onPrepare: function() { + jasmine.getEnv().addReporter(new SpecReporter()); + } +}; diff --git a/microdocs-ui/src/app/app.component.html b/microdocs-ui/src/app/app.component.html new file mode 100644 index 00000000..0b0f751a --- /dev/null +++ b/microdocs-ui/src/app/app.component.html @@ -0,0 +1,22 @@ + + + + +
+ +
+
+ + + + + + + + + + + + + + diff --git a/microdocs-ui/src/app/app.component.scss b/microdocs-ui/src/app/app.component.scss new file mode 100644 index 00000000..bb181654 --- /dev/null +++ b/microdocs-ui/src/app/app.component.scss @@ -0,0 +1,3 @@ +md-sidenav-container{ + height: 100vh; +} diff --git a/microdocs-ui/src/app/app.component.spec.ts b/microdocs-ui/src/app/app.component.spec.ts new file mode 100644 index 00000000..5be2cb3f --- /dev/null +++ b/microdocs-ui/src/app/app.component.spec.ts @@ -0,0 +1,34 @@ +/* tslint:disable:no-unused-variable */ + +import { TestBed, async } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + }); + TestBed.compileComponents(); + }); + + it('should create the app', async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app).toBeTruthy(); + })); + + it(`should have as title 'app works!'`, async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app.title).toEqual('app works!'); + })); + + it('should render title in a h1 tag', async(() => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.debugElement.nativeElement; + expect(compiled.querySelector('h1').textContent).toContain('app works!'); + })); +}); diff --git a/microdocs-ui/src/app/app.component.ts b/microdocs-ui/src/app/app.component.ts new file mode 100644 index 00000000..73ac3f7f --- /dev/null +++ b/microdocs-ui/src/app/app.component.ts @@ -0,0 +1,63 @@ +import {Component, Injectable} from "@angular/core"; +import {Router, ActivatedRoute} from "@angular/router"; +import {Subject} from "rxjs/Subject"; +import {Notification} from "rxjs/Notification"; +import {ProjectTree} from "@maxxton/microdocs-core/domain"; +import { ProjectService } from "./services/project.service"; + + +/** + * @application + * @projectInclude microdocs-core + */ +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['app.component.scss'] +}) +@Injectable() +export class AppComponent { + private showFullSideBar:boolean = true; + private user = {}; + private login = { + error: false, + status: null + }; + + projects:Subject>; + envs:string[]; + selectedEnv:string; + + constructor(private projectService:ProjectService, private router:Router, private activatedRoute: ActivatedRoute) { + this.projects = this.projectService.getProjects(); + + projectService.getEnvs().subscribe((envs) => { + this.envs = Object.keys(envs); + if (projectService.getSelectedEnv() == undefined) { + for (let key in envs) { + if (envs[key].default) { + projectService.setSelectedEnv(key); + this.selectedEnv = key; + break; + } + } + } + }); + + this.activatedRoute.queryParams.subscribe(params => { + if (params['env'] && this.projectService.getSelectedEnv() !== params['env']) { + this.selectedEnv = params['env']; + this.projectService.setSelectedEnv(params['env']); + } + }); + } + + public onEnvVersion(newEnv:string) { + this.projectService.setSelectedEnv(newEnv); + this.selectedEnv = newEnv; + + this.router.navigate(['/dashboard'], {queryParams: {env: newEnv}}); + } + + +} diff --git a/microdocs-ui/src/app/app.module.ts b/microdocs-ui/src/app/app.module.ts new file mode 100644 index 00000000..df1f21b7 --- /dev/null +++ b/microdocs-ui/src/app/app.module.ts @@ -0,0 +1,85 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; +import { RouterModule } from "@angular/router"; +import {PrettyJsonModule} from 'angular2-prettyjson'; + +import { environment } from "../environments/environment"; +import { AppComponent } from './app.component'; +import { ProjectService } from "./services/project.service"; +import { RestProjectClient } from "./clients/rest-project.client"; +import { StandaloneProjectClient } from "./clients/standalone-project.client"; +import { SidebarComponent } from "./components/sidebar/sidebar.component"; +import { DashboardComponent } from "./components/dashboard/dashboard.component"; +import { MaterialModule } from "@angular/material"; +import 'hammerjs'; +import { SidebarListComponent } from './components/sidebar-list/sidebar-list.component'; +import { IconGeneratorComponent } from './components/icon-generator/icon-generator.component'; +import { SortByHttpMethodPipe } from "./pipes/sort-by-http-method.pipe"; +import { ProjectComponent } from "./components/project/project.component"; +import { NotEmptyPipe } from "./pipes/not-empty.pipe"; +import { SimpleCardComponent } from './components/simple-card/simple-card.component'; +import { SortByKeyPipe } from "./pipes/sort-by-key.pipe"; +import { ObjectIteratorPipe } from "./pipes/object-iterator.pipe"; +import { FilterByFieldPipe } from "./pipes/filter-by-field.pipe"; +import { EmptyPipe } from "./pipes/empty.pipe"; +import { EndpointGroupComponent } from "./components/endpoint-group/endpoint-group.component"; +import { EndpointComponent } from "./components/endpoint/endpoint.component"; +import { PathHighlightComponent } from "./components/path-highlight/path-highlight.component"; +import { ProblemBoxComponent } from "./components/problem-box/problem-box.component"; +import { BodyRenderComponent } from "./components/body-render/body-render.component"; +import { ModelComponent } from "./components/model/model.component"; + +@NgModule({ + declarations: [ + AppComponent, + SidebarComponent, + DashboardComponent, + SidebarListComponent, + IconGeneratorComponent, + ProjectComponent, + SimpleCardComponent, + EndpointGroupComponent, + EndpointComponent, + PathHighlightComponent, + ProblemBoxComponent, + BodyRenderComponent, + ModelComponent, + + SortByHttpMethodPipe, + SortByKeyPipe, + FilterByFieldPipe, + NotEmptyPipe, + EmptyPipe, + ObjectIteratorPipe + ], + imports: [ + BrowserModule, + FormsModule, + HttpModule, + MaterialModule.forRoot(), + RouterModule.forRoot([ + { + path: 'dashboard', + component: DashboardComponent, + }, + { + path: 'projects/:project', + component: ProjectComponent, + }, + { + path: '', + pathMatch: 'full', + redirectTo: 'dashboard' + } + ], {useHash: true}), + PrettyJsonModule + ], + providers: [ + ProjectService, + {provide: 'ProjectClient', useClass: environment.standalone ? StandaloneProjectClient : RestProjectClient } + ], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/microdocs-ui/src/app/clients/project.client.ts b/microdocs-ui/src/app/clients/project.client.ts new file mode 100644 index 00000000..422ad665 --- /dev/null +++ b/microdocs-ui/src/app/clients/project.client.ts @@ -0,0 +1,35 @@ +import { ProjectTree, Project, Environments, ProjectChangeRule, ProblemResponse } from "@maxxton/microdocs-core/domain"; +import { Observable } from "rxjs/Observable"; +import { Response } from "@angular/http"; + +/** + * @author Steven Hermans + */ +export interface ProjectClient { + + /** + * Loads all projects + */ + loadProjects( env:string ):Observable; + + /** + * Load project + */ + loadProject( env:string, title:string, version?:string ):Observable; + + /** + * Load all the environments + */ + getEnvs():Observable<{[key:string]:Environments}>; + + + importProject( env:string, project:Project, title:string, group:string, version:string ):Observable; + + deleteProject( env:string, title:string, version?:string ):Observable; + + updateProject( env:string, title:string, rules:ProjectChangeRule[], version?:string ):Observable; + + reindex( env:string ):Observable; + + +} \ No newline at end of file diff --git a/microdocs-ui/src/app/clients/rest-project.client.ts b/microdocs-ui/src/app/clients/rest-project.client.ts new file mode 100644 index 00000000..458b3735 --- /dev/null +++ b/microdocs-ui/src/app/clients/rest-project.client.ts @@ -0,0 +1,87 @@ +import { Injectable } from "@angular/core"; +import { Http, Response } from "@angular/http"; +import { + Get, Path, Put, Patch, Query, Body, Map, Produces, MediaType, Client, Delete, + RestClient, HttpClient +} from "@maxxton/angular-rest"; +import { ProjectTree, Project, Environments, ProjectChangeRule, ProblemResponse } from "@maxxton/microdocs-core/domain"; +import { Observable } from "rxjs/Observable"; +import { SchemaHelper } from "@maxxton/microdocs-core/helpers/schema/schema.helper"; +import { ProjectClient } from "./project.client"; + +/** + * Client for integration with the microdocs-server implementation. + * Uses the Rest api of the microdocs-server + */ +@Client( { + serviceId: 'microdocs-server', + baseUrl: "/api/v1", + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + } +} ) +@Injectable() +export class RestProjectClient extends RestClient implements ProjectClient { + + constructor( private http:Http ) { + super(http); + } + + /** + * Loads all projects + * @httpQuery env for which environment, default is the current one + * @httpResponse 200 {TreeNode} + */ + @Get( "/projects" ) + @Map( resp => ProjectTree.link( resp.json() ) ) + public loadProjects( @Query( "env" ) env:string ):Observable { + return null; + } + + /** + * Load project + * @httpPath title name of the project + * @httpQuery version specific version, or if empty the latest + * @httpQuery env for which environment, default is the current one + * @httpBody body {Project} + * @httpResponse 200 {Project} + */ + @Get( "/projects/{title}" ) + @Map( resp => SchemaHelper.resolveObject( resp.json() ) ) + public loadProject( @Query( "env" ) env:string, @Path( "title" ) title:string, @Query( "version" ) version?:string ):Observable { + return null; + } + + /** + * Load all the environments + * @httpResponse 200 {{[key: string]: Environments}} + */ + @Get( "/envs" ) + @Map( resp => resp.json() ) + public getEnvs():Observable<{[key:string]:Environments}> { + return null + } + + @Put( "/projects/{title}" ) + @Map( resp => resp.json() ) + public importProject( @Query( "env" ) env:string, @Body project:Project, @Path( "title" ) name:string, @Query( "group" ) group:string, @Query( "version" ) version:string ):Observable { + return null; + } + + @Delete( "/projects/{title}" ) + public deleteProject( @Query( 'env' ) env:string, @Path( 'title' ) name:string, @Query( 'version' ) version?:string ):Observable { + return null; + } + + @Patch( "/projects/{title}" ) + public updateProject( @Query( 'env' ) env:string, @Path( 'title' ) name:string, @Body rules:ProjectChangeRule[], @Query( 'version' ) version?:string ):Observable { + return null; + } + + @Put( "/reindex" ) + reindex( @Query( 'env' ) env:string ):Observable { + return null; + } + +} diff --git a/microdocs-ui/src/app/clients/standalone-project.client.ts b/microdocs-ui/src/app/clients/standalone-project.client.ts new file mode 100644 index 00000000..c113b260 --- /dev/null +++ b/microdocs-ui/src/app/clients/standalone-project.client.ts @@ -0,0 +1,86 @@ +import { Injectable } from "@angular/core"; +import { Http, Response } from "@angular/http"; +import { + RestClient, + HttpClient, + Get, + Path, + Query, + Map, + Produces, MediaType, Client +} from "@maxxton/angular-rest"; +import { ProjectTree, Project, Environments, ProjectChangeRule, ProblemResponse } from "@maxxton/microdocs-core/domain"; +import { Observable } from "rxjs/Observable"; +import { SchemaHelper } from "@maxxton/microdocs-core/helpers/schema/schema.helper"; +import { ProjectClient } from "./project.client"; + +/** + * Client for the standalone implementation. + * Uses static json files + */ +@Client( { + serviceId: 'static', + baseUrl: "/data", + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + } +} ) +@Injectable() +export class StandaloneProjectClient extends RestClient implements ProjectClient{ + + constructor( private http:Http ) { + super(http); + } + + /** + * Loads all projects + * @httpPath env for which environment, default is the current one + * @httpResponse 200 {TreeNode} + */ + @Get( "/projects-{env}.json" ) + @Map( resp => ProjectTree.link( resp.json() ) ) + public loadProjects( @Path( "env" ) env:string ):Observable { + return null; + } + + /** + * Load project + * @httpPath title name of the project + * @httpPath version specific version, or if empty the latest + * @httpPath env for which environment, default is the current one + * @httpResponse 200 {Project} + */ + @Get( "/projects/{title}-{env}-{version}.json" ) + @Map( resp => SchemaHelper.resolveObject( resp.json() ) ) + public loadProject( @Path( "env" ) env:string, @Path( "projectName" ) projectName:string, @Path( "version" ) version?:string ):Observable { + return null; + } + + /** + * Load all the environments + * @httpResponse 200 {{[key:string]:Environments}} map of environments + */ + @Get( "/envs.json" ) + @Map( resp => resp.json() ) + public getEnvs():Observable<{[key:string]:Environments}> { + return null + } + + public importProject( env:string, project:Project, name:string, group:string, version:string ):Observable { + throw new Error( 'Import project is not supported in standalone' ); + } + + public deleteProject( env:string, name:string, version?:string ):Observable { + throw new Error( 'Delete project is not supported in standalone' ); + } + + public updateProject( env:string, name:string, rules:ProjectChangeRule[], version?:string ):Observable { + throw new Error( 'Update project is not supported in standalone' ); + } + + public reindex( env:string ):Observable { + throw new Error( 'Reindex is not supported in standalone' ); + } + +} diff --git a/microdocs-ui/src/app/components/body-render/body-render.component.html b/microdocs-ui/src/app/components/body-render/body-render.component.html new file mode 100644 index 00000000..bcfe4a9a --- /dev/null +++ b/microdocs-ui/src/app/components/body-render/body-render.component.html @@ -0,0 +1,14 @@ +
+ +
\ No newline at end of file diff --git a/microdocs-ui/src/app/components/body-render/body-render.component.scss b/microdocs-ui/src/app/components/body-render/body-render.component.scss new file mode 100644 index 00000000..3b2e1062 --- /dev/null +++ b/microdocs-ui/src/app/components/body-render/body-render.component.scss @@ -0,0 +1,6 @@ + +@import '../../../assets/scss/settings'; + +.mime{ + color: $primary +} diff --git a/microdocs-ui/src/app/components/body-render/body-render.component.ts b/microdocs-ui/src/app/components/body-render/body-render.component.ts new file mode 100644 index 00000000..866e992a --- /dev/null +++ b/microdocs-ui/src/app/components/body-render/body-render.component.ts @@ -0,0 +1,32 @@ +import {Component, Input} from "@angular/core"; + +import {SchemaHelper} from '@maxxton/microdocs-core/helpers'; +import {Schema} from '@maxxton/microdocs-core/domain'; + +@Component({ + selector: 'body-render', + templateUrl: 'body-render.component.html', + styleUrls: ['body-render.component.scss'] +}) +export class BodyRenderComponent { + + @Input() + private contentTypes:string[]; + @Input() + private schema:Schema; + @Input() + private mimes:string[]; + @Input() + schemaList:{[key:string]:Schema}; + + private example:string; + + ngOnInit(){ + + } + + ngOnChanges(){ + this.example = SchemaHelper.generateExample(this.schema, undefined, [], this.schemaList); + } + +} diff --git a/microdocs-ui/src/app/components/dashboard/dashboard.component.html b/microdocs-ui/src/app/components/dashboard/dashboard.component.html new file mode 100644 index 00000000..8df05256 --- /dev/null +++ b/microdocs-ui/src/app/components/dashboard/dashboard.component.html @@ -0,0 +1,10 @@ +
+ + +
diff --git a/microdocs-ui/src/app/components/dashboard/dashboard.component.ts b/microdocs-ui/src/app/components/dashboard/dashboard.component.ts new file mode 100644 index 00000000..c0cf3b24 --- /dev/null +++ b/microdocs-ui/src/app/components/dashboard/dashboard.component.ts @@ -0,0 +1,42 @@ +import {Component} from "@angular/core"; + +import {ProjectTree} from '@maxxton/microdocs-core/domain'; +//import {DependencyGraph} from '../../panels/dependency-graph/dependency-graph'; +import {ProjectService} from "../../services/project.service"; +import {Subject} from "rxjs/Subject"; +import {ReplaySubject} from "rxjs/ReplaySubject"; +import {Router} from "@angular/router"; +//import {WelcomePanel} from "../../panels/welcome-panel/welcome.panel"; + +/** + * Dashboard route (page that shows the most important information to a user) + * + * @author R. Sonke + */ + +@Component({ + selector: 'dashboard', + providers: [], +// templateUrl: 'dashboard.component.html' + template: 'dashboard' +}) + +export class DashboardComponent { + + empty:boolean = false; + nodes:Subject = new ReplaySubject(1); + + constructor(private projectService:ProjectService, private router:Router){ + this.projectService.getProjects().subscribe(notification => { + notification.do(data => { + this.nodes.next(data as ProjectTree); + if ((data as ProjectTree).projects) { + this.empty = (data as ProjectTree).projects.length == 0; + } else { + this.empty = true; + } + }); + }); + } + +} diff --git a/microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.html b/microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.html new file mode 100644 index 00000000..47ebc7f4 --- /dev/null +++ b/microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.html @@ -0,0 +1,21 @@ +
+
+ + + + {{endpoint._id | uppercase}} + + + +
+ +
diff --git a/microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.scss b/microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.scss new file mode 100644 index 00000000..dd7319d0 --- /dev/null +++ b/microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.scss @@ -0,0 +1,82 @@ + +@import '../../../assets/scss/settings'; + +.endpoint-group { + display: block; + box-shadow: rgba(0, 0, 0, 0.227451) 0px 3px 10px 0px, rgba(0, 0, 0, 0.156863) 0px 3px 10px 0px !important; + border-radius: 2px; + margin-bottom: 10px; + background-color: white; + + .group-header { + padding: 15px; + font-size: 14px; + font-family: monospace; + cursor: pointer; + + .path { + cursor: text; + } + } + + .methods > span { + position: relative; + padding: 5px 10px; + margin-left: 10px; + background-color: map_get($md-blue-grey, 500); + color: white; + border-radius: 5px; + + md-icon { + position: absolute; + top: -8px; + right: -14px; + } + } + + .methods > span.get { + background-color: map_get($md-green, 500); + } + + .methods > span.post { + background-color: map_get($md-blue, 500); + } + + .methods > span.put { + background-color: map_get($md-orange, 500); + } + + .methods > span.delete { + background-color: map_get($md-red, 500); + } + + .methods > span.path { + background-color: map_get($md-brown, 500); + } + + .group-content { + height: auto; + padding: 0 20px; + opacity: 1; + overflow: hidden; + transition: 0.2s ease opacity; + } + + .group-content.hidden { + opacity: 0; + height: 0; + } + + .group-content .methods { + border-top: 1px solid #d6d6d6; + margin-bottom: 20px; + padding-top: 1px; + } + + .group-content .methods > span { + display: inline-block; + margin: 10px 0; + margin-bottom: 20px; + font-size: 18px; + } +} diff --git a/microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.ts b/microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.ts new file mode 100644 index 00000000..cc4ef3ec --- /dev/null +++ b/microdocs-ui/src/app/components/endpoint-group/endpoint-group.component.ts @@ -0,0 +1,21 @@ +import {Component, Input} from "@angular/core"; +import { Schema } from "@maxxton/microdocs-core/domain"; + +/** + * @author Steven Hermans + */ +@Component({ + selector: 'endpoint-group', + templateUrl: 'endpoint-group.component.html', + styleUrls: ['endpoint-group.component.scss'] +}) +export class EndpointGroupComponent { + + @Input() + endpointGroup:any; + @Input() + schemaList:{[key:string]:Schema}; + + hidden = true; + +} diff --git a/microdocs-ui/src/app/components/endpoint/endpoint.component.html b/microdocs-ui/src/app/components/endpoint/endpoint.component.html new file mode 100644 index 00000000..2793456f --- /dev/null +++ b/microdocs-ui/src/app/components/endpoint/endpoint.component.html @@ -0,0 +1,62 @@ +
+ +
+ + {{endpoint.operationId}}() + {{endpoint.operationId}} + - + + {{endpoint.description}} +
+ + +
diff --git a/microdocs-ui/src/app/components/endpoint/endpoint.component.scss b/microdocs-ui/src/app/components/endpoint/endpoint.component.scss new file mode 100644 index 00000000..838b9afe --- /dev/null +++ b/microdocs-ui/src/app/components/endpoint/endpoint.component.scss @@ -0,0 +1,77 @@ + +@import '../../../assets/scss/settings'; +.endpoint { + .request, endpoint .response { + font-family: monospace; + } + + tr, endpoint th, endpoint td { + border: none !important; + background: none !important; + padding-top: 2px; + padding-bottom: 2px; + } + + tr > *:first-child { + color: #2196F3; + text-align: left; + } + + tr > *:nth-child(2)::before { + content: ": "; + } + + tr > *:nth-child(2) { + color: #4CAF50; + } + + tr > *:nth-child(3) span::before { + content: " = "; + } + + tr > *:nth-child(4) { + color: #AFAFAF; + } + + tr > *:nth-child(5)::before { + content: "- "; + } + + h4{ + font-size: 18px; + margin-top: 20px; + color: $accent; + } + + h5{ + font-size: 14px; + font-weight: normal; + font-family: inherit; + margin-top: 10px; + } + + .mime { + color: #2196F3; + } + + .json-preview { + height: 200px; + resize: none; + cursor: text !important; + font-family: monospace; + } + + .client-header { + font-size: 14px; + margin-bottom: 15px; + } + + .response-description { + padding-top: 5px; + padding-bottom: 5px; + } + + .response-status { + color: #2196F3; + } +} diff --git a/microdocs-ui/src/app/components/endpoint/endpoint.component.ts b/microdocs-ui/src/app/components/endpoint/endpoint.component.ts new file mode 100644 index 00000000..d9f89023 --- /dev/null +++ b/microdocs-ui/src/app/components/endpoint/endpoint.component.ts @@ -0,0 +1,35 @@ +import {Component, Input} from '@angular/core'; +import {Path, Schema, Project, Method} from '@maxxton/microdocs-core/domain'; + +@Component({ + selector: 'endpoint', + templateUrl: 'endpoint.component.html', + styleUrls: ['endpoint.component.scss'] +}) +export class EndpointComponent { + + @Input() + private endpoint:Path; + @Input() + private path:string; + @Input() + schemaList:{[key:string]:Schema}; + + getStatusName(statusCode : string){ + switch(statusCode.trim()){ + case '200': return 'OK'; + case '201': return 'CREATED'; + case '204': return 'NO CONTENT'; + case '400': return 'BAD REQUEST'; + case '401': return 'UNAUTHORIZED'; + case '403': return 'FORBIDDEN'; + case '404': return 'NOT FOUND'; + case '405': return 'METHOD NOT ALLOWED'; + case '409': return 'CONFLICT'; + case '500': return 'INTERNAL SERVER ERROR'; + case '503': return 'SERVICE UNAVAILABLE'; + default: return ''; + } + } + +} diff --git a/microdocs-ui/src/app/components/icon-generator/icon-generator.component.html b/microdocs-ui/src/app/components/icon-generator/icon-generator.component.html new file mode 100644 index 00000000..a594e9e6 --- /dev/null +++ b/microdocs-ui/src/app/components/icon-generator/icon-generator.component.html @@ -0,0 +1,3 @@ + + {{initials}} + diff --git a/microdocs-ui/src/app/components/icon-generator/icon-generator.component.scss b/microdocs-ui/src/app/components/icon-generator/icon-generator.component.scss new file mode 100644 index 00000000..52843318 --- /dev/null +++ b/microdocs-ui/src/app/components/icon-generator/icon-generator.component.scss @@ -0,0 +1,29 @@ +@import "../../../assets/scss/settings"; + +.icon-generator span { + + display: inline-block; + padding: 7px 5px; + height: 32px; + width: 32px; + border-radius: 100%; + color: white; + text-align: center; + margin-right: 5px; + font-size: 17px !important; +} + +/** +** generate a list of components based on the color-palette, +**/ +.icon-generator { + @each $name, $color in $colors { + span.#{$name} { + background: $color; + } + } + + span.amber, span.yellow { + color: black; + } +} diff --git a/microdocs-ui/src/app/components/icon-generator/icon-generator.component.ts b/microdocs-ui/src/app/components/icon-generator/icon-generator.component.ts new file mode 100644 index 00000000..43f02fda --- /dev/null +++ b/microdocs-ui/src/app/components/icon-generator/icon-generator.component.ts @@ -0,0 +1,41 @@ +import { Component, ElementRef, Input, Renderer } from "@angular/core"; +import { StringUtil } from "../../helpers/string.util"; + +@Component( { + selector: 'icon-generator', + templateUrl: 'icon-generator.component.html', + styleUrls: [ 'icon-generator.component.scss' ] +} ) +export class IconGeneratorComponent { + + @Input( "text" ) + private text: string; + + @Input( "color" ) + private color: string; + + private initials: string; + + constructor( private el: ElementRef, private renderer: Renderer ) { + } + + ngOnChanges() { + if ( !this.text ) { + this.initials = null; + return; + } + let first = this.text.substr( 0, 1 ); + let second = this.text.substr( 1, 1 ); + this.initials = first.toUpperCase() + second.toLowerCase(); + + if ( !this.color ) { + this.color = StringUtil.getColorCodeFromString( this.text ); + + if ( !this.color ) { + this.color = 'blue-grey'; + } + } +// this.renderer.setElementAttribute( this.el.nativeElement.querySelector('.icon-generator'), 'class', selectedColor ); + } + +} diff --git a/microdocs-ui/src/app/components/model/model.component.html b/microdocs-ui/src/app/components/model/model.component.html new file mode 100644 index 00000000..497cad6f --- /dev/null +++ b/microdocs-ui/src/app/components/model/model.component.html @@ -0,0 +1,10 @@ + +
{{schema.description}}
+ + +
diff --git a/microdocs-ui/src/app/components/model/model.component.scss b/microdocs-ui/src/app/components/model/model.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/microdocs-ui/src/app/components/model/model.component.ts b/microdocs-ui/src/app/components/model/model.component.ts new file mode 100644 index 00000000..68b2be6c --- /dev/null +++ b/microdocs-ui/src/app/components/model/model.component.ts @@ -0,0 +1,36 @@ +import {Component, Input} from "@angular/core"; +import {PrettyJsonComponent} from 'angular2-prettyjson'; + +import {SchemaHelper} from "@maxxton/microdocs-core/helpers"; +import {Schema} from "@maxxton/microdocs-core/domain"; + +@Component({ + selector: 'model', + templateUrl: 'model.component.html', + styleUrls: ['model.component.scss'] +}) +export class ModelComponent { + + @Input() + schema:Schema; + + example:{}; + + ngOnChanges() { + this.example = SchemaHelper.generateExample(this.schema); + } + + getSubTitle(schema:Schema){ + let tables = ""; + if(schema.mappings != undefined && schema.mappings != null && + schema.mappings.relational != undefined && schema.mappings.relational != null && + schema.mappings.relational.tables != undefined && schema.mappings.relational.tables != null){ + schema.mappings.relational.tables.forEach(table => tables += table + ", "); + if(tables.length > 1){ + tables = "(" + tables.substring(0, tables.length-2) + ")"; + } + } + return tables; + } + +} diff --git a/microdocs-ui/src/app/components/path-highlight/path-highlight.component.html b/microdocs-ui/src/app/components/path-highlight/path-highlight.component.html new file mode 100644 index 00000000..27b46491 --- /dev/null +++ b/microdocs-ui/src/app/components/path-highlight/path-highlight.component.html @@ -0,0 +1 @@ + diff --git a/microdocs-ui/src/app/components/path-highlight/path-highlight.component.scss b/microdocs-ui/src/app/components/path-highlight/path-highlight.component.scss new file mode 100644 index 00000000..e9f0dfa9 --- /dev/null +++ b/microdocs-ui/src/app/components/path-highlight/path-highlight.component.scss @@ -0,0 +1,8 @@ + +@import '../../../assets/scss/settings'; + +.highlight-container{ + .highlight{ + color: $primary; + } +} diff --git a/microdocs-ui/src/app/components/path-highlight/path-highlight.component.ts b/microdocs-ui/src/app/components/path-highlight/path-highlight.component.ts new file mode 100644 index 00000000..606f537f --- /dev/null +++ b/microdocs-ui/src/app/components/path-highlight/path-highlight.component.ts @@ -0,0 +1,26 @@ +import {Component, Input, ElementRef, SimpleChanges, OnChanges} from "@angular/core"; +/** + * @author Steven Hermans + */ +@Component({ + selector: 'path-highlight', + templateUrl: 'path-highlight.component.html', + styleUrls: [ 'path-highlight.component.scss' ] +}) +export class PathHighlightComponent implements OnChanges{ + + constructor(private el:ElementRef){ + } + + @Input() + path:string; + + highlightPath = ''; + + ngOnChanges(changes: SimpleChanges){ + this.highlightPath = this.path + .replace(new RegExp("\{", 'g'), '{') + .replace(new RegExp("\}", 'g'), '}'); + } + +} diff --git a/microdocs-ui/src/app/components/problem-box/problem-box.component.html b/microdocs-ui/src/app/components/problem-box/problem-box.component.html new file mode 100644 index 00000000..d3a503e5 --- /dev/null +++ b/microdocs-ui/src/app/components/problem-box/problem-box.component.html @@ -0,0 +1,12 @@ +
+ error + {{problem.message}} +
+
+ warning + {{problem.message}} +
+
+ info + {{problem.message}} +
diff --git a/microdocs-ui/src/app/components/problem-box/problem-box.component.scss b/microdocs-ui/src/app/components/problem-box/problem-box.component.scss new file mode 100644 index 00000000..336a5a85 --- /dev/null +++ b/microdocs-ui/src/app/components/problem-box/problem-box.component.scss @@ -0,0 +1,3 @@ +.problem-box{ + +} diff --git a/microdocs-ui/src/app/components/problem-box/problem-box.component.ts b/microdocs-ui/src/app/components/problem-box/problem-box.component.ts new file mode 100644 index 00000000..73dba776 --- /dev/null +++ b/microdocs-ui/src/app/components/problem-box/problem-box.component.ts @@ -0,0 +1,16 @@ + +import {Component, Input} from "@angular/core"; +import {Problem} from "@maxxton/microdocs-core/domain"; + + +@Component({ + selector: 'problem-box', + templateUrl: 'problem-box.component.html', + styleUrls: ['problem-box.component.scss'] +}) +export class ProblemBoxComponent{ + + @Input() + problems:Problem[]; + +} diff --git a/microdocs-ui/src/app/components/project/project.component.html b/microdocs-ui/src/app/components/project/project.component.html new file mode 100644 index 00000000..3ca74cc3 --- /dev/null +++ b/microdocs-ui/src/app/components/project/project.component.html @@ -0,0 +1,127 @@ + + + + {{title}} + + + + broken_image + file_download + edit + delete + +
+
+ + +
+
+ + + + + diff --git a/microdocs-ui/src/app/components/project/project.component.scss b/microdocs-ui/src/app/components/project/project.component.scss new file mode 100644 index 00000000..8eec1c31 --- /dev/null +++ b/microdocs-ui/src/app/components/project/project.component.scss @@ -0,0 +1,195 @@ + +@import '../../../assets/scss/settings'; + +.example-spacer { + flex: 1 1 auto; +} + +md-toolbar{ + background-color: white; + box-shadow: rgba(0, 0, 0, 0.117647) 0px 1px 6px 0px, rgba(0, 0, 0, 0.117647) 0px 1px 6px 0px; + position: absolute; + top: 0; + left: 0; + right: 0; + + .version-box{ + background: none; + border: none; + outline: none; + height: 40px; + cursor: pointer; + font-size: 14px; + margin-left: 20px; + } + + md-icon{ + width: 50px; + height: 50px; + padding: 13px; + border-radius: 100%; + cursor: pointer; + } + + md-icon.export-button{ + color: map_get($md-blue, 500); + } + + md-icon.edit-button{ + color: map_get($md-green, 500); + } + + md-icon.delete-button{ + color: map_get($md-red, 500); + } + + md-icon.deprecate-button{ + color: map_get($md-blue-grey, 500); + } + + md-icon:hover, md-icon.selected{ + background-color: rgba(0, 0, 0, 0.1); + } +} + +.project-content{ + position: absolute; + top: 64px; + left: 0; + right: 0; + bottom: 0; + overflow: auto; +} +.page-content{ + margin: 2rem auto 0 8rem; + width: auto !important; + max-width: 1200px !important; +} + +.error-page .error{ + text-align: center; + md-icon{ + font-size: 40px; + width: 40px; + height: 40px; + } +} + + + + +project-route .page-content{ + width: auto !important; + max-width: 106.66667rem !important; + padding: 0px !important; +} + +project-route .project-content{ + transition: 0.2s ease opacity; + opacity: 1; +} + +project-route .project-content.loading{ + opacity: 0; +} + +project-route .card { + box-shadow: none !important; + padding: 5px 20px; + background: none; + margin: 0; +} + +project-route .card .card-section.header{ + border: none; +} + +project-route .card .float-right{ + float: none; + position: relative; + top: 3px; +} + +project-route card .card-section.content{ + padding-top: 0px; +} + +project-route .info-content{ + min-height: 20px; +} + +project-route dependency-graph{ + display: block; + height: 200px; + margin-top: 10px; + border: 1px solid #d6d6d6; + border-radius: 2px; +} + +project-route .project-desc{ + font-size: 14px; +} + +project-route .time-box{ + position: absolute; + right: 20px; + top: 0; + text-align: right; + font-size: 12px; + color: gray; + line-height: 13px; +} + +project-route .project-links ul{ + margin: 5px 0; +} + +project-route model-panel card.model-card .card{ + box-shadow: rgba(0, 0, 0, 0.227451) 0px 3px 10px 0px, rgba(0, 0, 0, 0.156863) 0px 3px 10px 0px !important; + border: none; +} + +project-route .client-problem .icon{ + position: relative; + top: 5px; +} + +project-route .client-problem .client-problem-desc{ + color: map_get($md-red, 500); +} + +project-route modal .card{ + background-color: white; + box-shadow: rgba(0, 0, 0, 0.227451) 0px 3px 10px 0px, rgba(0, 0, 0, 0.156863) 0px 3px 10px 0px !important; +} + +project-route .error-page{ + h1 { + padding: 40px; + text-align: center; + font-size: 2rem; + text-transform: capitalize; + font-weight: 400; + color: $accent; + } + + h1.error{ + color: map_get($md-red, 500); + } + + h1 i { + font-size: 40px; + height: 40px; + width: 40px; + position: relative; + top: 10px; + margin-right: 10px; + } + + h2 { + text-align: center; + font-size: 1.5rem; + font-weight: 400; + color: #656565; + } +} diff --git a/microdocs-ui/src/app/components/project/project.component.ts b/microdocs-ui/src/app/components/project/project.component.ts new file mode 100644 index 00000000..db0119cf --- /dev/null +++ b/microdocs-ui/src/app/components/project/project.component.ts @@ -0,0 +1,190 @@ +import {Component} from "@angular/core"; +import {ActivatedRoute, Router, Params} from "@angular/router"; +import {Subject} from "rxjs/Subject"; +import {ReplaySubject} from "rxjs/ReplaySubject"; +import {Subscription} from "rxjs/Subscription"; + +import {Project, Path, Method, Schema, Dependency, ProjectTree, DependencyTypes} from "@maxxton/microdocs-core/domain"; +import {SchemaHelper} from "@maxxton/microdocs-core/helpers/schema/schema.helper"; +import { ProjectChangeRule } from "@maxxton/microdocs-core/domain/settings/project-change-rule.model"; + +import {ProjectService} from "../../services/project.service"; +import {environment} from '../../../environments/environment' +import { StringUtil } from "../../helpers/string.util"; + + +@Component({ + selector: 'project-route', + templateUrl: 'project.component.html', + styleUrls: ['project.component.scss'] +}) +export class ProjectComponent { + + config = environment; + + private projects:ProjectTree; + private nodes:Subject = new ReplaySubject(1); + + private title:string; + private version:string; + private versions:string[]; + private project:Project = {}; + + private loading:boolean = true; + private notFound:boolean = false; + private error:string; + + private showExportModal:boolean = false; + private showDeleteModal:boolean = false; + private showEditModal:boolean = false; + private subscribtions:Subscription[] = []; + private projectSubscribtion:Subscription; + + private queryParams:Params; + private pathParams:Params; + + private color = 'blue-gray'; + + private rest = DependencyTypes.REST; + private database = DependencyTypes.DATABASE; + private uses = DependencyTypes.USES; + private includes = DependencyTypes.INCLUDES; + + constructor(private projectService:ProjectService, + private activatedRoute:ActivatedRoute, + private router:Router) { + + //load metadata + this.projectService.getProjects().subscribe(notification => { + notification.do(projects => { + this.projects = projects; + this.updateNodes(); + }); + }); + } + + ngOnInit() { + this.subscribtions.push(this.activatedRoute.queryParams.subscribe(params => { + this.loading = true; + this.notFound = false; + this.error = undefined; + this.queryParams = params; + if (this.queryParams['version'] && this.pathParams) { + setTimeout(() => this.init()); + } + })); + this.subscribtions.push(this.activatedRoute.params.subscribe(params => { + this.loading = true; + this.notFound = false; + this.error = undefined; + this.pathParams = params; + if (this.queryParams != undefined) { + setTimeout(() => this.init(), 100); + } + })); + } + + ngOnDestroy() { + this.subscribtions.forEach(subscribtion => subscribtion.unsubscribe()); + this.subscribtions = []; + } + + init() { + this.version = this.queryParams['version']; + this.title = this.pathParams['project']; + this.color = StringUtil.getColorCodeFromString(this.title); + this.updateNodes(); + this.loadProject(this.title, this.version); + } + + loadProject(title:string, version:string) { + if (this.projectSubscribtion) { + this.projectSubscribtion.unsubscribe(); + } + this.projectSubscribtion = this.projectService.getProject(title, version).subscribe(notification => { + notification.do(project => { + this.project = project; + this.loading = false; + this.notFound = false; + this.error = undefined; + }, (error) => { + this.loading = false; + if(notification.error.status == 404){ + this.notFound = true; + this.error = "Not Found"; + }else{ + this.notFound = false; + this.error = "Something went wrong" + } + }); + }); + } + + onChangeVersion(version:string) { + var url = '/projects/' + this.title; + this.router.navigate([url], { + queryParams: { + version: version, + env: this.projectService.getSelectedEnv() + } + }); + } + + getModelSourceLink(sourceLink:string, name:string, schema:Schema) { + if (sourceLink != null && sourceLink != undefined) { + var schemaSettings = { + class: { + type: schema.type, + simpleName: schema.name, + name: name, + path: name.replace(new RegExp('\\.', 'g'), '/'), + lineNumber: 0 + } + }; + + sourceLink = SchemaHelper.resolveString(sourceLink, schemaSettings); + } + return sourceLink; + } + + getDependencyLink(dependency:Dependency):string { + return '/projects/' + dependency['_id']; + } + + getLastDependencyParams(dependency:Dependency):{} { + return {version: dependency.latestVersion, env: this.projectService.getSelectedEnv()}; + } + + getDependencyParams(dependency:Dependency):{} { + return {version: dependency.version, env: this.projectService.getSelectedEnv()}; + } + + updateNodes() { + if (this.projects) { + this.projects.projects.forEach(project => { + if (project.title === this.title) { + this.versions = project.versions; + if(this.version && this.versions.indexOf(this.version) == -1){ + this.versions.push(this.version); + } + this.versions = this.versions.sort(); + } + }); + this.nodes.next(this.projects); + } + } + + toggleDeprecated(){ + this.project.deprecated = !this.project.deprecated; + let rules = [ + new ProjectChangeRule('deprecated', ProjectChangeRule.TYPE_ALTER, this.project.deprecated, ProjectChangeRule.SCOPE_VERSION) + ]; + this.projectService.updateProject(this.title, rules, this.version).subscribe(resp => { + this.projectService.refreshProject(this.title, this.version); + }); + } + + timeEquals(updateTime:string, publishTime:string):boolean{ + return new Date(updateTime).getTime() - new Date(publishTime).getTime() > 1000; + } +} diff --git a/microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.html b/microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.html new file mode 100644 index 00000000..f1c6a137 --- /dev/null +++ b/microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.html @@ -0,0 +1,34 @@ + + + + + + diff --git a/microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.scss b/microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.scss new file mode 100644 index 00000000..82e42544 --- /dev/null +++ b/microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.scss @@ -0,0 +1,45 @@ + +@import '../../../assets/scss/settings'; + + +md-list{ + padding: 0; + font-size: 11px; + + a{ + text-decoration: none ; + } + + md-list-item{ + cursor: pointer; + + md-icon{ + width: 24px; + height: 24px; + } + + icon-generator{ + width: 34px; + position: relative; + top: 3px; + } + + md-list-item{ + padding-left: 20px; + } + + @each $name, $color in $colors { + .#{$name} md-icon { + color: $color; + } + } + } + + md-list-item:hover{ + color: $accent; + background: #f4f4f4; + h4{ + color: $accent; + } + } +} diff --git a/microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.ts b/microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.ts new file mode 100644 index 00000000..6a41829c --- /dev/null +++ b/microdocs-ui/src/app/components/sidebar-list/sidebar-list.component.ts @@ -0,0 +1,40 @@ +import { Component, Input } from "@angular/core"; +import { RouteInfo } from "../../domain/route-info.model"; + +@Component( { + selector: 'sidebar-list', + templateUrl: 'sidebar-list.component.html', + styleUrls: [ 'sidebar-list.component.scss' ] +} ) +export class SidebarListComponent { + + constructor() { + } + + @Input() + private routes: any[]; + + public getIcon( route: RouteInfo ): string { + if ( route.open ) { + return route.iconOpen || route.icon; + } else { + return route.icon; + } + } + + public isLink( route: RouteInfo):boolean{ + return !!(route.path && route.path.trim()); + } + + public toggleRoute(route:RouteInfo):void{ + route.open = !route.open; + } + + public getColor(route:RouteInfo):string{ + if(route.generateIconColor){ + return route.generateIconColor; + } + return null; + } + +} diff --git a/microdocs-ui/src/app/components/sidebar/sidebar.component.html b/microdocs-ui/src/app/components/sidebar/sidebar.component.html new file mode 100644 index 00000000..6368194d --- /dev/null +++ b/microdocs-ui/src/app/components/sidebar/sidebar.component.html @@ -0,0 +1,47 @@ + + diff --git a/microdocs-ui/src/app/components/sidebar/sidebar.component.scss b/microdocs-ui/src/app/components/sidebar/sidebar.component.scss new file mode 100644 index 00000000..137fd00d --- /dev/null +++ b/microdocs-ui/src/app/components/sidebar/sidebar.component.scss @@ -0,0 +1,176 @@ + +@import '../../../assets/scss/settings'; + +.sidebar { + width: 250px; + height: 100%; + display: flex; + flex-flow: column nowrap; + align-items: stretch; + + a.logo { + height: 88px; + position: relative; + display: block; + background-color: $primary; + padding-top: 24px; + padding-bottom: 64px; + font-weight: bold; + font-size: 23px; + text-decoration: none; + + h1 { + color: white; + text-align: center; + font-size: 24px; + } + + .logo-left, .logo-right{ + position: absolute; + height: 50px; + width: 50px; + background-image: url(../../../assets/images/microdocs-white.png); + background-size: contain; + } + + .logo-left{ + bottom: -10px; + left: 5px; + } + + .logo-right{ + height: 50px; + top: -10px; + right: 5px; + } + } + + .env-box { + position: absolute; + top: 48px; + right: 0; + + select { + -webkit-appearance: none; + -moz-appearance: none; + padding: 0.5rem; + padding-right: 1.625rem; + background: url(../../../assets/images/arrow_down.png) no-repeat right transparent; + background-size: 24px; + border: none; + color: white; + display: block; + width: 100%; + height: 2.4rem; + margin: 0 0 0.8rem 0; + outline: none; + + option { + color: black; + } + } + } + + .search-box{ + position: relative; + min-height: 33px; + + md-icon{ + position: absolute; + left: 12px; + top: 7px; + font-size: 21px; + } + + input { + -webkit-appearance: none; + -moz-appearance: none; + display: block; + width: 100%; + height: 2.4rem; + padding: 0.5rem; + padding-left: 42px; + border-radius: 0; + background: #fff; + color: #000; + font-size: 1rem; + -webkit-font-smoothing: antialiased; + vertical-align: middle; + background: none !important; + border: none; + outline: none; + margin: 0; + } + } + + .grid-block{ + height: 100%; + flex: 1 1 auto; + flex-flow: row wrap; + flex-wrap: nowrap; + align-items: stretch; + justify-content: flex-start; + overflow: auto; + } + + .controls-box { + position: relative; + height: 90px; + border-top: 1px solid #eee; + + a { + position: absolute; + display: inline-block; + height: 100%; + width: 50%; + transition: 0.2s ease all; + + md-icon{ + position: absolute; + width: 100%; + height: 80%; + top: 10%; + text-align: center; + font-size: 25px; + } + + .control-text{ + position: absolute; + display: inline-block; + width: 100%; + top: 57%; + text-align: center; + } + } + + a.import-button{ + color: md-color(md-palette($md-green)); + width: 33%; + left: 0; + } + + a.export-button{ + left: 33%; + width: 34%; + color: md-color(md-palette($md-blue)); + } + + a.reindex-button{ + color: md-color(md-palette($md-blue-grey)); + width: 33%; + left: 67%; + } + a:hover{ + background-color: map_get($md-grey, 300); + } + } + + .footer{ + padding: 10px; + background-color: map_get($md-grey, 300); + font-size: 14px; + a{ + color: $primary; + } + } +} diff --git a/microdocs-ui/src/app/components/sidebar/sidebar.component.ts b/microdocs-ui/src/app/components/sidebar/sidebar.component.ts new file mode 100644 index 00000000..36ec4ad6 --- /dev/null +++ b/microdocs-ui/src/app/components/sidebar/sidebar.component.ts @@ -0,0 +1,152 @@ +import { Component, HostBinding, Input, Output, EventEmitter } from "@angular/core"; +import { Notification } from "rxjs/Notification"; +import { Observable } from "rxjs/Observable"; +import { ProjectTree } from "@maxxton/microdocs-core/domain"; +import { ProjectService } from "../../services/project.service"; +import { environment } from "../../../environments/environment"; +import { MdIconRegistry, MdSnackBar } from "@angular/material"; +import { RouteInfo } from "../../domain/route-info.model"; + +@Component( { + selector: 'sidebar-component', + templateUrl: 'sidebar.component.html', + styleUrls: [ 'sidebar.component.scss' ] +} ) + +export class SidebarComponent { + + private config: any = environment; + + private user = {}; + showImportModal = false; + showExportModal = false; + showSettingsModal = false; + + @HostBinding( 'class.big' ) + private showFullSideBar: boolean = true; + + + @Input() + projects: Observable>; + + menu: Object = []; + + searchQuery: string = ''; + + @Input() + envs: string[]; + + @Input() + selectedEnv: string; + + node: ProjectTree; + + @Output( 'envChange' ) + change = new EventEmitter(); + + constructor( private projectService: ProjectService, private snackbar:MdSnackBar) { + + } + + ngOnInit() { + this.projects.subscribe( notification => { + notification.do( node => { + this.node = node; + this.initMenu() + } ); + } ); + } + + onEnvVersion( newEnv ) { + this.change.emit( newEnv ); + } + + onSearchQueryChange( query ) { + this.searchQuery = query; + this.initMenu(); + } + + private initMenu() { + let pathPrefix = "projects/"; + let menus: Array = [ { + path: '/dashboard', + pathMatch: 'full', + pathParams: { env: this.selectedEnv }, + name: 'Overview', + icon: 'home' + } ]; + let filteredNodes = this.filterNodes( this.node, this.searchQuery ); + filteredNodes.projects.forEach( projectNode => { + let groupName = projectNode.group; + if ( groupName == undefined ) { + groupName = "default"; + } + // add group if it doesn't exists + if ( menus.filter( group => group.name == groupName ).length == 0 ) { + menus.push( { name: groupName, icon: 'folder', iconOpen: 'folder_open', children: [], open: true } ); + } + // add project + let problems = projectNode.problems; + let icon = null; + let iconColor = null; + if ( problems != undefined && problems != null && problems > 0 ) { + iconColor = 'red'; + icon = 'error'; + } + let groupRoute = menus.filter( group => group.name == groupName )[ 0 ]; + groupRoute.children.push( { + path: pathPrefix + projectNode.title, + pathParams: { version: projectNode.version, env: this.selectedEnv }, + name: projectNode.title, + postIcon: icon, + postIconColor: iconColor, + generateIcon: true + } ); + } ); + console.info( menus ); + this.menu = menus; + } + + private filterNodes( projectTree: ProjectTree, query: string ): ProjectTree { + let newNode = new ProjectTree(); + let keywords = query.split( ' ' ); + projectTree.projects.forEach( projectNode => { + let hit = true; + if ( !query || query.trim().length == 0 ) { + hit = true; + } else { + for ( let i = 0; i < keywords.length; i++ ) { + if ( projectNode.title.toLowerCase().indexOf( keywords[ i ].toLowerCase() ) == -1 ) { + hit = false; + if ( projectNode.tags ) { + projectNode.tags.forEach( tag => { + if ( tag.toLowerCase().indexOf( keywords[ i ].toLowerCase() ) != -1 ) { + hit = true; + } + } ); + } + } + } + } + if ( hit ) { + newNode.projects.push( projectNode ); + } + } ); + return newNode; + } + + doReindex() { + let time = new Date().getTime(); + let ref = this.snackbar.open("Reindexing " + this.projectService.getSelectedEnv() + " environment"); +// let notification = this.snackbarService.addNotification( "Reindexing " + this.projectService.getSelectedEnv() + " environment", undefined, undefined, 'refresh', undefined ); + this.projectService.reindex().subscribe( resp => { + ref.dismiss(); + let difTime = new Date().getTime() - time; + this.snackbar.open("Reindexing complete in " + difTime + 'ms'); + this.projectService.refreshProjects(); + }, error => { + this.snackbar.open("Reindexing failed!"); + } ); + } + +} diff --git a/microdocs-ui/src/app/components/simple-card/simple-card.component.html b/microdocs-ui/src/app/components/simple-card/simple-card.component.html new file mode 100644 index 00000000..f07c6dcc --- /dev/null +++ b/microdocs-ui/src/app/components/simple-card/simple-card.component.html @@ -0,0 +1,17 @@ +
+
+

{{text}}

+ + expand_more + expand_less +
+ +
diff --git a/microdocs-ui/src/app/components/simple-card/simple-card.component.scss b/microdocs-ui/src/app/components/simple-card/simple-card.component.scss new file mode 100644 index 00000000..bfdf5dfe --- /dev/null +++ b/microdocs-ui/src/app/components/simple-card/simple-card.component.scss @@ -0,0 +1,59 @@ +@import '../../../assets/scss/settings'; + +.simple-card { + margin-bottom: 40px; + + .header { + display: inline-block; + cursor: pointer; + h1 { + display: inline-block; + color: $accent; + font-size: 21px; + } + .subtitle{ + margin: 12px 20px; + color: map_get($md-grey, 500); + } + + md-icon { + position: relative; + top: 5px; + } + md-icon[hidden] { + display: none; + } + } + .header:hover md-icon { + color: $accent; + } + .content{ + margin-top: 10px; + } +} + +.simple-card.paper { + box-shadow: rgba(0, 0, 0, 0.227451) 0px 3px 10px 0px, rgba(0, 0, 0, 0.156863) 0px 3px 10px 0px !important; + border-radius: 2px; + margin-bottom: 10px; + padding: 10px 20px; + background-color: white; + + .header { + display: flex; + h1 { + color: black; + font-size: 16px; + flex: 1; + margin-top: 8px; + } + h1:hover{ + color: $accent; + } + md-icon { + top: 7px; + } + } +} + + diff --git a/microdocs-ui/src/app/components/simple-card/simple-card.component.ts b/microdocs-ui/src/app/components/simple-card/simple-card.component.ts new file mode 100644 index 00000000..d0c19aa9 --- /dev/null +++ b/microdocs-ui/src/app/components/simple-card/simple-card.component.ts @@ -0,0 +1,42 @@ +import { Component, Input } from "@angular/core"; + +@Component( { + selector: 'simple-card', + templateUrl: 'simple-card.component.html', + styleUrls: [ 'simple-card.component.scss' ] +} ) +export class SimpleCardComponent { + + @Input('open') + private _open: boolean = true; + @Input() + private text: string; + @Input() + private subTitle:string; + @Input() + private paper:boolean; + + constructor() { + } + + public toggle(): void { + this._open = !this._open; + } + + public open(): void { + this._open = true; + } + + public close(): void { + this._open = true; + } + + public isOpened(): boolean { + return this._open; + } + + public isClosed(): boolean { + return !this._open; + } + +} diff --git a/microdocs-ui/src/app/domain/route-info.model.ts b/microdocs-ui/src/app/domain/route-info.model.ts new file mode 100644 index 00000000..62785a6f --- /dev/null +++ b/microdocs-ui/src/app/domain/route-info.model.ts @@ -0,0 +1,18 @@ + +export interface RouteInfo { + + path?: string; + pathMatch?: string; + pathParams?: {[key:string]:any}; + component?: any; + name?: string; + icon?: string; + iconOpen?: string; + children?: RouteInfo[]; + open?:boolean; + postIcon?: string; + postIconColor?: string; + generateIcon?: true; + generateIconColor?:string; + +} diff --git a/microdocs-ui/src/app/helpers/string.util.ts b/microdocs-ui/src/app/helpers/string.util.ts new file mode 100644 index 00000000..5a53ecb8 --- /dev/null +++ b/microdocs-ui/src/app/helpers/string.util.ts @@ -0,0 +1,32 @@ +export class StringUtil { + + public static getColorCodeFromString( string: string ) { + let colorRanges = { + 'pink': [ 'a', 'b' ], + 'red': [ 'c', 'd' ], + 'orange': [ 'e', 'f' ], + 'amber': [ 'g', 'h' ], + 'yellow': [ 'i', 'j' ], + 'lime': [ 'k', 'l' ], + 'green': [ 'm', 'n' ], + 'teal': [ 'o', 'p' ], + 'cyan': [ 'q', 'r' ], + 'light-blue': [ 's', 't' ], + 'blue': [ 'u', 'v' ], + 'indigo': [ 'w', 'x' ], + 'purple': [ 'y', 'z' ] + }; + let first = string.substr( 0, 1 ).toLowerCase(); + let selectedColor = "amber"; + + Object.keys( colorRanges ).forEach( function ( color ) { + colorRanges[ color ].forEach( ( char: string ) => { + if ( char == first ) { + selectedColor = color; + } + } ) + } ); + return selectedColor; + } + +} diff --git a/microdocs-ui/src/app/pipes/empty.pipe.ts b/microdocs-ui/src/app/pipes/empty.pipe.ts new file mode 100644 index 00000000..9cb1ffb3 --- /dev/null +++ b/microdocs-ui/src/app/pipes/empty.pipe.ts @@ -0,0 +1,63 @@ +import {Pipe} from "@angular/core"; + +/** + * This Pipe is used to check if an object or path of an object is not empty (null|undefined|zero length|zero keys) + * Example usage: + * parent = { + * name: 'Alise', + * children: { + * john: { + * name: 'John' + * } + * } + * } + *
+ * This example checks if the child john is not empty + */ +@Pipe({ + name: "empty" +}) +export class EmptyPipe { + + transform(rootValue:any, path?:string):boolean { + // check if the rootValue is empty + if (this.isEmpty(rootValue)) { + return true; + } + + // follow the path if exists + if (path != undefined) { + var currentObject = rootValue; + var segments = path.split("."); + console.info(segments); + for (var i = 0; i < segments.length; i++) { + currentObject = currentObject[segments[i]]; + if (this.isEmpty(currentObject)) { + return true; + } + } + } + return false; + } + + /** + * Check if an object is empty + * @param value + * @returns {boolean} + */ + private isEmpty(value:any):boolean { + if (value == undefined || value == null) { + return true; + } + if (Array.isArray(value)) { + if (value.length == 0) { + return true; + } + }else if(typeof(value) == 'object'){ + if(Object.keys(value).length == 0){ + return true; + } + } + return false; + } +} diff --git a/microdocs-ui/src/app/pipes/filter-by-field.pipe.ts b/microdocs-ui/src/app/pipes/filter-by-field.pipe.ts new file mode 100644 index 00000000..4157d399 --- /dev/null +++ b/microdocs-ui/src/app/pipes/filter-by-field.pipe.ts @@ -0,0 +1,67 @@ +import { Pipe } from "@angular/core"; + +/** + * This Pipe filter items based on if the item has a specified field with a specified value + * Example usage: + *
+ * + * This example filters the list for all object which has type == 'one' or type == 'two' + */ +@Pipe( { + name: "filterByField" +} ) +export class FilterByFieldPipe { + + transform( list: any, path: string, value: any ): any { + var self = this; + if ( list == undefined || list == null ) { + return list; + } + if ( Array.isArray( list ) ) { + return list.filter( ( item: Object ) => { + var fieldValue = this.getFieldValue( item, path ); + return self.filter( fieldValue, value ); + } ); + } + else if ( typeof(list) == 'object' ) { + var filteredObject = {}; + for ( var key in list ) { + var fieldValue = this.getFieldValue( list[ key ], path ); + if ( self.filter( fieldValue, value ) ) { + filteredObject[ key ] = list[ key ]; + } + } + return filteredObject; + } + else { + console.warn( "filterByField requires an Array as input, not " + typeof(list) ); + return list; + } + } + + filter( fieldValue: any, value: any ): boolean { + var equals = fieldValue == value; + if ( !equals && Array.isArray( value ) ) { + value.forEach( ( v: any )=> { + if ( fieldValue == v ) { + equals = true; + } + } ); + } + return equals; + } + + getFieldValue( item: {}, path: string ): any { + var currentItem = item; + path.split( "." ).forEach( segment => { + if ( currentItem == undefined || currentItem == null ) { + return true; + } + currentItem = currentItem[ segment ]; + return true; + } ); + return currentItem; + } + + +} diff --git a/microdocs-ui/src/app/pipes/not-empty.pipe.ts b/microdocs-ui/src/app/pipes/not-empty.pipe.ts new file mode 100644 index 00000000..e549cc1b --- /dev/null +++ b/microdocs-ui/src/app/pipes/not-empty.pipe.ts @@ -0,0 +1,46 @@ +import {Pipe} from "@angular/core"; + +/** + * This Pipe is used to check if an object or path of an object is not empty + * Example usage: + *
+ */ +@Pipe({ + name: "notEmpty" +}) +export class NotEmptyPipe { + + transform(value:any, path?:string):boolean { + if (this.isEmpty(value)) { + return false; + } + + if (path != undefined) { + var currentObject = value; + var segments = path.split("."); + for (var i = 0; i < segments.length; i++) { + currentObject = currentObject[segments[i]]; + if (this.isEmpty(currentObject)) { + return false; + } + } + } + return true; + } + + private isEmpty(value:any):boolean { + if (value == undefined || value == null) { + return true; + } + if (Array.isArray(value)) { + if (value.length == 0) { + return true; + } + }else if(typeof(value) == 'object'){ + if(Object.keys(value).length == 0){ + return true; + } + } + return false; + } +} diff --git a/microdocs-ui/src/app/pipes/object-iterator.pipe.ts b/microdocs-ui/src/app/pipes/object-iterator.pipe.ts new file mode 100644 index 00000000..e027edf4 --- /dev/null +++ b/microdocs-ui/src/app/pipes/object-iterator.pipe.ts @@ -0,0 +1,31 @@ +import { Pipe } from "@angular/core"; + +/** + * This Pipe is used to iterate through a key-value array + * Example usage: + *
+ * {{property._id}} + *
+ */ +@Pipe({ + name: "objectIterator" +}) +export class ObjectIteratorPipe { + + transform(object:{}) : Array{ + if(object == undefined || object == null){ + return []; + } + var result : Array = []; + for(var key in object){ + if(key != '_id') { + var value = object[key]; + if (value != undefined && value != null && typeof(value) == 'object' && !Array.isArray(value)) { + value._id = key; + } + result.push(value); + } + } + return result; + } +} diff --git a/microdocs-ui/src/app/pipes/sort-by-http-method.pipe.ts b/microdocs-ui/src/app/pipes/sort-by-http-method.pipe.ts new file mode 100644 index 00000000..93e9cd5a --- /dev/null +++ b/microdocs-ui/src/app/pipes/sort-by-http-method.pipe.ts @@ -0,0 +1,40 @@ +import { Pipe } from "@angular/core"; + +/** + * Custom sort http methods + */ +@Pipe({name: 'sortByHttpMethod'}) +export class SortByHttpMethodPipe { + + private transform(list:Array, key:string):any { + if (list == undefined || list == null || list.length == 0) + return list; + + if (key == undefined || key == null || key.length == 0) + return list; + + var self = this; + return list.sort(function (a, b) { + var fieldA = self.getValueOf(a[key]); + var fieldB = self.getValueOf(b[key]); + + if (fieldA < fieldB) + return -1; + if (fieldA > fieldB) + return 1; + return 0; + }); + } + + private getValueOf(httpMethod:string):number{ + switch (httpMethod.toLowerCase()){ + case 'get': return 1; + case 'post': return 2; + case 'put': return 3; + case 'patch': return 4; + case 'delete': return 5; + default: return 9; + } + } + +} diff --git a/microdocs-ui/src/app/pipes/sort-by-key.pipe.ts b/microdocs-ui/src/app/pipes/sort-by-key.pipe.ts new file mode 100644 index 00000000..4dd2eeb4 --- /dev/null +++ b/microdocs-ui/src/app/pipes/sort-by-key.pipe.ts @@ -0,0 +1,32 @@ +import { Pipe } from "@angular/core"; + +/** + * Filter list of cast by argument + * Usage: + * string | sortByKey: 'mustContain': 'mustNotContain' + * Example: + * url(assignment) | sortByKey: '/': '' //will not return a string + * assignment | sortByKey: '': '/' //returns string + */ +@Pipe({name: 'sortByKey'}) +export class SortByKeyPipe { + + private transform(list:Array, key:string):any { + if (list == undefined || list == null || list.length == 0) + return list; + + if (key == undefined || key == null || key.length == 0) + return list; + + return list.sort(function (a, b) { + var fieldA = a[key]; + var fieldB = b[key]; + + if (fieldA < fieldB) + return -1; + if (fieldA > fieldB) + return 1; + return 0; + }); + } +} diff --git a/microdocs-ui/src/app/services/project.service.ts b/microdocs-ui/src/app/services/project.service.ts new file mode 100644 index 00000000..81cd2510 --- /dev/null +++ b/microdocs-ui/src/app/services/project.service.ts @@ -0,0 +1,116 @@ +import { Observable } from "rxjs/Observable"; +import { Subject } from "rxjs/Subject"; +import { ReplaySubject } from "rxjs/ReplaySubject"; +import { Notification } from "rxjs/Notification"; +import { Response } from "@angular/http"; +import { ProjectTree, Project, Environments, ProjectChangeRule, ProblemResponse} from "@maxxton/microdocs-core/domain"; +import { ProjectClient } from "../clients/project.client"; +import { Injectable, Inject } from "@angular/core"; +import { MdSnackBar } from "@angular/material"; + +const TIMEOUT:number = 500; + +@Injectable() +export class ProjectService { + + private env:string; + private projects:Subject> = new ReplaySubject >( 1 ); + private project:Subject> = new ReplaySubject >( 1 ); + private lastProjectsValue:ProjectTree; + private lastProjectValue:Project; + private lastProjectsRefresh:number = 0; + private lastProjectRefresh:number = 0; + private lastProjectTitle:string; + private lastProjectVersion:string; + + constructor(@Inject('ProjectClient') private projectClient:ProjectClient, private snackbar:MdSnackBar) { + this.projects.subscribe( node => this.lastProjectsValue = node.value ); + this.project.subscribe( project => this.lastProjectValue = project ); + } + + public getProjects( env:string = this.getSelectedEnv() ):Subject> { + this.refreshProjects( env ); + return this.projects; + } + + public refreshProjects( env:string = this.getSelectedEnv(), force:boolean = false ):void { + if ( this.lastProjectsRefresh + TIMEOUT < new Date().getTime() || force ) { + this.projectClient.loadProjects( env ).subscribe( + node => this.projects.next( Notification.createNext( node ) ), + error => { + this.handleError( error, "Failed to load project list" ); + this.projects.next( Notification.createError( error ) ); + } ); + this.lastProjectsRefresh = new Date().getTime(); + } + } + + public getProject( name:string, version?:string, env:string = this.getSelectedEnv() ):Observable> { + this.refreshProject( name, version, env ); + return this.project; + } + + public refreshProject( name:string, version?:string, env:string = this.getSelectedEnv() ):void { + var shouldRefresh = true; + if ( this.lastProjectRefresh + TIMEOUT >= new Date().getTime() && this.lastProjectTitle && this.lastProjectTitle.toLowerCase() === name.toLowerCase() && this.lastProjectVersion === version ) { + shouldRefresh = false; + } + if ( shouldRefresh ) { + this.projectClient.loadProject( env, name, version ).subscribe( + project => this.project.next( Notification.createNext( project ) ), + error => { + this.handleError( error, "Failed to load project " + name + ":" + version ); + this.project.next( Notification.createError( error ) ); + } ); + this.lastProjectRefresh = new Date().getTime(); + this.lastProjectTitle = name; + this.lastProjectVersion = version; + } + } + + public importProject( project:Project, title:string, group:string, version:string, env:string = this.getSelectedEnv() ):Observable { + return this.projectClient.importProject( env, project, title, group, version ); + } + + public deleteProject( title:string, version?:string, env:string = this.getSelectedEnv() ):Observable { + return this.projectClient.deleteProject( env, title, version ); + } + + public updateProject( title:string, rules:ProjectChangeRule[], version?:string, env:string = this.getSelectedEnv() ):Observable { + return this.projectClient.updateProject( env, title, rules, version ); + } + + public reindex( env:string = this.getSelectedEnv() ):Observable { + return this.projectClient.reindex( env ); + } + + public getEnvs():Observable<{[key:string]:Environments}>{ + return this.projectClient.getEnvs(); + } + + public setSelectedEnv( env:string ) { + this.env = env; + this.lastProjectsRefresh = 0; + this.lastProjectRefresh = 0; + this.refreshProjects( env ); + } + + public getSelectedEnv():string { + return this.env; + } + + private handleError( error:Response, friendlyMessage:string ):void { + this.snackbar.open(friendlyMessage); +// this.snackbarService.addNotification( friendlyMessage, undefined, undefined, 'error_outline', undefined ); + try { + var body = error.json(); + var e = body.error; + var msg = body.message; + if ( e ) { + console.error( e + (msg ? ' (' + msg + ')' : '') ); + } + } catch ( e ) {/*hide parse error*/ + } + } + +} diff --git a/microdocs-ui/src/assets/.gitkeep b/microdocs-ui/src/assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/microdocs-ui/src/assets/scss/_settings.scss b/microdocs-ui/src/assets/scss/_settings.scss index 4f8ed938..c1f77085 100644 --- a/microdocs-ui/src/assets/scss/_settings.scss +++ b/microdocs-ui/src/assets/scss/_settings.scss @@ -6,13 +6,15 @@ $candy-app-primary: md-palette($md-blue); $candy-app-accent: md-palette($md-orange, A700); $candy-app-error: md-palette($md-red); $candy-app-warn: md-palette($md-deep-orange); +$candy-app-info: md-palette($md-blue); $candy-app-theme: md-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn); @include angular-material-theme($candy-app-theme); $primary: md-color($candy-app-primary); $accent: md-color($candy-app-accent); $error: md-color($candy-app-error); -$warn: md-color($candy-app-warn); +$warning: md-color($candy-app-warn); +$info: md-color($candy-app-info); $primary-opacity: rgba(33, 150, 243, 0.5); $colors: ( diff --git a/microdocs-ui/src/environments/environment.prod.ts b/microdocs-ui/src/environments/environment.prod.ts new file mode 100644 index 00000000..9b40bee5 --- /dev/null +++ b/microdocs-ui/src/environments/environment.prod.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + standalone: false +}; diff --git a/microdocs-ui/src/environments/environment.standalone.ts b/microdocs-ui/src/environments/environment.standalone.ts new file mode 100644 index 00000000..e37c20de --- /dev/null +++ b/microdocs-ui/src/environments/environment.standalone.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + standalone: true +}; diff --git a/microdocs-ui/src/environments/environment.ts b/microdocs-ui/src/environments/environment.ts new file mode 100644 index 00000000..67671f16 --- /dev/null +++ b/microdocs-ui/src/environments/environment.ts @@ -0,0 +1,9 @@ +// The file contents for the current environment will overwrite these during build. +// The build system defaults to the dev environment which uses `environment.ts`, but if you do +// `ng build --env=prod` then `environment.prod.ts` will be used instead. +// The list of which env maps to which file can be found in `angular-cli.json`. + +export const environment = { + production: false, + standalone: false +}; diff --git a/microdocs-ui/src/favicon.ico b/microdocs-ui/src/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8081c7ceaf2be08bf59010158c586170d9d2d517 GIT binary patch literal 5430 zcmc(je{54#6vvCoAI3i*G5%$U7!sA3wtMZ$fH6V9C`=eXGJb@R1%(I_{vnZtpD{6n z5Pl{DmxzBDbrB>}`90e12m8T*36WoeDLA&SD_hw{H^wM!cl_RWcVA!I+x87ee975; z@4kD^=bYPn&pmG@(+JZ`rqQEKxW<}RzhW}I!|ulN=fmjVi@x{p$cC`)5$a!)X&U+blKNvN5tg=uLvuLnuqRM;Yc*swiexsoh#XPNu{9F#c`G zQLe{yWA(Y6(;>y|-efAy11k<09(@Oo1B2@0`PtZSkqK&${ zgEY}`W@t{%?9u5rF?}Y7OL{338l*JY#P!%MVQY@oqnItpZ}?s z!r?*kwuR{A@jg2Chlf0^{q*>8n5Ir~YWf*wmsh7B5&EpHfd5@xVaj&gqsdui^spyL zB|kUoblGoO7G(MuKTfa9?pGH0@QP^b#!lM1yHWLh*2iq#`C1TdrnO-d#?Oh@XV2HK zKA{`eo{--^K&MW66Lgsktfvn#cCAc*(}qsfhrvOjMGLE?`dHVipu1J3Kgr%g?cNa8 z)pkmC8DGH~fG+dlrp(5^-QBeEvkOvv#q7MBVLtm2oD^$lJZx--_=K&Ttd=-krx(Bb zcEoKJda@S!%%@`P-##$>*u%T*mh+QjV@)Qa=Mk1?#zLk+M4tIt%}wagT{5J%!tXAE;r{@=bb%nNVxvI+C+$t?!VJ@0d@HIyMJTI{vEw0Ul ze(ha!e&qANbTL1ZneNl45t=#Ot??C0MHjjgY8%*mGisN|S6%g3;Hlx#fMNcL<87MW zZ>6moo1YD?P!fJ#Jb(4)_cc50X5n0KoDYfdPoL^iV`k&o{LPyaoqMqk92wVM#_O0l z09$(A-D+gVIlq4TA&{1T@BsUH`Bm=r#l$Z51J-U&F32+hfUP-iLo=jg7Xmy+WLq6_tWv&`wDlz#`&)Jp~iQf zZP)tu>}pIIJKuw+$&t}GQuqMd%Z>0?t%&BM&Wo^4P^Y z)c6h^f2R>X8*}q|bblAF?@;%?2>$y+cMQbN{X$)^R>vtNq_5AB|0N5U*d^T?X9{xQnJYeU{ zoZL#obI;~Pp95f1`%X3D$Mh*4^?O?IT~7HqlWguezmg?Ybq|7>qQ(@pPHbE9V?f|( z+0xo!#m@Np9PljsyxBY-UA*{U*la#8Wz2sO|48_-5t8%_!n?S$zlGe+NA%?vmxjS- zHE5O3ZarU=X}$7>;Okp(UWXJxI%G_J-@IH;%5#Rt$(WUX?6*Ux!IRd$dLP6+SmPn= z8zjm4jGjN772R{FGkXwcNv8GBcZI#@Y2m{RNF_w8(Z%^A*!bS*!}s6sh*NnURytky humW;*g7R+&|Ledvc- - + diff --git a/microdocs-ui/src/main.ts b/microdocs-ui/src/main.ts new file mode 100644 index 00000000..ac78a713 --- /dev/null +++ b/microdocs-ui/src/main.ts @@ -0,0 +1,12 @@ +import './polyfills.ts'; + +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { enableProdMode } from '@angular/core'; +import { environment } from './environments/environment'; +import { AppModule } from './app/app.module'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/microdocs-ui/src/polyfills.ts b/microdocs-ui/src/polyfills.ts new file mode 100644 index 00000000..4749399c --- /dev/null +++ b/microdocs-ui/src/polyfills.ts @@ -0,0 +1,19 @@ +// This file includes polyfills needed by Angular and is loaded before +// the app. You can add your own extra polyfills to this file. +import 'core-js/es6/symbol'; +import 'core-js/es6/object'; +import 'core-js/es6/function'; +import 'core-js/es6/parse-int'; +import 'core-js/es6/parse-float'; +import 'core-js/es6/number'; +import 'core-js/es6/math'; +import 'core-js/es6/string'; +import 'core-js/es6/date'; +import 'core-js/es6/array'; +import 'core-js/es6/regexp'; +import 'core-js/es6/map'; +import 'core-js/es6/set'; +import 'core-js/es6/reflect'; + +import 'core-js/es7/reflect'; +import 'zone.js/dist/zone'; diff --git a/microdocs-ui/src/styles.scss b/microdocs-ui/src/styles.scss new file mode 100644 index 00000000..7aa81304 --- /dev/null +++ b/microdocs-ui/src/styles.scss @@ -0,0 +1,87 @@ + +@import 'assets/scss/settings'; + +* { + box-sizing: border-box; +} + +body{ + background: #fff; + color: #222; + padding: 0; + margin: 0; + font-family: "Roboto", "Open Sans", "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; + font-weight: normal; + font-style: normal; + line-height: 1; + position: relative; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +app-root{ + height: 100%; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Roboto", "Open Sans", "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; + font-weight: normal; + font-style: normal; + color: #222; + text-rendering: optimizeLegibility; + margin-top: 0.2rem; + margin-bottom: 0.5rem; + line-height: 1.4; +} + +a, .link { + text-decoration: none; + color: $info; + cursor: pointer; + + .link-icon{ + position: relative; + top: 8px; + } +} + +a:hover{ + text-decoration: underline; +} + +.text{ + font-size: 14px; +} + +.text ul{ + list-style: none; + margin: 0; + padding: 0; +} + +.error, .warning, .info{ + padding-bottom: 20px; + + md-icon{ + position: relative; + top: 8px; + } +} + +.error{ + color: $error; +} + +.warning{ + color: $warning; +} + +.info{ + color: $info; +} + +.highlight-container{ + .highlight{ + color: $primary; + } +} diff --git a/microdocs-ui/src/test.ts b/microdocs-ui/src/test.ts new file mode 100644 index 00000000..f9d51efd --- /dev/null +++ b/microdocs-ui/src/test.ts @@ -0,0 +1,32 @@ +import './polyfills.ts'; + +import 'zone.js/dist/long-stack-trace-zone'; +import 'zone.js/dist/proxy.js'; +import 'zone.js/dist/sync-test'; +import 'zone.js/dist/jasmine-patch'; +import 'zone.js/dist/async-test'; +import 'zone.js/dist/fake-async-test'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. +declare var __karma__: any; +declare var require: any; + +// Prevent Karma from running prematurely. +__karma__.loaded = function () {}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); +// Finally, start Karma to run the tests. +__karma__.start(); diff --git a/microdocs-ui/tslint.json b/microdocs-ui/tslint.json new file mode 100644 index 00000000..86bc1841 --- /dev/null +++ b/microdocs-ui/tslint.json @@ -0,0 +1,116 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "callable-types": true, + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "import-blacklist": [true, "rxjs"], + "import-spacing": true, + "indent": [ + true, + "spaces" + ], + "interface-over-type-literal": true, + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + "static-before-instance", + "variables-before-functions" + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-variable": true, + "no-empty": false, + "no-empty-interface": true, + "no-eval": true, + "no-inferrable-types": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "prefer-const": true, + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "typeof-compare": true, + "unified-signatures": true, + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + + "directive-selector": [true, "attribute", "app", "camelCase"], + "component-selector": [true, "element", "app", "kebab-case"], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true, + "no-access-missing-member": true, + "templates-use-public": true, + "invoke-injectable": true + } +} diff --git a/npm-cache/conf/config.yaml b/npm-cache/conf/config.yaml new file mode 100644 index 00000000..635ac53a --- /dev/null +++ b/npm-cache/conf/config.yaml @@ -0,0 +1,48 @@ +# +# This is the default config file. It allows all users to do anything, +# so don't use it on production systems. +# +# Look here for more config file examples: +# https://github.com/rlidwka/sinopia/tree/master/conf +# + +# path to a directory with all packages +storage: ./storage + +auth: + htpasswd: + file: ./htpasswd + # Maximum amount of users allowed to register, defaults to "+inf". + # You can set this to -1 to disable registration. + #max_users: 1000 + +# a list of other known repositories we can talk to +uplinks: + npmjs: + url: https://registry.npmjs.org/ + +packages: + '@*/*': + # scoped packages + access: $all + publish: $authenticated + + '*': + # allow all users (including non-authenticated users) to read and + # publish all packages + # + # you can specify usernames/groupnames (depending on your auth plugin) + # and three keywords: "$all", "$anonymous", "$authenticated" + access: $all + + # allow all known users to publish packages + # (anyone can register by default, remember?) + publish: $authenticated + + # if package is not available locally, proxy requests to 'npmjs' registry + proxy: npmjs + +# log settings +logs: + - {type: stdout, format: pretty, level: http} + #- {type: file, path: sinopia.log, level: info} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000..48bdfca0 --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "scripts": { + "watch-server": "cd microdocs-server && gulp", + "watch-ui": "cd microdocs-ui && ng build --watch", + "start-docker": "docker-compose up --build", + "start-npm-cache": "docker-compose -f docker-compose-npm-cache.yml up" + } +}