From 5ceec23c827ea841024e8ca60a4c25853cebafed Mon Sep 17 00:00:00 2001 From: viktornar Date: Tue, 25 Jul 2017 23:23:09 +0300 Subject: [PATCH 01/15] Lets start with jspm and SystemJS as those tools are quite simple to start working with. --- .gitignore | 1 + package.json | 17 +++++++++++- ui/config.js | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 ui/config.js diff --git a/.gitignore b/.gitignore index 0db216bfa4..951d804f67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ npm-debug.log node_modules +ui/jspm_packages diff --git a/package.json b/package.json index 362aa65404..3e6d9d8544 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,25 @@ "scripts": { "start": "json-server --port 3010 --watch api.json" }, - "keywords": ["react challenge", "tryouts", "react developer", "hiring"], + "keywords": [ + "react challenge", + "tryouts", + "react developer", + "hiring" + ], "author": "Adapt A/S", "license": "ISC", "devDependencies": { "json-server": "0.10.1" + }, + "jspm": { + "directories": { + "baseURL": "ui" + }, + "devDependencies": { + "babel": "npm:babel-core@^5.8.24", + "babel-runtime": "npm:babel-runtime@^5.8.24", + "core-js": "npm:core-js@^1.1.4" + } } } diff --git a/ui/config.js b/ui/config.js new file mode 100644 index 0000000000..b617edf7c7 --- /dev/null +++ b/ui/config.js @@ -0,0 +1,76 @@ +System.config({ + baseURL: "/", + defaultJSExtensions: true, + transpiler: "babel", + babelOptions: { + "optional": [ + "runtime", + "optimisation.modules.system" + ] + }, + paths: { + "github:*": "jspm_packages/github/*", + "npm:*": "jspm_packages/npm/*" + }, + + map: { + "babel": "npm:babel-core@5.8.38", + "babel-runtime": "npm:babel-runtime@5.8.38", + "core-js": "npm:core-js@1.2.7", + "github:jspm/nodelibs-assert@0.1.0": { + "assert": "npm:assert@1.4.1" + }, + "github:jspm/nodelibs-buffer@0.1.1": { + "buffer": "npm:buffer@5.0.6" + }, + "github:jspm/nodelibs-path@0.1.0": { + "path-browserify": "npm:path-browserify@0.0.0" + }, + "github:jspm/nodelibs-process@0.1.2": { + "process": "npm:process@0.11.10" + }, + "github:jspm/nodelibs-util@0.1.0": { + "util": "npm:util@0.10.3" + }, + "github:jspm/nodelibs-vm@0.1.0": { + "vm-browserify": "npm:vm-browserify@0.0.4" + }, + "npm:assert@1.4.1": { + "assert": "github:jspm/nodelibs-assert@0.1.0", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "process": "github:jspm/nodelibs-process@0.1.2", + "util": "npm:util@0.10.3" + }, + "npm:babel-runtime@5.8.38": { + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:buffer@5.0.6": { + "base64-js": "npm:base64-js@1.2.1", + "ieee754": "npm:ieee754@1.1.8" + }, + "npm:core-js@1.2.7": { + "fs": "github:jspm/nodelibs-fs@0.1.2", + "path": "github:jspm/nodelibs-path@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:inherits@2.0.1": { + "util": "github:jspm/nodelibs-util@0.1.0" + }, + "npm:path-browserify@0.0.0": { + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:process@0.11.10": { + "assert": "github:jspm/nodelibs-assert@0.1.0", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "vm": "github:jspm/nodelibs-vm@0.1.0" + }, + "npm:util@0.10.3": { + "inherits": "npm:inherits@2.0.1", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:vm-browserify@0.0.4": { + "indexof": "npm:indexof@0.0.1" + } + } +}); From 902e01ce704b296916fe5cb6f7194efc2b116b17 Mon Sep 17 00:00:00 2001 From: viktornar Date: Tue, 25 Jul 2017 23:31:21 +0300 Subject: [PATCH 02/15] Added dependencies that I may need. --- package.json | 41 ++- ui/config.js | 687 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 725 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3e6d9d8544..3a79ba67a5 100644 --- a/package.json +++ b/package.json @@ -13,17 +13,52 @@ ], "author": "Adapt A/S", "license": "ISC", - "devDependencies": { - "json-server": "0.10.1" - }, "jspm": { "directories": { "baseURL": "ui" }, + "dependencies": { + "bootstrap": "npm:bootstrap@^3.3.7", + "isomorphic-fetch": "npm:isomorphic-fetch@^2.2.1", + "jsx": "github:floatdrop/plugin-jsx@^1.2.1", + "path-to-regexp": "npm:path-to-regexp@^1.7.0", + "react": "npm:react@^15.6.1", + "react-bootstrap": "npm:react-bootstrap@^0.31.1", + "react-dom": "npm:react-dom@^15.6.1", + "react-helmet": "npm:react-helmet@^5.1.3", + "react-intl": "npm:react-intl@^2.3.0", + "react-redux": "npm:react-redux@^5.0.5", + "react-router": "npm:react-router@^4.1.2", + "react-router-bootstrap": "npm:react-router-bootstrap@^0.24.2", + "react-router-config": "npm:react-router-config@^1.0.0-beta.3", + "react-router-dom": "npm:react-router-dom@^4.1.2", + "underscore": "npm:underscore@^1.8.3" + }, "devDependencies": { "babel": "npm:babel-core@^5.8.24", "babel-runtime": "npm:babel-runtime@^5.8.24", "core-js": "npm:core-js@^1.1.4" + }, + "overrides": { + "npm:react-redux@5.0.5": { + "peerDependencies": { + "react": "15.6.1" + } + } } + }, + "devDependencies": { + "babel-eslint": "^7.2.3", + "eslint": "^4.3.0", + "eslint-config-fbjs": "^2.0.0", + "eslint-plugin-babel": "^4.1.1", + "eslint-plugin-flowtype": "^2.35.0", + "eslint-plugin-jsx-a11y": "^6.0.2", + "eslint-plugin-react": "^7.1.0", + "eslint-plugin-relay": "0.0.8", + "http-proxy-middleware": "^0.17.4", + "json-server": "0.10.1", + "jspm": "^0.16.53", + "live-server": "^1.2.0" } } diff --git a/ui/config.js b/ui/config.js index b617edf7c7..22ce510537 100644 --- a/ui/config.js +++ b/ui/config.js @@ -16,25 +16,101 @@ System.config({ map: { "babel": "npm:babel-core@5.8.38", "babel-runtime": "npm:babel-runtime@5.8.38", + "bootstrap": "npm:bootstrap@3.3.7", "core-js": "npm:core-js@1.2.7", + "isomorphic-fetch": "npm:isomorphic-fetch@2.2.1", + "jsx": "github:floatdrop/plugin-jsx@1.2.1", + "path-to-regexp": "npm:path-to-regexp@1.7.0", + "react": "npm:react@15.6.1", + "react-bootstrap": "npm:react-bootstrap@0.31.1", + "react-dom": "npm:react-dom@15.6.1", + "react-helmet": "npm:react-helmet@5.1.3", + "react-intl": "npm:react-intl@2.3.0", + "react-redux": "npm:react-redux@5.0.5", + "react-router": "npm:react-router@4.1.2", + "react-router-bootstrap": "npm:react-router-bootstrap@0.24.2", + "react-router-config": "npm:react-router-config@1.0.0-beta.3", + "react-router-dom": "npm:react-router-dom@4.1.2", + "underscore": "npm:underscore@1.8.3", + "github:floatdrop/plugin-jsx@1.2.1": { + "react-tools": "npm:react-tools@0.13.3" + }, "github:jspm/nodelibs-assert@0.1.0": { "assert": "npm:assert@1.4.1" }, "github:jspm/nodelibs-buffer@0.1.1": { "buffer": "npm:buffer@5.0.6" }, + "github:jspm/nodelibs-constants@0.1.0": { + "constants-browserify": "npm:constants-browserify@0.0.1" + }, + "github:jspm/nodelibs-crypto@0.1.0": { + "crypto-browserify": "npm:crypto-browserify@3.11.1" + }, + "github:jspm/nodelibs-domain@0.1.0": { + "domain-browser": "npm:domain-browser@1.1.7" + }, + "github:jspm/nodelibs-events@0.1.1": { + "events": "npm:events@1.0.2" + }, + "github:jspm/nodelibs-http@1.7.1": { + "Base64": "npm:Base64@0.2.1", + "events": "github:jspm/nodelibs-events@0.1.1", + "inherits": "npm:inherits@2.0.1", + "stream": "github:jspm/nodelibs-stream@0.1.0", + "url": "github:jspm/nodelibs-url@0.1.0", + "util": "github:jspm/nodelibs-util@0.1.0" + }, + "github:jspm/nodelibs-https@0.1.0": { + "https-browserify": "npm:https-browserify@0.0.0" + }, + "github:jspm/nodelibs-os@0.1.0": { + "os-browserify": "npm:os-browserify@0.1.2" + }, "github:jspm/nodelibs-path@0.1.0": { "path-browserify": "npm:path-browserify@0.0.0" }, "github:jspm/nodelibs-process@0.1.2": { "process": "npm:process@0.11.10" }, + "github:jspm/nodelibs-stream@0.1.0": { + "stream-browserify": "npm:stream-browserify@1.0.0" + }, + "github:jspm/nodelibs-string_decoder@0.1.0": { + "string_decoder": "npm:string_decoder@0.10.31" + }, + "github:jspm/nodelibs-url@0.1.0": { + "url": "npm:url@0.10.3" + }, "github:jspm/nodelibs-util@0.1.0": { "util": "npm:util@0.10.3" }, "github:jspm/nodelibs-vm@0.1.0": { "vm-browserify": "npm:vm-browserify@0.0.4" }, + "github:jspm/nodelibs-zlib@0.1.0": { + "browserify-zlib": "npm:browserify-zlib@0.1.4" + }, + "npm:acorn@4.0.13": { + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:amdefine@1.0.1": { + "fs": "github:jspm/nodelibs-fs@0.1.2", + "module": "github:jspm/nodelibs-module@0.1.0", + "path": "github:jspm/nodelibs-path@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:asap@2.0.6": { + "domain": "github:jspm/nodelibs-domain@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:asn1.js@4.9.1": { + "bn.js": "npm:bn.js@4.11.7", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "inherits": "npm:inherits@2.0.1", + "minimalistic-assert": "npm:minimalistic-assert@1.0.0", + "vm": "github:jspm/nodelibs-vm@0.1.0" + }, "npm:assert@1.4.1": { "assert": "github:jspm/nodelibs-assert@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.1", @@ -44,33 +120,644 @@ System.config({ "npm:babel-runtime@5.8.38": { "process": "github:jspm/nodelibs-process@0.1.2" }, + "npm:babel-runtime@6.23.0": { + "core-js": "npm:core-js@2.4.1", + "regenerator-runtime": "npm:regenerator-runtime@0.10.5" + }, + "npm:bn.js@4.11.7": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1" + }, + "npm:bootstrap@3.3.7": { + "fs": "github:jspm/nodelibs-fs@0.1.2", + "path": "github:jspm/nodelibs-path@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:brace-expansion@1.1.8": { + "balanced-match": "npm:balanced-match@1.0.0", + "concat-map": "npm:concat-map@0.0.1" + }, + "npm:browserify-aes@1.0.6": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "buffer-xor": "npm:buffer-xor@1.0.3", + "cipher-base": "npm:cipher-base@1.0.4", + "create-hash": "npm:create-hash@1.1.3", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "evp_bytestokey": "npm:evp_bytestokey@1.0.0", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "inherits": "npm:inherits@2.0.1", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:browserify-cipher@1.0.0": { + "browserify-aes": "npm:browserify-aes@1.0.6", + "browserify-des": "npm:browserify-des@1.0.0", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "evp_bytestokey": "npm:evp_bytestokey@1.0.0" + }, + "npm:browserify-des@1.0.0": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "cipher-base": "npm:cipher-base@1.0.4", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "des.js": "npm:des.js@1.0.0", + "inherits": "npm:inherits@2.0.1" + }, + "npm:browserify-rsa@4.0.1": { + "bn.js": "npm:bn.js@4.11.7", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "constants": "github:jspm/nodelibs-constants@0.1.0", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "randombytes": "npm:randombytes@2.0.5" + }, + "npm:browserify-sign@4.0.4": { + "bn.js": "npm:bn.js@4.11.7", + "browserify-rsa": "npm:browserify-rsa@4.0.1", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "create-hash": "npm:create-hash@1.1.3", + "create-hmac": "npm:create-hmac@1.1.6", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "elliptic": "npm:elliptic@6.4.0", + "inherits": "npm:inherits@2.0.1", + "parse-asn1": "npm:parse-asn1@5.1.0", + "stream": "github:jspm/nodelibs-stream@0.1.0", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:browserify-zlib@0.1.4": { + "assert": "github:jspm/nodelibs-assert@0.1.0", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "pako": "npm:pako@0.2.9", + "process": "github:jspm/nodelibs-process@0.1.2", + "readable-stream": "npm:readable-stream@2.3.3", + "util": "github:jspm/nodelibs-util@0.1.0" + }, + "npm:buffer-xor@1.0.3": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, "npm:buffer@5.0.6": { "base64-js": "npm:base64-js@1.2.1", "ieee754": "npm:ieee754@1.1.8" }, + "npm:cipher-base@1.0.4": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "inherits": "npm:inherits@2.0.1", + "safe-buffer": "npm:safe-buffer@5.1.1", + "stream": "github:jspm/nodelibs-stream@0.1.0", + "string_decoder": "github:jspm/nodelibs-string_decoder@0.1.0" + }, + "npm:commander@2.11.0": { + "child_process": "github:jspm/nodelibs-child_process@0.1.0", + "events": "github:jspm/nodelibs-events@0.1.1", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "path": "github:jspm/nodelibs-path@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:commoner@0.10.8": { + "assert": "github:jspm/nodelibs-assert@0.1.0", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "child_process": "github:jspm/nodelibs-child_process@0.1.0", + "commander": "npm:commander@2.11.0", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "detective": "npm:detective@4.5.0", + "events": "github:jspm/nodelibs-events@0.1.1", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "glob": "npm:glob@5.0.15", + "graceful-fs": "npm:graceful-fs@4.1.11", + "iconv-lite": "npm:iconv-lite@0.4.18", + "mkdirp": "npm:mkdirp@0.5.1", + "path": "github:jspm/nodelibs-path@0.1.0", + "private": "npm:private@0.1.7", + "process": "github:jspm/nodelibs-process@0.1.2", + "q": "npm:q@1.5.0", + "recast": "npm:recast@0.11.23", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:constants-browserify@0.0.1": { + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, "npm:core-js@1.2.7": { "fs": "github:jspm/nodelibs-fs@0.1.2", "path": "github:jspm/nodelibs-path@0.1.0", "process": "github:jspm/nodelibs-process@0.1.2", "systemjs-json": "github:systemjs/plugin-json@0.1.2" }, + "npm:core-js@2.4.1": { + "fs": "github:jspm/nodelibs-fs@0.1.2", + "path": "github:jspm/nodelibs-path@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:core-util-is@1.0.2": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1" + }, + "npm:create-ecdh@4.0.0": { + "bn.js": "npm:bn.js@4.11.7", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "elliptic": "npm:elliptic@6.4.0" + }, + "npm:create-hash@1.1.3": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "cipher-base": "npm:cipher-base@1.0.4", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "inherits": "npm:inherits@2.0.1", + "ripemd160": "npm:ripemd160@2.0.1", + "sha.js": "npm:sha.js@2.4.8" + }, + "npm:create-hmac@1.1.6": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "cipher-base": "npm:cipher-base@1.0.4", + "create-hash": "npm:create-hash@1.1.3", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "inherits": "npm:inherits@2.0.1", + "ripemd160": "npm:ripemd160@2.0.1", + "safe-buffer": "npm:safe-buffer@5.1.1", + "sha.js": "npm:sha.js@2.4.8" + }, + "npm:create-react-class@15.6.0": { + "fbjs": "npm:fbjs@0.8.12", + "loose-envify": "npm:loose-envify@1.3.1", + "object-assign": "npm:object-assign@4.1.1", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:crypto-browserify@3.11.1": { + "browserify-cipher": "npm:browserify-cipher@1.0.0", + "browserify-sign": "npm:browserify-sign@4.0.4", + "create-ecdh": "npm:create-ecdh@4.0.0", + "create-hash": "npm:create-hash@1.1.3", + "create-hmac": "npm:create-hmac@1.1.6", + "diffie-hellman": "npm:diffie-hellman@5.0.2", + "inherits": "npm:inherits@2.0.1", + "pbkdf2": "npm:pbkdf2@3.0.12", + "public-encrypt": "npm:public-encrypt@4.0.0", + "randombytes": "npm:randombytes@2.0.5" + }, + "npm:des.js@1.0.0": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "inherits": "npm:inherits@2.0.1", + "minimalistic-assert": "npm:minimalistic-assert@1.0.0" + }, + "npm:detective@4.5.0": { + "acorn": "npm:acorn@4.0.13", + "defined": "npm:defined@1.0.0", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:diffie-hellman@5.0.2": { + "bn.js": "npm:bn.js@4.11.7", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "miller-rabin": "npm:miller-rabin@4.0.0", + "randombytes": "npm:randombytes@2.0.5", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:domain-browser@1.1.7": { + "events": "github:jspm/nodelibs-events@0.1.1" + }, + "npm:elliptic@6.4.0": { + "bn.js": "npm:bn.js@4.11.7", + "brorand": "npm:brorand@1.1.0", + "hash.js": "npm:hash.js@1.1.3", + "hmac-drbg": "npm:hmac-drbg@1.0.1", + "inherits": "npm:inherits@2.0.1", + "minimalistic-assert": "npm:minimalistic-assert@1.0.0", + "minimalistic-crypto-utils": "npm:minimalistic-crypto-utils@1.0.1", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:encoding@0.1.12": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "iconv-lite": "npm:iconv-lite@0.4.18" + }, + "npm:esprima-fb@13001.1001.0-dev-harmony-fb": { + "fs": "github:jspm/nodelibs-fs@0.1.2", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:esprima@3.1.3": { + "fs": "github:jspm/nodelibs-fs@0.1.2", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:evp_bytestokey@1.0.0": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "create-hash": "npm:create-hash@1.1.3", + "crypto": "github:jspm/nodelibs-crypto@0.1.0" + }, + "npm:fbjs@0.8.12": { + "core-js": "npm:core-js@1.2.7", + "isomorphic-fetch": "npm:isomorphic-fetch@2.2.1", + "loose-envify": "npm:loose-envify@1.3.1", + "object-assign": "npm:object-assign@4.1.1", + "process": "github:jspm/nodelibs-process@0.1.2", + "promise": "npm:promise@7.3.1", + "setimmediate": "npm:setimmediate@1.0.5", + "ua-parser-js": "npm:ua-parser-js@0.7.14" + }, + "npm:glob@5.0.15": { + "assert": "github:jspm/nodelibs-assert@0.1.0", + "events": "github:jspm/nodelibs-events@0.1.1", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "inflight": "npm:inflight@1.0.6", + "inherits": "npm:inherits@2.0.1", + "minimatch": "npm:minimatch@3.0.4", + "once": "npm:once@1.4.0", + "path": "github:jspm/nodelibs-path@0.1.0", + "path-is-absolute": "npm:path-is-absolute@1.0.1", + "process": "github:jspm/nodelibs-process@0.1.2", + "util": "github:jspm/nodelibs-util@0.1.0" + }, + "npm:graceful-fs@4.1.11": { + "assert": "github:jspm/nodelibs-assert@0.1.0", + "constants": "github:jspm/nodelibs-constants@0.1.0", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "process": "github:jspm/nodelibs-process@0.1.2", + "stream": "github:jspm/nodelibs-stream@0.1.0", + "util": "github:jspm/nodelibs-util@0.1.0" + }, + "npm:hash-base@2.0.2": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "inherits": "npm:inherits@2.0.1", + "stream": "github:jspm/nodelibs-stream@0.1.0" + }, + "npm:hash.js@1.1.3": { + "inherits": "npm:inherits@2.0.3", + "minimalistic-assert": "npm:minimalistic-assert@1.0.0" + }, + "npm:history@4.6.3": { + "invariant": "npm:invariant@2.2.2", + "loose-envify": "npm:loose-envify@1.3.1", + "resolve-pathname": "npm:resolve-pathname@2.1.0", + "value-equal": "npm:value-equal@0.2.1", + "warning": "npm:warning@3.0.0" + }, + "npm:hmac-drbg@1.0.1": { + "hash.js": "npm:hash.js@1.1.3", + "minimalistic-assert": "npm:minimalistic-assert@1.0.0", + "minimalistic-crypto-utils": "npm:minimalistic-crypto-utils@1.0.1", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:https-browserify@0.0.0": { + "http": "github:jspm/nodelibs-http@1.7.1" + }, + "npm:iconv-lite@0.4.18": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "process": "github:jspm/nodelibs-process@0.1.2", + "stream": "github:jspm/nodelibs-stream@0.1.0", + "string_decoder": "github:jspm/nodelibs-string_decoder@0.1.0", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:inflight@1.0.6": { + "once": "npm:once@1.4.0", + "process": "github:jspm/nodelibs-process@0.1.2", + "wrappy": "npm:wrappy@1.0.2" + }, "npm:inherits@2.0.1": { "util": "github:jspm/nodelibs-util@0.1.0" }, + "npm:inherits@2.0.3": { + "util": "github:jspm/nodelibs-util@0.1.0" + }, + "npm:intl-messageformat@1.3.0": { + "intl-messageformat-parser": "npm:intl-messageformat-parser@1.2.0" + }, + "npm:intl-relativeformat@1.3.0": { + "intl-messageformat": "npm:intl-messageformat@1.3.0" + }, + "npm:invariant@2.2.2": { + "loose-envify": "npm:loose-envify@1.3.1", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:isomorphic-fetch@2.2.1": { + "node-fetch": "npm:node-fetch@1.7.1", + "whatwg-fetch": "npm:whatwg-fetch@2.0.3" + }, + "npm:jstransform@10.1.0": { + "base62": "npm:base62@0.1.1", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "esprima-fb": "npm:esprima-fb@13001.1001.0-dev-harmony-fb", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "process": "github:jspm/nodelibs-process@0.1.2", + "source-map": "npm:source-map@0.1.31" + }, + "npm:loose-envify@1.3.1": { + "fs": "github:jspm/nodelibs-fs@0.1.2", + "js-tokens": "npm:js-tokens@3.0.2", + "process": "github:jspm/nodelibs-process@0.1.2", + "stream": "github:jspm/nodelibs-stream@0.1.0", + "util": "github:jspm/nodelibs-util@0.1.0" + }, + "npm:miller-rabin@4.0.0": { + "bn.js": "npm:bn.js@4.11.7", + "brorand": "npm:brorand@1.1.0" + }, + "npm:minimatch@3.0.4": { + "brace-expansion": "npm:brace-expansion@1.1.8", + "path": "github:jspm/nodelibs-path@0.1.0" + }, + "npm:mkdirp@0.5.1": { + "fs": "github:jspm/nodelibs-fs@0.1.2", + "minimist": "npm:minimist@0.0.8", + "path": "github:jspm/nodelibs-path@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:node-fetch@1.7.1": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "encoding": "npm:encoding@0.1.12", + "http": "github:jspm/nodelibs-http@1.7.1", + "https": "github:jspm/nodelibs-https@0.1.0", + "is-stream": "npm:is-stream@1.1.0", + "process": "github:jspm/nodelibs-process@0.1.2", + "stream": "github:jspm/nodelibs-stream@0.1.0", + "url": "github:jspm/nodelibs-url@0.1.0", + "util": "github:jspm/nodelibs-util@0.1.0", + "zlib": "github:jspm/nodelibs-zlib@0.1.0" + }, + "npm:once@1.4.0": { + "wrappy": "npm:wrappy@1.0.2" + }, + "npm:os-browserify@0.1.2": { + "os": "github:jspm/nodelibs-os@0.1.0" + }, + "npm:pako@0.2.9": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:parse-asn1@5.1.0": { + "asn1.js": "npm:asn1.js@4.9.1", + "browserify-aes": "npm:browserify-aes@1.0.6", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "create-hash": "npm:create-hash@1.1.3", + "evp_bytestokey": "npm:evp_bytestokey@1.0.0", + "pbkdf2": "npm:pbkdf2@3.0.12", + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, "npm:path-browserify@0.0.0": { "process": "github:jspm/nodelibs-process@0.1.2" }, + "npm:path-is-absolute@1.0.1": { + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:path-to-regexp@1.7.0": { + "isarray": "npm:isarray@0.0.1" + }, + "npm:pbkdf2@3.0.12": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "create-hash": "npm:create-hash@1.1.3", + "create-hmac": "npm:create-hmac@1.1.6", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2", + "ripemd160": "npm:ripemd160@2.0.1", + "safe-buffer": "npm:safe-buffer@5.1.1", + "sha.js": "npm:sha.js@2.4.8" + }, + "npm:process-nextick-args@1.0.7": { + "process": "github:jspm/nodelibs-process@0.1.2" + }, "npm:process@0.11.10": { "assert": "github:jspm/nodelibs-assert@0.1.0", "fs": "github:jspm/nodelibs-fs@0.1.2", "vm": "github:jspm/nodelibs-vm@0.1.0" }, + "npm:promise@7.3.1": { + "asap": "npm:asap@2.0.6", + "fs": "github:jspm/nodelibs-fs@0.1.2" + }, + "npm:prop-types-extra@1.0.1": { + "react": "npm:react@15.6.1", + "warning": "npm:warning@3.0.0" + }, + "npm:prop-types@15.5.10": { + "fbjs": "npm:fbjs@0.8.12", + "loose-envify": "npm:loose-envify@1.3.1", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:public-encrypt@4.0.0": { + "bn.js": "npm:bn.js@4.11.7", + "browserify-rsa": "npm:browserify-rsa@4.0.1", + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "create-hash": "npm:create-hash@1.1.3", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "parse-asn1": "npm:parse-asn1@5.1.0", + "randombytes": "npm:randombytes@2.0.5" + }, + "npm:punycode@1.3.2": { + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:q@1.5.0": { + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:randombytes@2.0.5": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "crypto": "github:jspm/nodelibs-crypto@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2", + "safe-buffer": "npm:safe-buffer@5.1.1" + }, + "npm:react-bootstrap@0.31.1": { + "babel-runtime": "npm:babel-runtime@6.23.0", + "classnames": "npm:classnames@2.2.5", + "dom-helpers": "npm:dom-helpers@3.2.1", + "invariant": "npm:invariant@2.2.2", + "keycode": "npm:keycode@2.1.9", + "process": "github:jspm/nodelibs-process@0.1.2", + "prop-types": "npm:prop-types@15.5.10", + "prop-types-extra": "npm:prop-types-extra@1.0.1", + "react": "npm:react@15.6.1", + "react-dom": "npm:react-dom@15.6.1", + "react-overlays": "npm:react-overlays@0.7.0", + "react-prop-types": "npm:react-prop-types@0.4.0", + "uncontrollable": "npm:uncontrollable@4.1.0", + "warning": "npm:warning@3.0.0" + }, + "npm:react-dom@15.6.1": { + "fbjs": "npm:fbjs@0.8.12", + "loose-envify": "npm:loose-envify@1.3.1", + "object-assign": "npm:object-assign@4.1.1", + "process": "github:jspm/nodelibs-process@0.1.2", + "prop-types": "npm:prop-types@15.5.10", + "react": "npm:react@15.6.1" + }, + "npm:react-helmet@5.1.3": { + "deep-equal": "npm:deep-equal@1.0.1", + "object-assign": "npm:object-assign@4.1.1", + "process": "github:jspm/nodelibs-process@0.1.2", + "prop-types": "npm:prop-types@15.5.10", + "react": "npm:react@15.6.1", + "react-side-effect": "npm:react-side-effect@1.1.3" + }, + "npm:react-intl@2.3.0": { + "intl-format-cache": "npm:intl-format-cache@2.0.5", + "intl-messageformat": "npm:intl-messageformat@1.3.0", + "intl-relativeformat": "npm:intl-relativeformat@1.3.0", + "invariant": "npm:invariant@2.2.2", + "process": "github:jspm/nodelibs-process@0.1.2", + "prop-types": "npm:prop-types@15.5.10", + "react": "npm:react@15.6.1" + }, + "npm:react-overlays@0.7.0": { + "classnames": "npm:classnames@2.2.5", + "dom-helpers": "npm:dom-helpers@3.2.1", + "prop-types": "npm:prop-types@15.5.10", + "react": "npm:react@15.6.1", + "react-dom": "npm:react-dom@15.6.1", + "react-prop-types": "npm:react-prop-types@0.4.0", + "warning": "npm:warning@3.0.0" + }, + "npm:react-prop-types@0.4.0": { + "react": "npm:react@15.6.1", + "warning": "npm:warning@3.0.0" + }, + "npm:react-redux@5.0.5": { + "create-react-class": "npm:create-react-class@15.6.0", + "hoist-non-react-statics": "npm:hoist-non-react-statics@1.2.0", + "invariant": "npm:invariant@2.2.2", + "lodash": "npm:lodash@4.17.4", + "lodash-es": "npm:lodash-es@4.17.4", + "loose-envify": "npm:loose-envify@1.3.1", + "process": "github:jspm/nodelibs-process@0.1.2", + "prop-types": "npm:prop-types@15.5.10", + "react": "npm:react@15.6.1" + }, + "npm:react-router-bootstrap@0.24.2": { + "prop-types": "npm:prop-types@15.5.10", + "react": "npm:react@15.6.1", + "react-router-dom": "npm:react-router-dom@4.1.2" + }, + "npm:react-router-config@1.0.0-beta.3": { + "react": "npm:react@15.6.1", + "react-router": "npm:react-router@4.1.2" + }, + "npm:react-router-dom@4.1.2": { + "history": "npm:history@4.6.3", + "loose-envify": "npm:loose-envify@1.3.1", + "prop-types": "npm:prop-types@15.5.10", + "react": "npm:react@15.6.1", + "react-router": "npm:react-router@4.1.2" + }, + "npm:react-router@4.1.2": { + "history": "npm:history@4.6.3", + "hoist-non-react-statics": "npm:hoist-non-react-statics@1.2.0", + "invariant": "npm:invariant@2.2.2", + "loose-envify": "npm:loose-envify@1.3.1", + "path-to-regexp": "npm:path-to-regexp@1.7.0", + "prop-types": "npm:prop-types@15.5.10", + "react": "npm:react@15.6.1", + "warning": "npm:warning@3.0.0" + }, + "npm:react-side-effect@1.1.3": { + "exenv": "npm:exenv@1.2.2", + "shallowequal": "npm:shallowequal@1.0.2" + }, + "npm:react-tools@0.13.3": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "commoner": "npm:commoner@0.10.8", + "jstransform": "npm:jstransform@10.1.0", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:react@15.6.1": { + "create-react-class": "npm:create-react-class@15.6.0", + "fbjs": "npm:fbjs@0.8.12", + "loose-envify": "npm:loose-envify@1.3.1", + "object-assign": "npm:object-assign@4.1.1", + "process": "github:jspm/nodelibs-process@0.1.2", + "prop-types": "npm:prop-types@15.5.10" + }, + "npm:readable-stream@1.1.14": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "core-util-is": "npm:core-util-is@1.0.2", + "events": "github:jspm/nodelibs-events@0.1.1", + "inherits": "npm:inherits@2.0.1", + "isarray": "npm:isarray@0.0.1", + "process": "github:jspm/nodelibs-process@0.1.2", + "stream-browserify": "npm:stream-browserify@1.0.0", + "string_decoder": "npm:string_decoder@0.10.31" + }, + "npm:readable-stream@2.3.3": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "core-util-is": "npm:core-util-is@1.0.2", + "events": "github:jspm/nodelibs-events@0.1.1", + "inherits": "npm:inherits@2.0.3", + "isarray": "npm:isarray@1.0.0", + "process": "github:jspm/nodelibs-process@0.1.2", + "process-nextick-args": "npm:process-nextick-args@1.0.7", + "safe-buffer": "npm:safe-buffer@5.1.1", + "stream": "github:jspm/nodelibs-stream@0.1.0", + "string_decoder": "npm:string_decoder@1.0.3", + "util-deprecate": "npm:util-deprecate@1.0.2" + }, + "npm:recast@0.11.23": { + "assert": "github:jspm/nodelibs-assert@0.1.0", + "ast-types": "npm:ast-types@0.9.6", + "esprima": "npm:esprima@3.1.3", + "os": "github:jspm/nodelibs-os@0.1.0", + "private": "npm:private@0.1.7", + "process": "github:jspm/nodelibs-process@0.1.2", + "source-map": "npm:source-map@0.5.6" + }, + "npm:regenerator-runtime@0.10.5": { + "path": "github:jspm/nodelibs-path@0.1.0" + }, + "npm:ripemd160@2.0.1": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "hash-base": "npm:hash-base@2.0.2", + "inherits": "npm:inherits@2.0.1" + }, + "npm:safe-buffer@5.1.1": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1" + }, + "npm:setimmediate@1.0.5": { + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:sha.js@2.4.8": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "inherits": "npm:inherits@2.0.1", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:source-map@0.1.31": { + "amdefine": "npm:amdefine@1.0.1", + "fs": "github:jspm/nodelibs-fs@0.1.2", + "path": "github:jspm/nodelibs-path@0.1.0", + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:source-map@0.5.6": { + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:stream-browserify@1.0.0": { + "events": "github:jspm/nodelibs-events@0.1.1", + "inherits": "npm:inherits@2.0.1", + "readable-stream": "npm:readable-stream@1.1.14" + }, + "npm:string_decoder@0.10.31": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1" + }, + "npm:string_decoder@1.0.3": { + "buffer": "github:jspm/nodelibs-buffer@0.1.1", + "safe-buffer": "npm:safe-buffer@5.1.1" + }, + "npm:ua-parser-js@0.7.14": { + "systemjs-json": "github:systemjs/plugin-json@0.1.2" + }, + "npm:uncontrollable@4.1.0": { + "invariant": "npm:invariant@2.2.2", + "process": "github:jspm/nodelibs-process@0.1.2", + "react": "npm:react@15.6.1" + }, + "npm:url@0.10.3": { + "assert": "github:jspm/nodelibs-assert@0.1.0", + "punycode": "npm:punycode@1.3.2", + "querystring": "npm:querystring@0.2.0", + "util": "github:jspm/nodelibs-util@0.1.0" + }, + "npm:util-deprecate@1.0.2": { + "util": "github:jspm/nodelibs-util@0.1.0" + }, "npm:util@0.10.3": { "inherits": "npm:inherits@2.0.1", "process": "github:jspm/nodelibs-process@0.1.2" }, "npm:vm-browserify@0.0.4": { "indexof": "npm:indexof@0.0.1" + }, + "npm:warning@3.0.0": { + "loose-envify": "npm:loose-envify@1.3.1", + "process": "github:jspm/nodelibs-process@0.1.2" } } }); From f096e7e1e9f5bb10c33937242fce4872a698ec31 Mon Sep 17 00:00:00 2001 From: viktornar Date: Tue, 25 Jul 2017 23:33:21 +0300 Subject: [PATCH 03/15] Added ui server with live reload capability. Lets start dev :) --- index.js | 15 +++++++++++++++ package.json | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 index.js diff --git a/index.js b/index.js new file mode 100644 index 0000000000..314d7736f6 --- /dev/null +++ b/index.js @@ -0,0 +1,15 @@ +var liveServer = require('live-server'); +var proxy = require('http-proxy-middleware'); + +var params = { + port: 8080, + host: '0.0.0.0', + root: './ui', + ignore: 'jspm_packages', + wait: 1000, + // mount: [[]], + logLevel: 2, + // middleware: [proxy('http://127.0.0.1:3010')], +}; + +liveServer.start(params); \ No newline at end of file diff --git a/package.json b/package.json index 3a79ba67a5..e1522a4312 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "1.0.0", "description": "A package containing a simple dummy book API, which should be used in the challenge", "scripts": { - "start": "json-server --port 3010 --watch api.json" + "start-api": "json-server --port 3010 --watch api.json", + "start-ui": "node index.js" }, "keywords": [ "react challenge", From 349ba9a78807ee8fcba53f49b57cbf436b88d959 Mon Sep 17 00:00:00 2001 From: viktornar Date: Tue, 25 Jul 2017 23:36:27 +0300 Subject: [PATCH 04/15] Yep. Now I have working hello world example. Let's pimp this app. --- ui/index.html | 13 +++++++++++++ ui/src/main.js | 7 +++++++ 2 files changed, 20 insertions(+) create mode 100644 ui/index.html create mode 100644 ui/src/main.js diff --git a/ui/index.html b/ui/index.html new file mode 100644 index 0000000000..9a55dba9ea --- /dev/null +++ b/ui/index.html @@ -0,0 +1,13 @@ + + + + + +
+ + + + + \ No newline at end of file diff --git a/ui/src/main.js b/ui/src/main.js new file mode 100644 index 0000000000..ae5df08cf6 --- /dev/null +++ b/ui/src/main.js @@ -0,0 +1,7 @@ +import React from 'react'; +import { render } from 'react-dom'; + + +render(( +

Hello world!!!

+), document.getElementById('root')); From 95ad203fc2c25f9ecaa8bdaa13a45a945a25c0b9 Mon Sep 17 00:00:00 2001 From: viktornar Date: Tue, 25 Jul 2017 23:48:29 +0300 Subject: [PATCH 05/15] Added react-router for navigation. Implemented RoutesRenderer for nested routes using (some parts of code was borrowed from others repos :)). --- ui/src/app/app-comp.js | 32 ++++++++++++++++++++++++++++++++ ui/src/app/config.js | 33 +++++++++++++++++++++++++++++++++ ui/src/app/route-utils.js | 33 +++++++++++++++++++++++++++++++++ ui/src/main.js | 7 ++++++- 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 ui/src/app/app-comp.js create mode 100644 ui/src/app/config.js create mode 100644 ui/src/app/route-utils.js diff --git a/ui/src/app/app-comp.js b/ui/src/app/app-comp.js new file mode 100644 index 0000000000..bba9fbf92f --- /dev/null +++ b/ui/src/app/app-comp.js @@ -0,0 +1,32 @@ +import React, { Component } from 'react'; +import { Link } from 'react-router-dom'; + +import { RoutesRenderer } from './route-utils'; +import { routes } from './config'; + + +class App extends Component { + constructor(props) { + super(props); + } + + render() { + return ( +
+
+ +
+ {routes.map((route, i) => ( + + ))} +
+ ); + } +} + +export default App; diff --git a/ui/src/app/config.js b/ui/src/app/config.js new file mode 100644 index 0000000000..4b800da972 --- /dev/null +++ b/ui/src/app/config.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { RootRedirecter } from './route-utils'; + + +const Books = () => { + return ( +

Hello from books

+ ) +} + +const About = () => { + return ( +

Hello from about

+ ) +} + +export const routes = [ + { + path: '/', + exact: true, + component: () => (), + }, + { + path: '/books', + exact: true, + component: Books, + }, + { + path: '/about', + exact: true, + component: About, + }, +]; \ No newline at end of file diff --git a/ui/src/app/route-utils.js b/ui/src/app/route-utils.js new file mode 100644 index 0000000000..7a5cb70bc4 --- /dev/null +++ b/ui/src/app/route-utils.js @@ -0,0 +1,33 @@ +import React, { Component, PropTypes } from 'react'; +import { Route, Redirect } from 'react-router-dom'; + +export const RoutesRenderer = (route) => ( + ( + React.cloneElement( + + ) + )}/> +); + +export class RootRedirecter extends Component { + constructor(props) { + super(props); + } + + render() { + const { to } = this.props; + + return ; + } +} + +RootRedirecter.propTypes = { + to: PropTypes.string.isRequired, +}; diff --git a/ui/src/main.js b/ui/src/main.js index ae5df08cf6..2674cc4490 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -1,7 +1,12 @@ import React from 'react'; import { render } from 'react-dom'; +import { HashRouter as Router } from 'react-router-dom'; + +import App from './app/app-comp'; render(( -

Hello world!!!

+ + + ), document.getElementById('root')); From 6c90f186cbe1432c042b0a3b0841a9bbf1991863 Mon Sep 17 00:00:00 2001 From: viktornar Date: Tue, 25 Jul 2017 23:51:54 +0300 Subject: [PATCH 06/15] Completed bootsrap integration. From now I will use bootsrap components. --- ui/assets/logo.png | Bin 0 -> 1341 bytes ui/index.html | 33 ++++++++++++++++++++++++++++++++ ui/src/app/app-comp.js | 42 ++++++++++++++++++++++++++++------------- 3 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 ui/assets/logo.png diff --git a/ui/assets/logo.png b/ui/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4f8bf95fc5024d7149356d9f3857c483fa6e6c GIT binary patch literal 1341 zcmV-D1;YA?P)2bKv07;F(^>TwHd-uV$35Zr}*xq(vB(b18Xpsk^xzI;^L z>GIv(-RPH5qRGC2m7mR>cFf}M#EMmeZDy6IjJqOrr%(ssPRTXv7l%*vd+ zwUC*FNKtLPXKI&}s-=jUpk!*Bkfw>DyK8ZUqqe)0xyp!_mW;l$na*IA000C0NklTH{>1azP>x@U7gKp$l?B zqPYWhJ`D2nSu*25FwB7&y08EwHi+vAJd6j5Qo^uxcq_zWkgwvyVnLTPaSX>)9z^y(8F+7C#KUmYdnSbU5p z`?7VpL+G+4;kby031oX^t6ihimT;hhSd7%kT06Crb|f6a-R2nS@{iM$T5X*D1+hFc zOdwr;7Sons@b;F%AbDJl+l0O-*eTOR%A9~hkj@VGhCBgcj2-UigxAOEn) zdqxxRu-uuce!=6|Bt{`H;p91^$-~leeXh<0`<3Zh3C2>`FnO3iOC+XVe`Lpjc7jpa zWq6a8!gMy-abl+qF-U1ITX@UpH^y|bF|j!z#uj0EiMS5EvMjX*b7G7zW^S%O&2)Q@ zF-&;wAMG6x+pytrF~JlQ2yD4~mS}bThO-Oo?M*_m1m0042A?`Wv~A)^d!6WX z_q4>?Km-IHP@$(`%uv7pOn8>Ui1xFJG4}t2F&+N@pD<6DC(J(&PhFTG$3ej4wm)(l z1ggi{*;n>P@L44t1ktgkd`N#~?>CB=@Q_wcIf`S#ltVdzDuMBGq=VnzF#3j*TSJ=M zM(Ln>a$D5XqU2VXs#g{_(^OsY;!z?zy{v0gRhal1CAt(=hH*q!+^E!ZN4voK);mV* zO*&M1ut0EOHwXQ0x4+$>warmCrSQ8!>POEm?CJ6Y92_@F=y*U4my{n;#T3{tLuoYn zNi6Kivv^nskDIZI$<_R$vRp?5U?MD4(3H3+hoKa;vx(K;AdQn27 zMba3m2ua6IYq6Aw>U6rKN)?EE3#HUcDCq(*UGDC+N+_Xl%H5T9h)8jY_wrUmUU6Fy z3XPR~5;RtHwTW~!y)?D(bTuS(wO4gDi*>cWbv5!0P0tbxU5_kuJsB}HJ%BNEJ?F^_ zD*8BS3exb@Dw2+eW0ABxgA1kQQD2NI_eAk8akkt%ELJ_I00000NkvXXu0mjfxLBFB literal 0 HcmV?d00001 diff --git a/ui/index.html b/ui/index.html index 9a55dba9ea..3160df9936 100644 --- a/ui/index.html +++ b/ui/index.html @@ -1,6 +1,39 @@ + +
diff --git a/ui/src/app/app-comp.js b/ui/src/app/app-comp.js index bba9fbf92f..371da43f9f 100644 --- a/ui/src/app/app-comp.js +++ b/ui/src/app/app-comp.js @@ -1,5 +1,6 @@ import React, { Component } from 'react'; -import { Link } from 'react-router-dom'; +import { Navbar, Nav, NavItem, Row, Image } from 'react-bootstrap'; +import { LinkContainer } from 'react-router-bootstrap'; import { RoutesRenderer } from './route-utils'; import { routes } from './config'; @@ -12,18 +13,33 @@ class App extends Component { render() { return ( -
-
- -
- {routes.map((route, i) => ( - - ))} +
+
+ + + + + + + + +
+ + {routes.map((route, i) => ( + + ))} +
); } From d9ed43a7c7102813b6c5699b5835c85b43c931d6 Mon Sep 17 00:00:00 2001 From: viktornar Date: Wed, 26 Jul 2017 00:09:02 +0300 Subject: [PATCH 07/15] Data fetching from the api is implemented in less or more level (thanks to my other project :)) --- .eslintrc | 3 + ui/src/app/about-comp.js | 8 +++ ui/src/app/books-comp.js | 38 +++++++++++ ui/src/app/config.js | 15 +---- ui/src/app/constants.js | 2 + ui/src/app/fetch-utils.js | 132 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 186 insertions(+), 12 deletions(-) create mode 100644 .eslintrc create mode 100644 ui/src/app/about-comp.js create mode 100644 ui/src/app/books-comp.js create mode 100644 ui/src/app/constants.js create mode 100644 ui/src/app/fetch-utils.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000000..00c9940fd7 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "fbjs" +} \ No newline at end of file diff --git a/ui/src/app/about-comp.js b/ui/src/app/about-comp.js new file mode 100644 index 0000000000..e4fea0b43a --- /dev/null +++ b/ui/src/app/about-comp.js @@ -0,0 +1,8 @@ +import React from 'react'; + + +const About = () => { + return (

Simple page application for getting info about books.

); +}; + +export default About; diff --git a/ui/src/app/books-comp.js b/ui/src/app/books-comp.js new file mode 100644 index 0000000000..226c9fb262 --- /dev/null +++ b/ui/src/app/books-comp.js @@ -0,0 +1,38 @@ +import React, { Component, PropTypes } from 'react'; + +import { get } from './fetch-utils'; +import { + SUBJECTS_API_PATH, +} from './constants'; + + +class Books extends Component { + constructor(props) { + super(props); + this.state = { subjects: [] }; + } + + componentDidMount() { + get(SUBJECTS_API_PATH).then((res) => { + this.setState({ ...{ subjects: res } }); + }); + } + + render() { + const { subjects } = this.state; + + return ( +
+
    + { + subjects.map((subject, idx) => { + return
  • {subject}
  • + }) + } +
+
+ ); + } +} + +export default Books; \ No newline at end of file diff --git a/ui/src/app/config.js b/ui/src/app/config.js index 4b800da972..7cddcbd276 100644 --- a/ui/src/app/config.js +++ b/ui/src/app/config.js @@ -1,18 +1,9 @@ import React from 'react'; -import { RootRedirecter } from './route-utils'; - -const Books = () => { - return ( -

Hello from books

- ) -} +import { RootRedirecter } from './route-utils'; +import About from './about-comp'; +import Books from './books-comp'; -const About = () => { - return ( -

Hello from about

- ) -} export const routes = [ { diff --git a/ui/src/app/constants.js b/ui/src/app/constants.js new file mode 100644 index 0000000000..4bdddf37de --- /dev/null +++ b/ui/src/app/constants.js @@ -0,0 +1,2 @@ +export const BASE_API_URL = 'http://127.0.0.1:3010'; +export const SUBJECTS_API_PATH = '/subjects'; \ No newline at end of file diff --git a/ui/src/app/fetch-utils.js b/ui/src/app/fetch-utils.js new file mode 100644 index 0000000000..587d6b5bb4 --- /dev/null +++ b/ui/src/app/fetch-utils.js @@ -0,0 +1,132 @@ +import 'isomorphic-fetch'; + +import { NO_DATA, BASE_API_URL } from './constants'; + + +export function checkStatus(response) { + if (response.ok) { + if (response.status === 204) { + return Promise.resolve({ + status: response.status, + statusText: response.statusText, + }); + } + // Workaround for API 200 status, when 'no data' has been returned + if (response.status === 200) { + return response.json() + // parsing json to get codeNumber, etc. + .then(json => { + if (json.codeNumber && json.codeNumber === NO_DATA) { + return Promise.resolve({ + noData: json + }); + } + return Promise.resolve(json); + }) + // if json parsing fails, return original response + .catch(() => { + return Promise.resolve(response); + }) + // return either json response or original + // response as rejected promise result + .then(result => { + if (result.noData) { + return Promise.reject(result.noData); + } + return Promise.resolve(result); + }); + } + return Promise.resolve(response.json()); + } + if (response.status === 400 + || response.status === 409 + || response.status === 404) { + + return response.json() + // parsing json to get codeNumber, etc. + .then(json => Promise.resolve(json)) + // if json parsing fails, return original error response + .catch(() => Promise.resolve(response)) + // return either json response or original + // error response as rejected promise result + .then(result => Promise.reject(result)); + } + return Promise.reject(response); +} + +export default function customFetch(url, options) { + return fetch(`${BASE_API_URL}${url}`, options); +} + +export function enhancedFetch(url, options = {}) { + options.credentials = 'same-origin'; + options.headers = Object.assign({ + Accept: 'application/json', + }, options.headers); + if (typeof options.body !== 'string') { + options.body = JSON.stringify(options.body); + } + return customFetch(url, options).then(checkStatus); +} + +export function get(url, headers) { + return enhancedFetch(url, { + method: 'get', + headers, + }); +} + +export function put(url, data) { + return enhancedFetch(url, { + method: 'put', + body: data, + headers: { + 'Content-Type': 'application/json', + }, + }); +} + +export function post(url, data) { + return enhancedFetch(url, { + method: 'post', + body: data, + headers: { + 'Content-Type': 'application/json', + }, + }); +} + +export function xform(url, data) { + return enhancedFetch(url, { + method: 'post', + body: data, + headers: { + Accept: 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', + }, + }); +} + +export function del(url) { + return enhancedFetch(url, { + method: 'delete', + }); +} + +export function fixedEncodeURIComponent(str) { + return encodeURIComponent(str).replace(/[!'()*]/g, (c) => { + return `%${c.charCodeAt(0).toString(16)}`; + }); +} + +export const createQueryUrl = (obj = {}) => { + const createItem = (k) => `${fixedEncodeURIComponent(k)}=${fixedEncodeURIComponent(obj[k])}`; + + return Object.keys(obj).map(createItem).join('&'); +}; + +export const createQueryWithAmpPrefix = (obj) => { + const queryUrl = createQueryUrl(obj); + + return queryUrl ? `&${queryUrl}` : ''; +}; \ No newline at end of file From 644b1f793f5de986dffee129a27f728eae15b230 Mon Sep 17 00:00:00 2001 From: viktornar Date: Wed, 26 Jul 2017 00:33:05 +0300 Subject: [PATCH 08/15] Implemented list of books for specific subject. --- ui/src/app/book-comp.js | 79 ++++++++++++++++++++++++++++++++++++++++ ui/src/app/books-comp.js | 32 ++++++++++++---- ui/src/app/constants.js | 5 ++- 3 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 ui/src/app/book-comp.js diff --git a/ui/src/app/book-comp.js b/ui/src/app/book-comp.js new file mode 100644 index 0000000000..794f4eede3 --- /dev/null +++ b/ui/src/app/book-comp.js @@ -0,0 +1,79 @@ +import React, { PropTypes, Component } from 'react'; +import pathToRegexp from 'path-to-regexp'; +import { ListGroup, ListGroupItem, Col, Row } from 'react-bootstrap'; +import { LinkContainer } from 'react-router-bootstrap'; + +import { get } from './fetch-utils'; +import { RoutesRenderer } from './route-utils'; +import { + BOOKS_API_PATH, + BOOK_DETAIL_ROUTE_PATH, +} from './constants'; + +const bookPath = pathToRegexp.compile(BOOKS_API_PATH); +const bookDetailPath = pathToRegexp.compile(BOOK_DETAIL_ROUTE_PATH); + +const defaultState = { + books: [ + { + id: null, + authors: [], + bookshelves: [], + download_count: null, + formats: {}, + languages: [], + media_type: null, + subjects:[], + title: null, + }, + ], +}; + +export class Book extends Component { + constructor(props) { + super(props); + this.state = { ...defaultState }; + } + + componentDidMount() { + const { subject } = this.props; + this.fetchBooks(subject); + } + + fetchBooks(bookId) { + get(bookPath({id: bookId})).then((res) => { + this.setState({ ...{ books: res } }); + }); + } + + render() { + const { subject } = this.props; + const { books } = this.state; + + return ( + + + + {books.map((book, idx) => { + const bookId = book.id || '#'; + return ( + + {book.title} + + ); + })} + + + + ); + } +} + +Book.propTypes = { + subject: PropTypes.string.isRequired, +}; + +export default Book; diff --git a/ui/src/app/books-comp.js b/ui/src/app/books-comp.js index 226c9fb262..daaa74d3a4 100644 --- a/ui/src/app/books-comp.js +++ b/ui/src/app/books-comp.js @@ -1,11 +1,33 @@ import React, { Component, PropTypes } from 'react'; +import { Tabs, Tab } from 'react-bootstrap'; +import Book from './book-comp'; import { get } from './fetch-utils'; import { SUBJECTS_API_PATH, } from './constants'; +const BookTabs = ({ subjects }) => { + const createTabs = () => (subjects.map((subject, idx) => { + return ( + + + + ); + })); + + return ( + + {createTabs()} + + ); +}; + +BookTabs.propTypes = { + subjects: PropTypes.array.isRequired, +}; + class Books extends Component { constructor(props) { super(props); @@ -20,16 +42,10 @@ class Books extends Component { render() { const { subjects } = this.state; - + return (
-
    - { - subjects.map((subject, idx) => { - return
  • {subject}
  • - }) - } -
+
); } diff --git a/ui/src/app/constants.js b/ui/src/app/constants.js index 4bdddf37de..0a3b6fdef0 100644 --- a/ui/src/app/constants.js +++ b/ui/src/app/constants.js @@ -1,2 +1,5 @@ export const BASE_API_URL = 'http://127.0.0.1:3010'; -export const SUBJECTS_API_PATH = '/subjects'; \ No newline at end of file +export const SUBJECTS_API_PATH = '/subjects'; +export const BOOKS_API_PATH = '/books?subjects_like=:id'; + +export const BOOK_DETAIL_ROUTE_PATH = '/books/:subject/:id'; \ No newline at end of file From ef370f5e9d718053189d5ec6aba6dfbd00534b00 Mon Sep 17 00:00:00 2001 From: viktornar Date: Wed, 26 Jul 2017 00:43:13 +0300 Subject: [PATCH 09/15] Preperation for details about book displaying. --- ui/src/app/book-comp.js | 13 ++++++++++++- ui/src/app/books-comp.js | 10 ++++++---- ui/src/app/config.js | 6 ++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/ui/src/app/book-comp.js b/ui/src/app/book-comp.js index 794f4eede3..b28988e2de 100644 --- a/ui/src/app/book-comp.js +++ b/ui/src/app/book-comp.js @@ -47,7 +47,7 @@ export class Book extends Component { } render() { - const { subject } = this.props; + const { routes, subject } = this.props; const { books } = this.state; return ( @@ -67,6 +67,16 @@ export class Book extends Component { })} + + {routes.map((route, i) => { + return ( + + ); + })} + ); } @@ -74,6 +84,7 @@ export class Book extends Component { Book.propTypes = { subject: PropTypes.string.isRequired, + routes: PropTypes.array.isRequired, }; export default Book; diff --git a/ui/src/app/books-comp.js b/ui/src/app/books-comp.js index daaa74d3a4..988089f064 100644 --- a/ui/src/app/books-comp.js +++ b/ui/src/app/books-comp.js @@ -8,11 +8,11 @@ import { } from './constants'; -const BookTabs = ({ subjects }) => { +const BookTabs = ({ subjects, routes }) => { const createTabs = () => (subjects.map((subject, idx) => { return ( - + ); })); @@ -26,6 +26,7 @@ const BookTabs = ({ subjects }) => { BookTabs.propTypes = { subjects: PropTypes.array.isRequired, + routes: PropTypes.array.isRequired, }; class Books extends Component { @@ -41,11 +42,12 @@ class Books extends Component { } render() { + const { routes } = this.props; const { subjects } = this.state; - + return (
- +
); } diff --git a/ui/src/app/config.js b/ui/src/app/config.js index 7cddcbd276..f7fba7df8d 100644 --- a/ui/src/app/config.js +++ b/ui/src/app/config.js @@ -15,6 +15,12 @@ export const routes = [ path: '/books', exact: true, component: Books, + routes: [ + { + path: '/books/:subject/:id', + component: () => (

Details page

), + }, + ], }, { path: '/about', From 6aa60173e2adac4836f0727dced75944c756d637 Mon Sep 17 00:00:00 2001 From: viktornar Date: Wed, 26 Jul 2017 01:39:54 +0300 Subject: [PATCH 10/15] Implemented ugly form. --- ui/src/app/config.js | 3 +- ui/src/app/constants.js | 4 +- ui/src/app/details-comp.js | 276 +++++++++++++++++++++++++++++++++++++ ui/src/app/ui-utils.js | 38 +++++ 4 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 ui/src/app/details-comp.js create mode 100644 ui/src/app/ui-utils.js diff --git a/ui/src/app/config.js b/ui/src/app/config.js index f7fba7df8d..29c21967eb 100644 --- a/ui/src/app/config.js +++ b/ui/src/app/config.js @@ -3,6 +3,7 @@ import React from 'react'; import { RootRedirecter } from './route-utils'; import About from './about-comp'; import Books from './books-comp'; +import Details from './details-comp'; export const routes = [ @@ -18,7 +19,7 @@ export const routes = [ routes: [ { path: '/books/:subject/:id', - component: () => (

Details page

), + component: Details, }, ], }, diff --git a/ui/src/app/constants.js b/ui/src/app/constants.js index 0a3b6fdef0..1e30c69ab6 100644 --- a/ui/src/app/constants.js +++ b/ui/src/app/constants.js @@ -1,5 +1,7 @@ export const BASE_API_URL = 'http://127.0.0.1:3010'; export const SUBJECTS_API_PATH = '/subjects'; export const BOOKS_API_PATH = '/books?subjects_like=:id'; +export const BOOK_DETAIL_API_PATH = '/books?subjects_like=:subject&id=:id'; +export const BOOK_DETAIL_UPDATE_API_PATH = '/books/:id'; -export const BOOK_DETAIL_ROUTE_PATH = '/books/:subject/:id'; \ No newline at end of file +export const BOOK_DETAIL_ROUTE_PATH = '/books/:subject/:id'; diff --git a/ui/src/app/details-comp.js b/ui/src/app/details-comp.js new file mode 100644 index 0000000000..45d7efaa5f --- /dev/null +++ b/ui/src/app/details-comp.js @@ -0,0 +1,276 @@ +import React, { PropTypes, Component } from 'react'; +import pathToRegexp from 'path-to-regexp'; +import { + Form, + FormGroup, + Col, + FormControl, + Button, + Jumbotron, +} from 'react-bootstrap'; +import _ from 'underscore'; + +import { get, put } from './fetch-utils'; +import { renderInputText, renderInputNumber } from './ui-utils'; +import { + BOOK_DETAIL_API_PATH, + BOOK_DETAIL_UPDATE_API_PATH, +} from './constants'; + +const bookDetailsPath = pathToRegexp.compile(BOOK_DETAIL_API_PATH); +const bookUpdatePath = pathToRegexp.compile(BOOK_DETAIL_UPDATE_API_PATH); + +const defaultState = { + details: { + id: null, + authors: [], + bookshelves: [], + download_count: null, + formats: {}, + languages: [], + media_type: null, + subjects:[], + title: null, + }, +}; + +export class Details extends Component { + constructor(props) { + super(props); + this.state = { + ...defaultState, + ...{ renderDetails: true }, + ...{ selectedItems: [] }, + }; + } + + componentDidMount() { + const { params } = this.props.match; + this.fetchBookDetails(params); + } + + componentWillReceiveProps(nextProps) { + const { params } = this.props.match; + const { params: nextParams } = nextProps.match; + + if (params.id !== nextParams.id || params.subject !== nextParams.subject) { + this.fetchBookDetails(nextParams); + } + } + + fetchBookDetails(params) { + get(bookDetailsPath(params)).then((res) => { + this.setState({ ...{ details: res[0] }, ...{ renderDetails: true } }); + }); + } + + textHandler(event) { + const fieldVal = event.target.value; + let fieldName = event.target.name; + let tokens = fieldName.split('['); + let detailsToUpdate; + + if (tokens.length > 1) { + fieldName = tokens[0]; + + const idx = tokens[1].split(']')[0]; + const key = tokens[1].split(']')[1].replace('.', ''); + + detailsToUpdate = {details: {...this.state.details}}; + key ? (detailsToUpdate.details[fieldName][idx][key] = fieldVal) : (detailsToUpdate.details[fieldName][idx] = fieldVal); + } else { + detailsToUpdate = { + details: {...this.state.details, [fieldName]: fieldVal}, + }; + } + + + this.setState({ ...detailsToUpdate }); + } + + numberHandler(event) { + const fieldVal = event.target.value; + const fieldName = event.target.name; + + const detailsToUpdate = { + details: {...this.state.details, [fieldName]: fieldVal}, + }; + + this.setState({ ...detailsToUpdate }); + } + + submitHandler(event) { + event.preventDefault(); + const { params } = this.props.match; + const { details } = this.state; + put(bookUpdatePath({ id: params.id }), details) + .then(() => {/* display nice message */}) + .catch(() => {/* display ugly message */}); + } + + addFormatHandler(event) { + event.preventDefault(); + let { details: detailsToUpdate } = this.state; + + if (this.formatKeyInput && this.formatValueInput) { + detailsToUpdate.formats[this.formatKeyInput.value] = this.formatValueInput.value; + this.formatKeyInput.value = ''; + this.formatValueInput.value = ''; + this.setState({ ...detailsToUpdate }); + } + } + + deleteFormatHandler(event) { + event.preventDefault(); + const { selectedItems, details } = this.state; + const formats = _.omit(details.formats, selectedItems); + let { details: detailsToUpdate } = this.state; + detailsToUpdate.formats = formats; + this.setState({ ...detailsToUpdate }); + } + + selectHandler(event) { + const target = event.target; + const options = [].slice.call(target.querySelectorAll('option')); + const items = options.filter((option) => (option.selected)).map((option) => (option.value)); + this.setState({ ...{ selectedItems: items } }); + } + + render() { + const { details, renderDetails } = this.state; + + return ( +
+ {(details.id ? renderDetails : false) ? ( +
+ { + renderInputText( + 'title', + 'Title', + details.title + )(this.textHandler.bind(this)) + } + { + details.authors.map((author, idx) => { + return ( + + { + renderInputText( + `authors[${idx}].name`, + 'Author name', + author.name + )(this.textHandler.bind(this)) + } + { + renderInputText( + `authors[${idx}].birth_year`, + 'Author birth year', + author.birth_year + )(this.textHandler.bind(this)) + } + { + renderInputText( + `authors[${idx}].death_year`, + 'Author death year', + author.death_year + )(this.textHandler.bind(this)) + } + + ); + }) + } + {details.bookshelves.map((bookshelve, idx) => { + return ( + + { + renderInputText( + `bookshelves[${idx}]`, + 'Bookshelve', + bookshelve + )(this.textHandler.bind(this)) + } + + ); + })} + { + renderInputNumber( + 'download_count', + 'Download count', + details.download_count + )(this.numberHandler.bind(this)) + } + + + + + Formats + + + + {Object.keys(details.formats).map((key, idx) => { + return ; + })} + + + + + + + + + + + Format key + + + {this.formatKeyInput = ref;}} + defaultValue="" + /> + + + + + Format value + + + {this.formatValueInput = ref;}} + defaultValue="" + /> + + + + + + + + + + + + + +
+ ) : ''} +
+ ); + } +} + +Details.propTypes = { + match: PropTypes.object.isRequired, +}; + +export default Details; diff --git a/ui/src/app/ui-utils.js b/ui/src/app/ui-utils.js new file mode 100644 index 0000000000..2e56a53767 --- /dev/null +++ b/ui/src/app/ui-utils.js @@ -0,0 +1,38 @@ +import React from 'react'; +import { FormGroup, Col, FormControl } from 'react-bootstrap'; + +export const renderInputText = (name, label, value) => (callback) => { + return ( + + + {label} + + + + + + ); +}; + +export const renderInputNumber = (name, label, value) => (callback) => { + return ( + + + {label} + + + + + + ); +} \ No newline at end of file From 95983ca037d1c9380fb9ae872be7c4e9b24f49bd Mon Sep 17 00:00:00 2001 From: viktornar Date: Wed, 26 Jul 2017 01:51:19 +0300 Subject: [PATCH 11/15] Fixed bug that prevented from book list updating. Probably I need to start using redux? --- ui/src/app/details-comp.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/src/app/details-comp.js b/ui/src/app/details-comp.js index 45d7efaa5f..374d439015 100644 --- a/ui/src/app/details-comp.js +++ b/ui/src/app/details-comp.js @@ -56,6 +56,10 @@ export class Details extends Component { if (params.id !== nextParams.id || params.subject !== nextParams.subject) { this.fetchBookDetails(nextParams); } + + if (nextParams.subject !== nextProps.context.subject) { + this.setState({ ...defaultState, ...{ renderDetails: false } }); + } } fetchBookDetails(params) { From a4a980efbe5cf26833cedfab89e8948b3ed8db0e Mon Sep 17 00:00:00 2001 From: viktornar Date: Wed, 26 Jul 2017 02:12:33 +0300 Subject: [PATCH 12/15] Now book details can be saved. --- ui/src/app/books-comp.js | 9 ++++- ui/src/app/constants.js | 4 ++ ui/src/app/details-comp.js | 77 +++++++++++++++++++++++++++++++++++--- 3 files changed, 82 insertions(+), 8 deletions(-) diff --git a/ui/src/app/books-comp.js b/ui/src/app/books-comp.js index 988089f064..673a0cb8a3 100644 --- a/ui/src/app/books-comp.js +++ b/ui/src/app/books-comp.js @@ -1,5 +1,5 @@ import React, { Component, PropTypes } from 'react'; -import { Tabs, Tab } from 'react-bootstrap'; +import { Tabs, Tab, Alert } from 'react-bootstrap'; import Book from './book-comp'; import { get } from './fetch-utils'; @@ -47,7 +47,12 @@ class Books extends Component { return (
- + {subjects.length >= 0 ? + : + +

Oh snap! You got an error!

+
+ }
); } diff --git a/ui/src/app/constants.js b/ui/src/app/constants.js index 1e30c69ab6..510f65a323 100644 --- a/ui/src/app/constants.js +++ b/ui/src/app/constants.js @@ -3,5 +3,9 @@ export const SUBJECTS_API_PATH = '/subjects'; export const BOOKS_API_PATH = '/books?subjects_like=:id'; export const BOOK_DETAIL_API_PATH = '/books?subjects_like=:subject&id=:id'; export const BOOK_DETAIL_UPDATE_API_PATH = '/books/:id'; +export const BOOK_DELETE_API_PATH = BOOK_DETAIL_UPDATE_API_PATH; +export const BOOK_DETAIL_CREATE_API_PATH = '/books'; export const BOOK_DETAIL_ROUTE_PATH = '/books/:subject/:id'; +export const BOOKS_ROUTE_PATH = '/books'; + diff --git a/ui/src/app/details-comp.js b/ui/src/app/details-comp.js index 374d439015..099a385d8f 100644 --- a/ui/src/app/details-comp.js +++ b/ui/src/app/details-comp.js @@ -7,18 +7,23 @@ import { FormControl, Button, Jumbotron, + ButtonToolbar, } from 'react-bootstrap'; import _ from 'underscore'; -import { get, put } from './fetch-utils'; +import { get, put, post, del } from './fetch-utils'; import { renderInputText, renderInputNumber } from './ui-utils'; import { BOOK_DETAIL_API_PATH, - BOOK_DETAIL_UPDATE_API_PATH, + BOOK_DETAIL_UPDATE_API_PATH, + BOOK_DETAIL_CREATE_API_PATH, + BOOK_DELETE_API_PATH, + BOOKS_ROUTE_PATH, } from './constants'; const bookDetailsPath = pathToRegexp.compile(BOOK_DETAIL_API_PATH); const bookUpdatePath = pathToRegexp.compile(BOOK_DETAIL_UPDATE_API_PATH); +const bookDeletePath = pathToRegexp.compile(BOOK_DELETE_API_PATH); const defaultState = { details: { @@ -103,7 +108,7 @@ export class Details extends Component { this.setState({ ...detailsToUpdate }); } - submitHandler(event) { + updateHandler(event) { event.preventDefault(); const { params } = this.props.match; const { details } = this.state; @@ -112,6 +117,41 @@ export class Details extends Component { .catch(() => {/* display ugly message */}); } + createHandler(event) { + event.preventDefault(); + const { history } = this.props; + const { details } = { ...this.state }; + delete details.id; + + post(BOOK_DETAIL_CREATE_API_PATH, details) + .then(() => { + const location = { + pathname: BOOKS_ROUTE_PATH, + context: { update: true }, + }; + + history.replace(location); + }) + .catch(() => {/* display ugly message */}); + } + + deleteHandler(event) { + event.preventDefault(); + const { params } = this.props.match; + const { history } = this.props; + const { details } = this.state; + del(bookDeletePath({ id: params.id }), details) + .then(() => { + const location = { + pathname: BOOKS_ROUTE_PATH, + context: { update: true }, + }; + + history.replace(location); + }) + .catch(() => {/* display ugly message */}); + } + addFormatHandler(event) { event.preventDefault(); let { details: detailsToUpdate } = this.state; @@ -145,6 +185,13 @@ export class Details extends Component { return (
+ {/* + Ugly form code. But as I remember it's + always difficult to work with forms in react. + I'm to much tired to start using react-redux with redux-form. + Flux architecture (redux) can help to seperate all logic, + but I don't want to investigate to much in this for such simple app. + */} {(details.id ? renderDetails : false) ? (
{ @@ -229,6 +276,10 @@ export class Details extends Component { Format key + {/* + Not very nice solution but don't want to play + with other libraries (redux-form, ...) + */} {this.formatKeyInput = ref;}} @@ -241,6 +292,9 @@ export class Details extends Component { Format value + {/* + The same as above. Ugly solution + */} {this.formatValueInput = ref;}} @@ -261,9 +315,20 @@ export class Details extends Component { - + + + + + From cc7cf56b78068322a75c1fd1e7372a833fedf02a Mon Sep 17 00:00:00 2001 From: viktornar Date: Wed, 26 Jul 2017 03:51:12 +0300 Subject: [PATCH 13/15] Removed unused packages. It will be greate to implement redux in this application, but I don't have time and I'm to much tired. Probably it would easer if I started work with redux from start :(. Fixed refrech bug (after refrech books list not refreshed) --- api.json | 240 +++++++++++++++++++------------------ package.json | 11 -- ui/config.js | 46 ------- ui/src/app/book-comp.js | 9 +- ui/src/app/details-comp.js | 8 +- 5 files changed, 140 insertions(+), 174 deletions(-) diff --git a/api.json b/api.json index 3df7e1b179..36ab9ef1ac 100644 --- a/api.json +++ b/api.json @@ -1,120 +1,132 @@ { - "subjects": ["Fiction", "Science"], + "subjects": [ + "Fiction", + "Science" + ], "books": [ - { - "id":1342, - "authors":[ - { - "birth_year":1775, - "death_year":1817, - "name":"Austen, Jane" - } - ], - "bookshelves":[ - "Best Books Ever Listings", - "Harvard Classics" - ], - "download_count":45668, - "formats":{ - "text/plain; charset=utf-8":"http://www.gutenberg.org/files/1342/1342-0.txt", - "application/pdf":"http://www.gutenberg.org/files/1342/1342-pdf.pdf", - "application/rdf+xml":"http://www.gutenberg.org/ebooks/1342.rdf", - "application/x-mobipocket-ebook":"http://www.gutenberg.org/ebooks/1342.kindle.noimages", - "application/epub+zip":"http://www.gutenberg.org/ebooks/1342.epub.images", - "text/plain; charset=us-ascii":"http://www.gutenberg.org/files/1342/1342.txt", - "text/html; charset=utf-8":"http://www.gutenberg.org/files/1342/1342-h/1342-h.htm" - }, - "languages":[ - "en" - ], - "media_type":"Text", - "subjects": ["Fiction"], - "title":"Pride and Prejudice" + { + "id": 1342, + "authors": [ + { + "birth_year": 1775, + "death_year": 1817, + "name": "Austenas" + } + ], + "bookshelves": [ + "Best Books Ever Listings", + "Harvard Classics" + ], + "download_count": 45668, + "formats": { + "text/plain; charset=utf-8": "http://www.gutenberg.org/files/1342/1342-0.txt", + "application/pdf": "http://www.gutenberg.org/files/1342/1342-pdf.pdf", + "application/rdf+xml": "http://www.gutenberg.org/ebooks/1342.rdf", + "application/x-mobipocket-ebook": "http://www.gutenberg.org/ebooks/1342.kindle.noimages", + "application/epub+zip": "http://www.gutenberg.org/ebooks/1342.epub.images", + "text/plain; charset=us-ascii": "http://www.gutenberg.org/files/1342/1342.txt", + "text/html; charset=utf-8": "http://www.gutenberg.org/files/1342/1342-h/1342-h.htm" }, - { - "id":33283, - "authors":[ - { - "birth_year":1851, - "death_year":1916, - "name":"Thompson, Silvanus P. (Silvanus Phillips)" - } - ], - "bookshelves":[ - "Mathematics" - ], - "download_count":24344, - "formats":{ - "application/prs.tex":"http://www.gutenberg.org/files/33283/33283-t.zip", - "application/rdf+xml":"http://www.gutenberg.org/ebooks/33283.rdf", - "application/pdf":"http://www.gutenberg.org/files/33283/33283-pdf.pdf" - }, - "languages":[ - "en" - ], - "media_type":"Text", - "subjects": ["Fiction"], - "title":"Calculus Made Easy: Being a very-simplest introduction to those beautiful methods which are generally called by the terrifying names of the Differential Calculus and the Integral Calculus" + "languages": [ + "en" + ], + "media_type": "Text", + "subjects": [ + "Fiction" + ], + "title": "Pride and Prejudice" + }, + { + "id": 33283, + "authors": [ + { + "birth_year": "1850", + "death_year": 1916, + "name": "Thompson, Silvanus P." + } + ], + "bookshelves": [ + "Mathematics" + ], + "download_count": 24344, + "formats": { + "application/prs.tex": "http://www.gutenberg.org/files/33283/33283-t.zip", + "application/rdf+xml": "http://www.gutenberg.org/ebooks/33283.rdf", + "application/pdf": "http://www.gutenberg.org/files/33283/33283-pdf.pdf" }, - { - "id":11, - "authors":[ - { - "birth_year":1832, - "death_year":1898, - "name":"Carroll, Lewis" - } - ], - "bookshelves":[ - "Children's Literature" - ], - "download_count":17866, - "formats":{ - "text/plain; charset=utf-8":"http://www.gutenberg.org/files/11/11-0.zip", - "application/rdf+xml":"http://www.gutenberg.org/ebooks/11.rdf", - "application/pdf":"http://www.gutenberg.org/files/11/11-pdf.pdf", - "application/x-mobipocket-ebook":"http://www.gutenberg.org/ebooks/11.kindle.noimages", - "application/epub+zip":"http://www.gutenberg.org/ebooks/11.epub.images", - "text/plain; charset=us-ascii":"http://www.gutenberg.org/files/11/11.zip", - "application/zip":"http://www.gutenberg.org/files/11/11-h.zip", - "text/html; charset=utf-8":"http://www.gutenberg.org/files/11/11-h/11-h.htm" - }, - "languages":[ - "en" - ], - "media_type":"Text", - "subjects": ["Fiction", "Science"], - "title":"Alice's Adventures in Wonderland" + "languages": [ + "en" + ], + "media_type": "Text", + "subjects": [ + "Fiction" + ], + "title": "Calculus Made Easy: Being a very-simplest introduction to those beautiful methods which are generally called by the terrifying names of the Differential Calculus and the Integral Calculus" + }, + { + "id": 74, + "authors": [ + { + "birth_year": 1835, + "death_year": 1910, + "name": "Twain, Mark" + } + ], + "bookshelves": [ + "Banned Books from Anne Haight's list" + ], + "download_count": 16204, + "formats": { + "image/jpeg": "http://www.gutenberg.org/cache/epub/74/pg74.cover.medium.jpg", + "text/plain; charset=utf-8": "http://www.gutenberg.org/files/74/74-0.zip", + "application/rdf+xml": "http://www.gutenberg.org/ebooks/74.rdf", + "application/x-mobipocket-ebook": "http://www.gutenberg.org/ebooks/74.kindle.images", + "application/epub+zip": "http://www.gutenberg.org/ebooks/74.epub.noimages", + "text/html; charset=utf-8": "http://www.gutenberg.org/files/74/74-h/74-h.htm", + "text/plain; charset=iso-8859-1": "http://www.gutenberg.org/files/74/74.txt", + "application/zip": "http://www.gutenberg.org/files/74/74.zip" }, - { - "id":74, - "authors":[ - { - "birth_year":1835, - "death_year":1910, - "name":"Twain, Mark" - } - ], - "bookshelves":[ - "Banned Books from Anne Haight's list" - ], - "download_count":16204, - "formats":{ - "image/jpeg":"http://www.gutenberg.org/cache/epub/74/pg74.cover.medium.jpg", - "text/plain; charset=utf-8":"http://www.gutenberg.org/files/74/74-0.zip", - "application/rdf+xml":"http://www.gutenberg.org/ebooks/74.rdf", - "application/x-mobipocket-ebook":"http://www.gutenberg.org/ebooks/74.kindle.images", - "application/epub+zip":"http://www.gutenberg.org/ebooks/74.epub.noimages", - "text/html; charset=utf-8":"http://www.gutenberg.org/files/74/74-h/74-h.htm", - "text/plain; charset=iso-8859-1":"http://www.gutenberg.org/files/74/74.txt", - "application/zip":"http://www.gutenberg.org/files/74/74.zip" - }, - "languages":[ - "en" - ], - "media_type":"Text", - "subjects": ["Science"], - "title":"The Adventures of Tom Sawyer" - } + "languages": [ + "en" + ], + "media_type": "Text", + "subjects": [ + "Science" + ], + "title": "The Adventures of Tom Sawyer" + }, + { + "authors": [ + { + "birth_year": 1832, + "death_year": 1898, + "name": "Carroll, Lew" + } + ], + "bookshelves": [ + "Children's Literature" + ], + "download_count": 17866, + "formats": { + "text/plain; charset=utf-8": "http://www.gutenberg.org/files/11/11-0.zip", + "application/rdf+xml": "http://www.gutenberg.org/ebooks/11.rdf", + "application/pdf": "http://www.gutenberg.org/files/11/11-pdf.pdf", + "application/x-mobipocket-ebook": "http://www.gutenberg.org/ebooks/11.kindle.noimages", + "application/epub+zip": "http://www.gutenberg.org/ebooks/11.epub.images", + "text/plain; charset=us-ascii": "http://www.gutenberg.org/files/11/11.zip", + "application/zip": "http://www.gutenberg.org/files/11/11-h.zip", + "text/html; charset=utf-8": "http://www.gutenberg.org/files/11/11-h/11-h.htm" + }, + "languages": [ + "en" + ], + "media_type": "Text", + "subjects": [ + "Fiction", + "Science" + ], + "title": "Alice's Adventures in Wonderland", + "id": 33288 + } ] -} +} \ No newline at end of file diff --git a/package.json b/package.json index e1522a4312..b20a30d22f 100644 --- a/package.json +++ b/package.json @@ -26,12 +26,8 @@ "react": "npm:react@^15.6.1", "react-bootstrap": "npm:react-bootstrap@^0.31.1", "react-dom": "npm:react-dom@^15.6.1", - "react-helmet": "npm:react-helmet@^5.1.3", - "react-intl": "npm:react-intl@^2.3.0", - "react-redux": "npm:react-redux@^5.0.5", "react-router": "npm:react-router@^4.1.2", "react-router-bootstrap": "npm:react-router-bootstrap@^0.24.2", - "react-router-config": "npm:react-router-config@^1.0.0-beta.3", "react-router-dom": "npm:react-router-dom@^4.1.2", "underscore": "npm:underscore@^1.8.3" }, @@ -39,13 +35,6 @@ "babel": "npm:babel-core@^5.8.24", "babel-runtime": "npm:babel-runtime@^5.8.24", "core-js": "npm:core-js@^1.1.4" - }, - "overrides": { - "npm:react-redux@5.0.5": { - "peerDependencies": { - "react": "15.6.1" - } - } } }, "devDependencies": { diff --git a/ui/config.js b/ui/config.js index 22ce510537..e46f2d49bc 100644 --- a/ui/config.js +++ b/ui/config.js @@ -24,12 +24,8 @@ System.config({ "react": "npm:react@15.6.1", "react-bootstrap": "npm:react-bootstrap@0.31.1", "react-dom": "npm:react-dom@15.6.1", - "react-helmet": "npm:react-helmet@5.1.3", - "react-intl": "npm:react-intl@2.3.0", - "react-redux": "npm:react-redux@5.0.5", "react-router": "npm:react-router@4.1.2", "react-router-bootstrap": "npm:react-router-bootstrap@0.24.2", - "react-router-config": "npm:react-router-config@1.0.0-beta.3", "react-router-dom": "npm:react-router-dom@4.1.2", "underscore": "npm:underscore@1.8.3", "github:floatdrop/plugin-jsx@1.2.1": { @@ -414,12 +410,6 @@ System.config({ "npm:inherits@2.0.3": { "util": "github:jspm/nodelibs-util@0.1.0" }, - "npm:intl-messageformat@1.3.0": { - "intl-messageformat-parser": "npm:intl-messageformat-parser@1.2.0" - }, - "npm:intl-relativeformat@1.3.0": { - "intl-messageformat": "npm:intl-messageformat@1.3.0" - }, "npm:invariant@2.2.2": { "loose-envify": "npm:loose-envify@1.3.1", "process": "github:jspm/nodelibs-process@0.1.2" @@ -573,23 +563,6 @@ System.config({ "prop-types": "npm:prop-types@15.5.10", "react": "npm:react@15.6.1" }, - "npm:react-helmet@5.1.3": { - "deep-equal": "npm:deep-equal@1.0.1", - "object-assign": "npm:object-assign@4.1.1", - "process": "github:jspm/nodelibs-process@0.1.2", - "prop-types": "npm:prop-types@15.5.10", - "react": "npm:react@15.6.1", - "react-side-effect": "npm:react-side-effect@1.1.3" - }, - "npm:react-intl@2.3.0": { - "intl-format-cache": "npm:intl-format-cache@2.0.5", - "intl-messageformat": "npm:intl-messageformat@1.3.0", - "intl-relativeformat": "npm:intl-relativeformat@1.3.0", - "invariant": "npm:invariant@2.2.2", - "process": "github:jspm/nodelibs-process@0.1.2", - "prop-types": "npm:prop-types@15.5.10", - "react": "npm:react@15.6.1" - }, "npm:react-overlays@0.7.0": { "classnames": "npm:classnames@2.2.5", "dom-helpers": "npm:dom-helpers@3.2.1", @@ -603,26 +576,11 @@ System.config({ "react": "npm:react@15.6.1", "warning": "npm:warning@3.0.0" }, - "npm:react-redux@5.0.5": { - "create-react-class": "npm:create-react-class@15.6.0", - "hoist-non-react-statics": "npm:hoist-non-react-statics@1.2.0", - "invariant": "npm:invariant@2.2.2", - "lodash": "npm:lodash@4.17.4", - "lodash-es": "npm:lodash-es@4.17.4", - "loose-envify": "npm:loose-envify@1.3.1", - "process": "github:jspm/nodelibs-process@0.1.2", - "prop-types": "npm:prop-types@15.5.10", - "react": "npm:react@15.6.1" - }, "npm:react-router-bootstrap@0.24.2": { "prop-types": "npm:prop-types@15.5.10", "react": "npm:react@15.6.1", "react-router-dom": "npm:react-router-dom@4.1.2" }, - "npm:react-router-config@1.0.0-beta.3": { - "react": "npm:react@15.6.1", - "react-router": "npm:react-router@4.1.2" - }, "npm:react-router-dom@4.1.2": { "history": "npm:history@4.6.3", "loose-envify": "npm:loose-envify@1.3.1", @@ -640,10 +598,6 @@ System.config({ "react": "npm:react@15.6.1", "warning": "npm:warning@3.0.0" }, - "npm:react-side-effect@1.1.3": { - "exenv": "npm:exenv@1.2.2", - "shallowequal": "npm:shallowequal@1.0.2" - }, "npm:react-tools@0.13.3": { "buffer": "github:jspm/nodelibs-buffer@0.1.1", "commoner": "npm:commoner@0.10.8", diff --git a/ui/src/app/book-comp.js b/ui/src/app/book-comp.js index b28988e2de..2796b2da37 100644 --- a/ui/src/app/book-comp.js +++ b/ui/src/app/book-comp.js @@ -46,9 +46,16 @@ export class Book extends Component { }); } + onDetailsChange() { + // Update component if books details info was changed + const { subject } = this.props; + this.fetchBooks(subject); + } + render() { const { routes, subject } = this.props; const { books } = this.state; + const onDetailsChange = this.onDetailsChange.bind(this); return ( @@ -72,7 +79,7 @@ export class Book extends Component { return ( ); })} diff --git a/ui/src/app/details-comp.js b/ui/src/app/details-comp.js index 099a385d8f..83f32c2158 100644 --- a/ui/src/app/details-comp.js +++ b/ui/src/app/details-comp.js @@ -119,7 +119,7 @@ export class Details extends Component { createHandler(event) { event.preventDefault(); - const { history } = this.props; + const { history, context: { onDetailsChange } } = this.props; const { details } = { ...this.state }; delete details.id; @@ -130,6 +130,8 @@ export class Details extends Component { context: { update: true }, }; + onDetailsChange(); + history.replace(location); }) .catch(() => {/* display ugly message */}); @@ -138,7 +140,7 @@ export class Details extends Component { deleteHandler(event) { event.preventDefault(); const { params } = this.props.match; - const { history } = this.props; + const { history, context: { onDetailsChange } } = this.props; const { details } = this.state; del(bookDeletePath({ id: params.id }), details) .then(() => { @@ -147,6 +149,8 @@ export class Details extends Component { context: { update: true }, }; + onDetailsChange(); + history.replace(location); }) .catch(() => {/* display ugly message */}); From 97feefa44a0c68e6f9ad73e39d5269f3b22584b4 Mon Sep 17 00:00:00 2001 From: viktornar Date: Wed, 26 Jul 2017 04:09:59 +0300 Subject: [PATCH 14/15] Finals commits. Still this application can and must be improved. Need to use flux architecture (redux), need to refactor code, need to implement error handling and so on. --- README.md | 69 ++++++++++------------------------------ ui/src/app/about-comp.js | 25 ++++++++++++++- 2 files changed, 40 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 5c84566eaa..020ff6eb8f 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,24 @@ -# Adapt React Coding Challenge +# My solution for `Adapt React Coding Challenge` +All steps was acomplished but not in nice way. There are much to be improvment. +Still this quiz was interesting for me and give me opportunity to remember how to work with react. -Seems like you're trying out for a position at -[Adapt](https://adapt.dk/en) or you've found this and would like to -apply. Fork this repo and go at it ;) +What should be done (what kind of improvement must be done): -Your goal is to set up a React application, where users will be able to -edit book info (and create new books, if you have enough time to -implement it). Once you are done with the challenge, please fire up a -Pull Request and we will get in touch. +* Decouple code by using flux architecture. Such pattern should seperate view from logic +* Implement error handling in case if api server will not respond +* Refactor code +* and much more ... :( -## Brief +# How to run -I am a user of the app and I want to create a book object and edit -previously provided info about it, so that my reading list can stay up -to date. The form should be split into three steps: +I'm usin SystemJS for building jspm for dependency managament and live-server for serving application -1. Choose subject (one of the two). -2. Depending on the selection in the first step, display a list of - reading material. Choose one. -3. When reading material chosen, display all the info that's available - about the book in a form (meaning that the book info can be edited). +First of all install dependencies: -## Requirements +* npm install && npm i --dev +* jspm install && jspm i --dev -* All steps should be visible on the screen and changable at all times - (when they are available -- step 1 when nothing is picked, step 1 - and 2 when step 1 is picked and step 1, 2 and 3 when step 2 is - picked). - -* You can use whatever libraries, task runners and build processes you - like. React and plain JavaScript are the only requirements (ES6 - encouraged, but no TypeScript, CoffeeScript, etc). Redux is strongly - encouraged if you see a need for it. - -### Suggested order of completion - -This depends on how much time you were given to accomplish the task. -Ideally you would provide a solution for each of the outlined steps -unless they are marked as optional. - -1. Data fetching from the api. -2. Form steps logic. -3. (optional) Saving the data. -4. (optional) Styling (minor for a 2-3 h challenge, more if there's more time). - -## API Usage - -API can be launched using `npm start`. You will need to run `npm -install` once starting working on the project to install dependencies. - -| Endpoint | Result | -|------------------------------|-----------------------------------------------------| -| /books?subjects_like=Fiction | Lists all books that contain "Fiction" as a subject | -| /subjects | Lists all available subjects | - ---- - -More info about API usage can be found at the [json-server -repo](https://github.com/typicode/json-server). +Then run application: +* npm run start-api +* npm run start-ui diff --git a/ui/src/app/about-comp.js b/ui/src/app/about-comp.js index e4fea0b43a..bf7c328fd9 100644 --- a/ui/src/app/about-comp.js +++ b/ui/src/app/about-comp.js @@ -2,7 +2,30 @@ import React from 'react'; const About = () => { - return (

Simple page application for getting info about books.

); + return ( +
+

Simple page application for getting info about books.

+

+ What was used for creating this application: +

    +
  • react
  • +
  • react-router
  • +
  • react-bootstrap
  • +
  • path-to-regexp
  • +
  • isomorphic-fetch
  • +
  • ...
  • +
+ The intemtion of this application was to remember how to work with react.
+ What would be nice to accomplish: +
    +
  • Decouple code by using flux architecture. Such pattern should seperate view from logic
  • +
  • Implement error handling in case if api server will not respond
  • +
  • Refactor code
  • +
  • and much more ... :(
  • +
+

+
+ ); }; export default About; From 02485d20e95af8a3dbc8cd41f00030943b43a78c Mon Sep 17 00:00:00 2001 From: viktornar Date: Wed, 26 Jul 2017 04:27:30 +0300 Subject: [PATCH 15/15] Small code refactoring before pull request. --- api.json | 86 ++++++------------- ui/src/app/about-comp.js | 30 ++++--- ui/src/app/book-comp.js | 1 + ui/src/app/config.js | 4 +- ui/src/app/constants.js | 1 - ui/src/app/fetch-utils.js | 18 ---- ui/src/app/{books-comp.js => subject-comp.js} | 4 +- 7 files changed, 48 insertions(+), 96 deletions(-) rename ui/src/app/{books-comp.js => subject-comp.js} (95%) diff --git a/api.json b/api.json index 36ab9ef1ac..1c43e4588f 100644 --- a/api.json +++ b/api.json @@ -36,65 +36,6 @@ ], "title": "Pride and Prejudice" }, - { - "id": 33283, - "authors": [ - { - "birth_year": "1850", - "death_year": 1916, - "name": "Thompson, Silvanus P." - } - ], - "bookshelves": [ - "Mathematics" - ], - "download_count": 24344, - "formats": { - "application/prs.tex": "http://www.gutenberg.org/files/33283/33283-t.zip", - "application/rdf+xml": "http://www.gutenberg.org/ebooks/33283.rdf", - "application/pdf": "http://www.gutenberg.org/files/33283/33283-pdf.pdf" - }, - "languages": [ - "en" - ], - "media_type": "Text", - "subjects": [ - "Fiction" - ], - "title": "Calculus Made Easy: Being a very-simplest introduction to those beautiful methods which are generally called by the terrifying names of the Differential Calculus and the Integral Calculus" - }, - { - "id": 74, - "authors": [ - { - "birth_year": 1835, - "death_year": 1910, - "name": "Twain, Mark" - } - ], - "bookshelves": [ - "Banned Books from Anne Haight's list" - ], - "download_count": 16204, - "formats": { - "image/jpeg": "http://www.gutenberg.org/cache/epub/74/pg74.cover.medium.jpg", - "text/plain; charset=utf-8": "http://www.gutenberg.org/files/74/74-0.zip", - "application/rdf+xml": "http://www.gutenberg.org/ebooks/74.rdf", - "application/x-mobipocket-ebook": "http://www.gutenberg.org/ebooks/74.kindle.images", - "application/epub+zip": "http://www.gutenberg.org/ebooks/74.epub.noimages", - "text/html; charset=utf-8": "http://www.gutenberg.org/files/74/74-h/74-h.htm", - "text/plain; charset=iso-8859-1": "http://www.gutenberg.org/files/74/74.txt", - "application/zip": "http://www.gutenberg.org/files/74/74.zip" - }, - "languages": [ - "en" - ], - "media_type": "Text", - "subjects": [ - "Science" - ], - "title": "The Adventures of Tom Sawyer" - }, { "authors": [ { @@ -127,6 +68,33 @@ ], "title": "Alice's Adventures in Wonderland", "id": 33288 + }, + { + "authors": [ + { + "birth_year": "1850", + "death_year": 1916, + "name": "Thompson, Silvanus P." + } + ], + "bookshelves": [ + "Mathematics" + ], + "download_count": 24344, + "formats": { + "application/prs.tex": "http://www.gutenberg.org/files/33283/33283-t.zip", + "application/rdf+xml": "http://www.gutenberg.org/ebooks/33283.rdf", + "application/pdf": "http://www.gutenberg.org/files/33283/33283-pdf.pdf" + }, + "languages": [ + "en" + ], + "media_type": "Text", + "subjects": [ + "Fiction" + ], + "title": "Calculus Made Easy: Being a very-simplest introduction to those beautiful methods which are generally called by the terrifying names of the Differential Calculus and the Integral Calculus", + "id": 33289 } ] } \ No newline at end of file diff --git a/ui/src/app/about-comp.js b/ui/src/app/about-comp.js index bf7c328fd9..3f3172b68c 100644 --- a/ui/src/app/about-comp.js +++ b/ui/src/app/about-comp.js @@ -7,23 +7,25 @@ const About = () => {

Simple page application for getting info about books.

What was used for creating this application: -

    -
  • react
  • -
  • react-router
  • -
  • react-bootstrap
  • -
  • path-to-regexp
  • -
  • isomorphic-fetch
  • -
  • ...
  • -
+

+
    +
  • react
  • +
  • react-router
  • +
  • react-bootstrap
  • +
  • path-to-regexp
  • +
  • isomorphic-fetch
  • +
  • ...
  • +
+

The intemtion of this application was to remember how to work with react.
What would be nice to accomplish: -

    -
  • Decouple code by using flux architecture. Such pattern should seperate view from logic
  • -
  • Implement error handling in case if api server will not respond
  • -
  • Refactor code
  • -
  • and much more ... :(
  • -

+
    +
  • Decouple code by using flux architecture. Such pattern should seperate view from logic
  • +
  • Implement error handling in case if api server will not respond
  • +
  • Refactor code
  • +
  • and much more ... :(
  • +
); }; diff --git a/ui/src/app/book-comp.js b/ui/src/app/book-comp.js index 2796b2da37..a910c95ff0 100644 --- a/ui/src/app/book-comp.js +++ b/ui/src/app/book-comp.js @@ -10,6 +10,7 @@ import { BOOK_DETAIL_ROUTE_PATH, } from './constants'; + const bookPath = pathToRegexp.compile(BOOKS_API_PATH); const bookDetailPath = pathToRegexp.compile(BOOK_DETAIL_ROUTE_PATH); diff --git a/ui/src/app/config.js b/ui/src/app/config.js index 29c21967eb..c1a2ccba20 100644 --- a/ui/src/app/config.js +++ b/ui/src/app/config.js @@ -2,7 +2,7 @@ import React from 'react'; import { RootRedirecter } from './route-utils'; import About from './about-comp'; -import Books from './books-comp'; +import Subject from './subject-comp'; import Details from './details-comp'; @@ -15,7 +15,7 @@ export const routes = [ { path: '/books', exact: true, - component: Books, + component: Subject, routes: [ { path: '/books/:subject/:id', diff --git a/ui/src/app/constants.js b/ui/src/app/constants.js index 510f65a323..2958de1c07 100644 --- a/ui/src/app/constants.js +++ b/ui/src/app/constants.js @@ -8,4 +8,3 @@ export const BOOK_DETAIL_CREATE_API_PATH = '/books'; export const BOOK_DETAIL_ROUTE_PATH = '/books/:subject/:id'; export const BOOKS_ROUTE_PATH = '/books'; - diff --git a/ui/src/app/fetch-utils.js b/ui/src/app/fetch-utils.js index 587d6b5bb4..4b7d9df08c 100644 --- a/ui/src/app/fetch-utils.js +++ b/ui/src/app/fetch-utils.js @@ -112,21 +112,3 @@ export function del(url) { method: 'delete', }); } - -export function fixedEncodeURIComponent(str) { - return encodeURIComponent(str).replace(/[!'()*]/g, (c) => { - return `%${c.charCodeAt(0).toString(16)}`; - }); -} - -export const createQueryUrl = (obj = {}) => { - const createItem = (k) => `${fixedEncodeURIComponent(k)}=${fixedEncodeURIComponent(obj[k])}`; - - return Object.keys(obj).map(createItem).join('&'); -}; - -export const createQueryWithAmpPrefix = (obj) => { - const queryUrl = createQueryUrl(obj); - - return queryUrl ? `&${queryUrl}` : ''; -}; \ No newline at end of file diff --git a/ui/src/app/books-comp.js b/ui/src/app/subject-comp.js similarity index 95% rename from ui/src/app/books-comp.js rename to ui/src/app/subject-comp.js index 673a0cb8a3..2daec51da5 100644 --- a/ui/src/app/books-comp.js +++ b/ui/src/app/subject-comp.js @@ -29,7 +29,7 @@ BookTabs.propTypes = { routes: PropTypes.array.isRequired, }; -class Books extends Component { +class Subject extends Component { constructor(props) { super(props); this.state = { subjects: [] }; @@ -58,4 +58,4 @@ class Books extends Component { } } -export default Books; \ No newline at end of file +export default Subject; \ No newline at end of file