├── .gitignore ├── Makefile ├── README.md ├── add_constraint_neo4j.sh ├── adoc ├── about_node.adoc ├── img │ ├── image00_empty_profile.png │ ├── image00_model.png │ ├── image01_api_directories.png │ ├── image01_rating_example.png │ ├── image02.png │ ├── image02_user_graph.png │ ├── image03_create_account.png │ ├── image03_person_detail.png │ ├── image04_homepage.png │ ├── image04_my_rated_movie.png │ ├── image05_example_rating_graph.png │ ├── image05_web_directories.png │ ├── image06_profile_with_ratings.png │ ├── image06_swagger_genres.png │ ├── image07_login.png │ ├── image07_movie_detail.png │ └── user_ratings_graph.svg └── user_responses.adoc ├── api ├── .env ├── .nvmrc ├── LICENSE ├── Procfile ├── app.js ├── config.js ├── helpers │ └── response.js ├── middlewares │ ├── loginRequired.js │ ├── neo4jSessionCleanup.js │ └── setAuthUser.js ├── models │ ├── genres.js │ ├── movies.js │ ├── neo4j │ │ ├── genre.js │ │ ├── movie.js │ │ ├── person.js │ │ └── user.js │ ├── people.js │ └── users.js ├── neo4j │ └── dbUtils.js ├── package-lock.json ├── package.json ├── routes │ ├── genres.js │ ├── index.js │ ├── movies.js │ ├── people.js │ ├── site.js │ └── users.js ├── swaggerui │ ├── css │ │ ├── print.css │ │ ├── reset.css │ │ ├── screen.css │ │ ├── style.css │ │ └── typography.css │ ├── fonts │ │ ├── DroidSans-Bold.ttf │ │ └── DroidSans.ttf │ ├── images │ │ ├── collapse.gif │ │ ├── expand.gif │ │ ├── explorer_icons.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── logo_small.png │ │ ├── pet_store_api.png │ │ ├── throbber.gif │ │ └── wordnik_api.png │ ├── index.html │ ├── lang │ │ ├── ca.js │ │ ├── en.js │ │ ├── es.js │ │ ├── fr.js │ │ ├── geo.js │ │ ├── it.js │ │ ├── ja.js │ │ ├── ko-kr.js │ │ ├── pl.js │ │ ├── pt.js │ │ ├── ru.js │ │ ├── tr.js │ │ ├── translator.js │ │ └── zh-cn.js │ ├── lib │ │ ├── backbone-min.js │ │ ├── es5-shim.js │ │ ├── handlebars-4.0.5.js │ │ ├── highlight.9.1.0.pack.js │ │ ├── highlight.9.1.0.pack_extended.js │ │ ├── jquery-1.8.0.min.js │ │ ├── jquery.ba-bbq.min.js │ │ ├── jquery.slideto.min.js │ │ ├── jquery.wiggle.min.js │ │ ├── js-yaml.min.js │ │ ├── jsoneditor.min.js │ │ ├── lodash.min.js │ │ ├── marked.js │ │ ├── object-assign-pollyfill.js │ │ ├── sanitize-html.min.js │ │ └── swagger-oauth.js │ ├── o2c.html │ ├── swagger-ui.js │ └── swagger-ui.min.js └── views │ ├── img │ └── neo4j-swagger.jpg │ ├── index.jade │ ├── layout.jade │ ├── user.jade │ └── users.jade ├── flask-api ├── .env ├── .gitignore ├── Procfile ├── __init__.py ├── app.py ├── requirements.txt └── swaggerui │ ├── css │ ├── print.css │ ├── reset.css │ ├── screen.css │ ├── style.css │ └── typography.css │ ├── fonts │ ├── DroidSans-Bold.ttf │ └── DroidSans.ttf │ ├── images │ ├── collapse.gif │ ├── expand.gif │ ├── explorer_icons.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── logo_small.png │ ├── pet_store_api.png │ ├── throbber.gif │ └── wordnik_api.png │ ├── index.html │ ├── lang │ ├── ca.js │ ├── en.js │ ├── es.js │ ├── fr.js │ ├── geo.js │ ├── it.js │ ├── ja.js │ ├── ko-kr.js │ ├── pl.js │ ├── pt.js │ ├── ru.js │ ├── tr.js │ ├── translator.js │ └── zh-cn.js │ ├── lib │ ├── backbone-min.js │ ├── es5-shim.js │ ├── handlebars-4.0.5.js │ ├── highlight.9.1.0.pack.js │ ├── highlight.9.1.0.pack_extended.js │ ├── jquery-1.8.0.min.js │ ├── jquery.ba-bbq.min.js │ ├── jquery.slideto.min.js │ ├── jquery.wiggle.min.js │ ├── js-yaml.min.js │ ├── jsoneditor.min.js │ ├── lodash.min.js │ ├── marked.js │ ├── object-assign-pollyfill.js │ └── swagger-oauth.js │ ├── o2c.html │ ├── swagger-ui.js │ └── swagger-ui.min.js ├── img ├── model.png ├── model.svg ├── neo4jBrowser_CypherCmd.png ├── ratings_csv.png ├── verifyCloudAtlasMovie.png └── webUX.png ├── import_neo4j.sh ├── setup.cql ├── setupCypherCommands.txt └── web ├── .env ├── .gitignore ├── .nvmrc ├── app.json ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── UserSession.js ├── api │ ├── AuthApi.js │ ├── MoviesApi.js │ ├── PersonApi.js │ ├── ProfileApi.js │ └── axios.js ├── assets │ ├── favicon.ico │ ├── logo.png │ └── neo4j_background3.gif ├── components │ ├── Breadcrumbs.jsx │ ├── Carousel.jsx │ ├── Footer.jsx │ ├── Header.jsx │ ├── Loading.jsx │ ├── UserRating.jsx │ ├── common │ │ ├── Notification.jsx │ │ └── NotificationContainer.jsx │ └── validation │ │ ├── InputValidator.jsx │ │ └── ValidatedComponent.jsx ├── config │ ├── settings.example.js │ └── settings.js ├── index.js ├── pages │ ├── App.jsx │ ├── AuthenticatedPage.jsx │ ├── Home.jsx │ ├── Login.jsx │ ├── Movie.jsx │ ├── Person.jsx │ ├── Profile.jsx │ ├── Signup.jsx │ ├── SignupStatus.jsx │ └── index.hbs ├── redux │ ├── actions │ │ ├── AuthActionTypes.js │ │ ├── AuthActions.js │ │ ├── MovieActionTypes.js │ │ ├── MovieActions.js │ │ ├── NotificationActionTypes.js │ │ ├── NotificationActions.js │ │ ├── PersonActionTypes.js │ │ ├── PersonActions.js │ │ ├── ProfileActionTypes.js │ │ └── ProfileActions.js │ ├── reducers │ │ ├── auth.js │ │ ├── genres.js │ │ ├── index.js │ │ ├── movies.js │ │ ├── notifications.js │ │ ├── person.js │ │ ├── profile.js │ │ └── signup.js │ └── sagas │ │ ├── authFlow.js │ │ ├── errorFlow.js │ │ ├── index.js │ │ ├── movieFlow.js │ │ ├── personFlow.js │ │ ├── profileFlow.js │ │ └── signupFlow.js ├── routes │ └── Routes.jsx ├── setupTests.js ├── styles │ ├── _variables.scss │ ├── components │ │ ├── _breadcrumbs.scss │ │ ├── _buttonLink.scss │ │ ├── _carousel.scss │ │ ├── _footer.scss │ │ ├── _header.scss │ │ ├── _loading.scss │ │ ├── _notificationContainer.scss │ │ ├── _userRating.scss │ │ └── _validation.scss │ ├── foundation │ │ ├── .bower.json │ │ ├── LICENSE │ │ ├── README.md │ │ └── scss │ │ │ ├── foundation.scss │ │ │ ├── foundation │ │ │ ├── _functions.scss │ │ │ ├── _settings.scss │ │ │ └── components │ │ │ │ ├── _accordion.scss │ │ │ │ ├── _alert-boxes.scss │ │ │ │ ├── _block-grid.scss │ │ │ │ ├── _breadcrumbs.scss │ │ │ │ ├── _button-groups.scss │ │ │ │ ├── _buttons.scss │ │ │ │ ├── _clearing.scss │ │ │ │ ├── _dropdown-buttons.scss │ │ │ │ ├── _dropdown.scss │ │ │ │ ├── _flex-video.scss │ │ │ │ ├── _forms.scss │ │ │ │ ├── _global.scss │ │ │ │ ├── _grid.scss │ │ │ │ ├── _icon-bar.scss │ │ │ │ ├── _inline-lists.scss │ │ │ │ ├── _joyride.scss │ │ │ │ ├── _keystrokes.scss │ │ │ │ ├── _labels.scss │ │ │ │ ├── _magellan.scss │ │ │ │ ├── _offcanvas.scss │ │ │ │ ├── _orbit.scss │ │ │ │ ├── _pagination.scss │ │ │ │ ├── _panels.scss │ │ │ │ ├── _pricing-tables.scss │ │ │ │ ├── _progress-bars.scss │ │ │ │ ├── _range-slider.scss │ │ │ │ ├── _reveal.scss │ │ │ │ ├── _side-nav.scss │ │ │ │ ├── _split-buttons.scss │ │ │ │ ├── _sub-nav.scss │ │ │ │ ├── _switches.scss │ │ │ │ ├── _tables.scss │ │ │ │ ├── _tabs.scss │ │ │ │ ├── _thumbs.scss │ │ │ │ ├── _tooltips.scss │ │ │ │ ├── _top-bar.scss │ │ │ │ ├── _type.scss │ │ │ │ └── _visibility.scss │ │ │ └── normalize.scss │ ├── main.scss │ └── views │ │ ├── _home.scss │ │ ├── _movie.scss │ │ ├── _person.scss │ │ └── _profile.scss └── utils │ ├── ErrorUtils.js │ └── ValidationUtils.js └── static.json /.gitignore: -------------------------------------------------------------------------------- 1 | import.report 2 | .DS_Store 3 | web/build 4 | web/bower_components 5 | web/node_modules 6 | web/package-lock.json 7 | 8 | api/node_modules 9 | api/config/settings.json 10 | *.pyc 11 | flask-api/venv 12 | 13 | database/* 14 | 15 | ### Vim template 16 | [._]*.s[a-w][a-z] 17 | [._]s[a-w][a-z] 18 | *.un~ 19 | Session.vim 20 | .netrwhist 21 | *~ 22 | ### JetBrains template 23 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 24 | 25 | *.iml 26 | 27 | ## Directory-based project format: 28 | .idea 29 | # if you remove the above rule, at least ignore the following: 30 | 31 | # User-specific stuff: 32 | # .idea/workspace.xml 33 | # .idea/tasks.xml 34 | # .idea/dictionaries 35 | 36 | # Sensitive or high-churn files: 37 | # .idea/dataSources.ids 38 | # .idea/dataSources.xml 39 | # .idea/sqlDataSources.xml 40 | # .idea/dynamic.xml 41 | # .idea/uiDesigner.xml 42 | 43 | # Gradle: 44 | # .idea/gradle.xml 45 | # .idea/libraries 46 | 47 | # Mongo Explorer plugin: 48 | # .idea/mongoSettings.xml 49 | 50 | ## File-based project format: 51 | *.ipr 52 | *.iws 53 | 54 | ## Plugin-specific files: 55 | 56 | # IntelliJ 57 | /out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Crashlytics plugin (for Android Studio and IntelliJ) 66 | com_crashlytics_export_strings.xml 67 | crashlytics.properties 68 | crashlytics-build.properties 69 | ### SublimeText template 70 | # cache files for sublime text 71 | *.tmlanguage.cache 72 | *.tmPreferences.cache 73 | *.stTheme.cache 74 | 75 | # workspace files are user-specific 76 | *.sublime-workspace 77 | 78 | # project files should be checked into the repository, unless a significant 79 | # proportion of contributors will probably not be using SublimeText 80 | # *.sublime-project 81 | 82 | # sftp configuration file 83 | sftp-config.json 84 | 85 | # Created by .ignore support plugin (hsz.mobi) 86 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | deploy-api: 2 | git branch -f heroku-api 3 | git branch -D heroku-api 4 | git subtree split --prefix flask-api -b heroku-api 5 | git push heroku-api heroku-api:master --force 6 | 7 | deploy-web: 8 | git branch -f heroku-web 9 | git branch -D heroku-web 10 | git subtree split --prefix web -b heroku-web 11 | git push heroku-web heroku-web:master --force 12 | -------------------------------------------------------------------------------- /add_constraint_neo4j.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | $NEO4J_HOME/bin/neo4j-shell < setup.cql -------------------------------------------------------------------------------- /adoc/img/image00_empty_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image00_empty_profile.png -------------------------------------------------------------------------------- /adoc/img/image00_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image00_model.png -------------------------------------------------------------------------------- /adoc/img/image01_api_directories.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image01_api_directories.png -------------------------------------------------------------------------------- /adoc/img/image01_rating_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image01_rating_example.png -------------------------------------------------------------------------------- /adoc/img/image02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image02.png -------------------------------------------------------------------------------- /adoc/img/image02_user_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image02_user_graph.png -------------------------------------------------------------------------------- /adoc/img/image03_create_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image03_create_account.png -------------------------------------------------------------------------------- /adoc/img/image03_person_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image03_person_detail.png -------------------------------------------------------------------------------- /adoc/img/image04_homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image04_homepage.png -------------------------------------------------------------------------------- /adoc/img/image04_my_rated_movie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image04_my_rated_movie.png -------------------------------------------------------------------------------- /adoc/img/image05_example_rating_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image05_example_rating_graph.png -------------------------------------------------------------------------------- /adoc/img/image05_web_directories.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image05_web_directories.png -------------------------------------------------------------------------------- /adoc/img/image06_profile_with_ratings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image06_profile_with_ratings.png -------------------------------------------------------------------------------- /adoc/img/image06_swagger_genres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image06_swagger_genres.png -------------------------------------------------------------------------------- /adoc/img/image07_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image07_login.png -------------------------------------------------------------------------------- /adoc/img/image07_movie_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/adoc/img/image07_movie_detail.png -------------------------------------------------------------------------------- /api/.env: -------------------------------------------------------------------------------- 1 | SECRET_KEY="super secret guy" 2 | MOVIE_DATABASE_USERNAME="neo4j" 3 | MOVIE_DATABASE_PASSWORD="height-restrictions-calculators" 4 | MOVIE_DATABASE_URL="bolt://52.91.202.103:32814" 5 | -------------------------------------------------------------------------------- /api/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/erbium 2 | -------------------------------------------------------------------------------- /api/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 tinj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /api/Procfile: -------------------------------------------------------------------------------- 1 | web: node app.js -------------------------------------------------------------------------------- /api/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('dotenv').config(); 4 | 5 | var nconf = require('nconf'); 6 | 7 | nconf.env(['PORT', 'NODE_ENV']) 8 | .argv({ 9 | 'e': { 10 | alias: 'NODE_ENV', 11 | describe: 'Set production or development mode.', 12 | demand: false, 13 | default: 'development' 14 | }, 15 | 'p': { 16 | alias: 'PORT', 17 | describe: 'Port to run on.', 18 | demand: false, 19 | default: 3000 20 | }, 21 | 'n': { 22 | alias: "neo4j", 23 | describe: "Use local or remote neo4j instance", 24 | demand: false, 25 | default: "local" 26 | } 27 | }) 28 | .defaults({ 29 | 'USERNAME': process.env.MOVIE_DATABASE_USERNAME, 30 | 'PASSWORD' : process.env.MOVIE_DATABASE_PASSWORD, 31 | 'neo4j': 'local', 32 | 'neo4j-local': process.env.MOVIE_DATABASE_URL || 'bolt://localhost:7687', 33 | 'base_url': 'http://localhost:3000', 34 | 'api_path': '/api/v0' 35 | }); 36 | 37 | module.exports = nconf; 38 | -------------------------------------------------------------------------------- /api/helpers/response.js: -------------------------------------------------------------------------------- 1 | // var sw = require("swagger-node-express"); 2 | var _ = require("lodash"); 3 | 4 | exports.writeResponse = function writeResponse(res, response, status) { 5 | // sw.setHeaders(res); 6 | res.status(status || 200).send(JSON.stringify(response)); 7 | }; 8 | 9 | exports.writeError = function writeError(res, error, status) { 10 | // sw.setHeaders(res); 11 | res 12 | .status(error.status || status || 400) 13 | .send(JSON.stringify(_.omit(error, ["status"]))); 14 | }; 15 | 16 | -------------------------------------------------------------------------------- /api/middlewares/loginRequired.js: -------------------------------------------------------------------------------- 1 | var writeError = require('../helpers/response').writeResponse; 2 | 3 | module.exports = function loginRequired(req, res, next) { 4 | var authHeader = req.headers['authorization']; 5 | if (!authHeader) { 6 | return writeError(res, {detail: 'no authorization provided'}, 401); 7 | } 8 | next(); 9 | }; 10 | -------------------------------------------------------------------------------- /api/middlewares/neo4jSessionCleanup.js: -------------------------------------------------------------------------------- 1 | module.exports = function neo4jSessionCleanup(req, res, next) { 2 | res.on('finish', function () { 3 | if(req.neo4jSession) { 4 | req.neo4jSession.close(); 5 | delete req.neo4jSession; 6 | } 7 | }); 8 | next(); 9 | }; -------------------------------------------------------------------------------- /api/middlewares/setAuthUser.js: -------------------------------------------------------------------------------- 1 | var writeError = require('../helpers/response').writeError; 2 | var Users = require('../models/users'); 3 | var dbUtils = require('../neo4j/dbUtils'); 4 | 5 | module.exports = function setAuthUser(req, res, next) { 6 | var authHeader = req.headers['authorization']; 7 | if (!authHeader) { 8 | req.user = {id: null}; 9 | next(); 10 | } 11 | else { 12 | var match = authHeader.match(/^Token (\S+)/); 13 | if (!match || !match[1]) { 14 | return writeError(res, {detail: 'invalid authorization format. Follow `Token `'}, 401); 15 | } 16 | var token = match[1]; 17 | 18 | Users.me(dbUtils.getSession(req), token) 19 | .then(user => { 20 | req.user = user; 21 | next(); 22 | }) 23 | .catch(next); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /api/models/genres.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Genre = require('../models/neo4j/genre'); 3 | 4 | const getAll = function(session) { 5 | return session.readTransaction(txc => 6 | txc.run('MATCH (genre:Genre) RETURN genre') 7 | ).then(_manyGenres); 8 | }; 9 | 10 | const _manyGenres = function (result) { 11 | return result.records.map(r => new Genre(r.get('genre'))); 12 | }; 13 | 14 | module.exports = { 15 | getAll: getAll 16 | }; 17 | -------------------------------------------------------------------------------- /api/models/neo4j/genre.js: -------------------------------------------------------------------------------- 1 | // extracts just the data from the query results 2 | 3 | const _ = require('lodash'); 4 | 5 | const Genre = module.exports = function (_node) { 6 | _.extend(this, _node.properties); 7 | if (this.id) { 8 | this.id = this.id.toNumber(); 9 | } else { 10 | this.id = _node.identity.low; 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /api/models/neo4j/movie.js: -------------------------------------------------------------------------------- 1 | // extracts just the data from the query results 2 | 3 | const _ = require('lodash'); 4 | 5 | const Movie = module.exports = function (_node, myRating) { 6 | _.extend(this, _node.properties); 7 | 8 | this.id = this.tmdbId; 9 | this.poster_image = this.poster; 10 | this.tagline = this.plot; 11 | 12 | if (this.duration) { 13 | this.duration = this.duration.toNumber(); 14 | } else if (this.runtime) { 15 | this.duration = this.runtime.low; 16 | } 17 | 18 | if(myRating || myRating === 0) { 19 | this['my_rating'] = myRating; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /api/models/neo4j/person.js: -------------------------------------------------------------------------------- 1 | // extracts just the data from the query results 2 | 3 | const _ = require('lodash'); 4 | 5 | const Person = module.exports = function (_node) { 6 | _.extend(this, _node.properties); 7 | this.id = this.tmdbId; 8 | this.poster_image = this.poster; 9 | }; 10 | -------------------------------------------------------------------------------- /api/models/neo4j/user.js: -------------------------------------------------------------------------------- 1 | // extracts just the data from the query results 2 | 3 | const _ = require('lodash'); 4 | const md5 = require('md5'); 5 | 6 | const User = module.exports = function (_node) { 7 | const username = _node.properties['username']; 8 | 9 | _.extend(this, { 10 | 'id': _node.properties['id'], 11 | 'username': username, 12 | 'avatar': { 13 | 'full_size': 'https://www.gravatar.com/avatar/' + md5(username) + '?d=retro' 14 | } 15 | }); 16 | }; -------------------------------------------------------------------------------- /api/models/users.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const uuid = require('node-uuid'); 4 | const randomstring = require("randomstring"); 5 | const _ = require('lodash'); 6 | const dbUtils = require('../neo4j/dbUtils'); 7 | const User = require('../models/neo4j/user'); 8 | const crypto = require('crypto'); 9 | 10 | const register = function (session, username, password) { 11 | return session.readTransaction(txc => txc.run('MATCH (user:User {username: $username}) RETURN user', {username: username})) 12 | .then(results => { 13 | if (!_.isEmpty(results.records)) { 14 | throw {username: 'username already in use', status: 400} 15 | } 16 | else { 17 | return session.writeTransaction(txc => txc.run('CREATE (user:User {id: $id, username: $username, password: $password, api_key: $api_key}) RETURN user', 18 | { 19 | id: uuid.v4(), 20 | username: username, 21 | password: hashPassword(username, password), 22 | api_key: randomstring.generate({ 23 | length: 20, 24 | charset: 'hex' 25 | }) 26 | } 27 | )).then(results => { 28 | return new User(results.records[0].get('user')); 29 | } 30 | ) 31 | } 32 | }); 33 | }; 34 | 35 | const me = function (session, apiKey) { 36 | return session.readTransaction(txc => txc.run('MATCH (user:User {api_key: $api_key}) RETURN user', {api_key: apiKey})) 37 | .then(results => { 38 | if (_.isEmpty(results.records)) { 39 | throw {message: 'invalid authorization key', status: 401}; 40 | } 41 | return new User(results.records[0].get('user')); 42 | }); 43 | }; 44 | 45 | const login = function (session, username, password) { 46 | return session.readTransaction(txc => txc.run('MATCH (user:User {username: $username}) RETURN user', {username: username})) 47 | .then(results => { 48 | if (_.isEmpty(results.records)) { 49 | throw {username: 'username does not exist', status: 400} 50 | } 51 | else { 52 | const dbUser = _.get(results.records[0].get('user'), 'properties'); 53 | if (dbUser.password != hashPassword(username, password)) { 54 | throw {password: 'wrong password', status: 400} 55 | } 56 | return {token: _.get(dbUser, 'api_key')}; 57 | } 58 | } 59 | ); 60 | }; 61 | 62 | function hashPassword(username, password) { 63 | const s = username + ':' + password; 64 | return crypto.createHash('sha256').update(s).digest('hex'); 65 | } 66 | 67 | module.exports = { 68 | register: register, 69 | me: me, 70 | login: login 71 | }; 72 | -------------------------------------------------------------------------------- /api/neo4j/dbUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // neo4j cypher helper module 4 | const nconf = require('../config'); 5 | 6 | const neo4j = require('neo4j-driver'); 7 | const driver = neo4j.driver(nconf.get('neo4j-local'), neo4j.auth.basic(nconf.get('USERNAME'), nconf.get('PASSWORD'))); 8 | 9 | exports.getSession = function (context) { 10 | if(context.neo4jSession) { 11 | return context.neo4jSession; 12 | } 13 | else { 14 | context.neo4jSession = driver.session(); 15 | return context.neo4jSession; 16 | } 17 | }; 18 | 19 | exports.dbWhere = function (name, keys) { 20 | if (_.isArray(name)) { 21 | _.map(name, (obj) => { 22 | return _whereTemplate(obj.name, obj.key, obj.paramKey); 23 | }); 24 | } else if (keys && keys.length) { 25 | return 'WHERE ' + _.map(keys, (key) => { 26 | return _whereTemplate(name, key); 27 | }).join(' AND '); 28 | } 29 | }; 30 | 31 | function whereTemplate(name, key, paramKey) { 32 | return name + '.' + key + '={' + (paramKey || key) + '}'; 33 | } 34 | -------------------------------------------------------------------------------- /api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neo4j-movies-api", 3 | "version": "0.0.20", 4 | "author": { 5 | "name": "The SilveLogic", 6 | "email": "info@tsl.io", 7 | "url": "tsl.io" 8 | }, 9 | "description": "API for a movie demo app using Neo4j", 10 | "repository": { 11 | "type": "git", 12 | "url": "tbd" 13 | }, 14 | "keywords": [ 15 | "node", 16 | "neo4j", 17 | "express", 18 | "api", 19 | "http", 20 | "rest", 21 | "swagger", 22 | "server" 23 | ], 24 | "engines": { 25 | "node": ">=0.11.x", 26 | "npm": "1.2.x" 27 | }, 28 | "dependencies": { 29 | "body-parser": "^1.19.0", 30 | "dotenv": "^8.2.0", 31 | "errorhandler": "^1.5.1", 32 | "express": "^4.17.1", 33 | "hat": "0.0.3", 34 | "install": "^0.13.0", 35 | "lodash": "^4.17.21", 36 | "md5": "^2.2.1", 37 | "method-override": "^3.0.0", 38 | "mocha": "^7.2.0", 39 | "moment": "^2.26.0", 40 | "morgan": "^1.10.0", 41 | "nconf": "^0.10.0", 42 | "neo4j-driver": "^4.1.2", 43 | "node-uuid": "^1.4.8", 44 | "pug": "^3.0.1", 45 | "randomstring": "^1.1.5", 46 | "serve-favicon": "^2.5.0", 47 | "should": "^13.2.3", 48 | "swagger-jsdoc": "^4.0.0", 49 | "swagger-ui-express": "^4.1.4" 50 | }, 51 | "license": "MIT" 52 | } 53 | -------------------------------------------------------------------------------- /api/routes/genres.js: -------------------------------------------------------------------------------- 1 | const Genres = require("../models/genres") 2 | , writeResponse = require('../helpers/response').writeResponse 3 | , dbUtils = require('../neo4j/dbUtils'); 4 | 5 | /** 6 | * @swagger 7 | * definition: 8 | * Genre: 9 | * type: object 10 | * properties: 11 | * id: 12 | * type: integer 13 | * name: 14 | * type: string 15 | */ 16 | 17 | /** 18 | * @swagger 19 | * /api/v0/genres: 20 | * get: 21 | * tags: 22 | * - genres 23 | * description: Returns all genres 24 | * summary: Returns all genres 25 | * produces: 26 | * - application/json 27 | * responses: 28 | * 200: 29 | * description: A list of genres 30 | * schema: 31 | * type: array 32 | * items: 33 | * $ref: '#/definitions/Genre' 34 | */ 35 | exports.list = function (req, res, next) { 36 | Genres.getAll(dbUtils.getSession(req)) 37 | .then(response => writeResponse(res, response)) 38 | .catch(next); 39 | }; -------------------------------------------------------------------------------- /api/routes/index.js: -------------------------------------------------------------------------------- 1 | // convenience wrapper around all other files: 2 | exports.users = require('./users'); 3 | exports.site = require('./site'); 4 | exports.people = require('./people'); 5 | exports.movies = require('./movies'); 6 | exports.genres = require('./genres'); -------------------------------------------------------------------------------- /api/routes/people.js: -------------------------------------------------------------------------------- 1 | const People = require('../models/people') 2 | , _ = require('lodash') 3 | , writeResponse = require('../helpers/response').writeResponse 4 | , dbUtils = require('../neo4j/dbUtils'); 5 | 6 | /** 7 | * @swagger 8 | * definition: 9 | * Person: 10 | * type: object 11 | * properties: 12 | * id: 13 | * type: integer 14 | * name: 15 | * type: string 16 | * poster_image: 17 | * type: string 18 | */ 19 | 20 | /** 21 | * @swagger 22 | * /api/v0/people: 23 | * get: 24 | * tags: 25 | * - people 26 | * description: Returns all people 27 | * summary: Returns all people 28 | * produces: 29 | * - application/json 30 | * responses: 31 | * 200: 32 | * description: A list of people 33 | * schema: 34 | * type: array 35 | * items: 36 | * $ref: '#/definitions/Person' 37 | */ 38 | exports.list = function (req, res, next) { 39 | People.getAll(dbUtils.getSession(req)) 40 | .then(response => writeResponse(res, response)) 41 | .catch(next); 42 | }; 43 | 44 | /** 45 | * @swagger 46 | * /api/v0/people/bacon: 47 | * get: 48 | * tags: 49 | * - people 50 | * description: Returns all Bacon paths from person 1 to person 2 51 | * summary: Returns all Bacon paths from person 1 to person 2 52 | * produces: 53 | * - application/json 54 | * parameters: 55 | * - name: name1 56 | * description: Name of the origin person 57 | * in: query 58 | * required: true 59 | * type: string 60 | * - name: name2 61 | * description: Name of the target person 62 | * in: query 63 | * required: true 64 | * type: string 65 | * responses: 66 | * 200: 67 | * description: A list of people 68 | * schema: 69 | * type: array 70 | * items: 71 | * $ref: '#/definitions/Person' 72 | */ 73 | exports.getBaconPeople = function (req, res, next) { 74 | const name1 = req.query.name1; 75 | const name2 = req.query.name2; 76 | 77 | People.getBaconPeople(dbUtils.getSession(req), req.query.name1, req.query.name2) 78 | .then(response => writeResponse(res, response)) 79 | .catch(next); 80 | }; 81 | 82 | /** 83 | * @swagger 84 | * /api/v0/people/{id}: 85 | * get: 86 | * tags: 87 | * - people 88 | * description: Returns a person by id 89 | * summary: Returns a person by id 90 | * produces: 91 | * - application/json 92 | * parameters: 93 | * - name: id 94 | * description: Person id 95 | * in: path 96 | * required: true 97 | * type: integer 98 | * responses: 99 | * 200: 100 | * description: A person 101 | * schema: 102 | * $ref: '#/definitions/Person' 103 | * 400: 104 | * description: Error message(s) 105 | * 404: 106 | * description: Person not found 107 | */ 108 | exports.findById = function (req, res, next) { 109 | const id = req.params.id; 110 | if (!id) throw {message: 'Invalid id', status: 400}; 111 | 112 | People.getById(dbUtils.getSession(req), id) 113 | .then(response => writeResponse(res, response)) 114 | .catch(next); 115 | }; -------------------------------------------------------------------------------- /api/routes/site.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GET home page. 3 | */ 4 | 5 | exports.index = function(req, res){ 6 | res.render('index'); 7 | }; -------------------------------------------------------------------------------- /api/swaggerui/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ 2 | html, 3 | body, 4 | div, 5 | span, 6 | applet, 7 | object, 8 | iframe, 9 | h1, 10 | h2, 11 | h3, 12 | h4, 13 | h5, 14 | h6, 15 | p, 16 | blockquote, 17 | pre, 18 | a, 19 | abbr, 20 | acronym, 21 | address, 22 | big, 23 | cite, 24 | code, 25 | del, 26 | dfn, 27 | em, 28 | img, 29 | ins, 30 | kbd, 31 | q, 32 | s, 33 | samp, 34 | small, 35 | strike, 36 | strong, 37 | sub, 38 | sup, 39 | tt, 40 | var, 41 | b, 42 | u, 43 | i, 44 | center, 45 | dl, 46 | dt, 47 | dd, 48 | ol, 49 | ul, 50 | li, 51 | fieldset, 52 | form, 53 | label, 54 | legend, 55 | table, 56 | caption, 57 | tbody, 58 | tfoot, 59 | thead, 60 | tr, 61 | th, 62 | td, 63 | article, 64 | aside, 65 | canvas, 66 | details, 67 | embed, 68 | figure, 69 | figcaption, 70 | footer, 71 | header, 72 | hgroup, 73 | menu, 74 | nav, 75 | output, 76 | ruby, 77 | section, 78 | summary, 79 | time, 80 | mark, 81 | audio, 82 | video { 83 | margin: 0; 84 | padding: 0; 85 | border: 0; 86 | font-size: 100%; 87 | font: inherit; 88 | vertical-align: baseline; 89 | } 90 | /* HTML5 display-role reset for older browsers */ 91 | article, 92 | aside, 93 | details, 94 | figcaption, 95 | figure, 96 | footer, 97 | header, 98 | hgroup, 99 | menu, 100 | nav, 101 | section { 102 | display: block; 103 | } 104 | body { 105 | line-height: 1; 106 | } 107 | ol, 108 | ul { 109 | list-style: none; 110 | } 111 | blockquote, 112 | q { 113 | quotes: none; 114 | } 115 | blockquote:before, 116 | blockquote:after, 117 | q:before, 118 | q:after { 119 | content: ''; 120 | content: none; 121 | } 122 | table { 123 | border-collapse: collapse; 124 | border-spacing: 0; 125 | } 126 | -------------------------------------------------------------------------------- /api/swaggerui/css/typography.css: -------------------------------------------------------------------------------- 1 | /* Google Font's Droid Sans */ 2 | @font-face { 3 | font-family: 'Droid Sans'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf'), format('truetype'); 7 | } 8 | /* Google Font's Droid Sans Bold */ 9 | @font-face { 10 | font-family: 'Droid Sans'; 11 | font-style: normal; 12 | font-weight: 700; 13 | src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf'), format('truetype'); 14 | } 15 | -------------------------------------------------------------------------------- /api/swaggerui/fonts/DroidSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/fonts/DroidSans-Bold.ttf -------------------------------------------------------------------------------- /api/swaggerui/fonts/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/fonts/DroidSans.ttf -------------------------------------------------------------------------------- /api/swaggerui/images/collapse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/collapse.gif -------------------------------------------------------------------------------- /api/swaggerui/images/expand.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/expand.gif -------------------------------------------------------------------------------- /api/swaggerui/images/explorer_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/explorer_icons.png -------------------------------------------------------------------------------- /api/swaggerui/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/favicon-16x16.png -------------------------------------------------------------------------------- /api/swaggerui/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/favicon-32x32.png -------------------------------------------------------------------------------- /api/swaggerui/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/favicon.ico -------------------------------------------------------------------------------- /api/swaggerui/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/logo_small.png -------------------------------------------------------------------------------- /api/swaggerui/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/pet_store_api.png -------------------------------------------------------------------------------- /api/swaggerui/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/throbber.gif -------------------------------------------------------------------------------- /api/swaggerui/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/swaggerui/images/wordnik_api.png -------------------------------------------------------------------------------- /api/swaggerui/lang/ca.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Advertència: Obsolet", 6 | "Implementation Notes":"Notes d'implementació", 7 | "Response Class":"Classe de la Resposta", 8 | "Status":"Estatus", 9 | "Parameters":"Paràmetres", 10 | "Parameter":"Paràmetre", 11 | "Value":"Valor", 12 | "Description":"Descripció", 13 | "Parameter Type":"Tipus del Paràmetre", 14 | "Data Type":"Tipus de la Dada", 15 | "Response Messages":"Missatges de la Resposta", 16 | "HTTP Status Code":"Codi d'Estatus HTTP", 17 | "Reason":"Raó", 18 | "Response Model":"Model de la Resposta", 19 | "Request URL":"URL de la Sol·licitud", 20 | "Response Body":"Cos de la Resposta", 21 | "Response Code":"Codi de la Resposta", 22 | "Response Headers":"Capçaleres de la Resposta", 23 | "Hide Response":"Amagar Resposta", 24 | "Try it out!":"Prova-ho!", 25 | "Show/Hide":"Mostrar/Amagar", 26 | "List Operations":"Llista Operacions", 27 | "Expand Operations":"Expandir Operacions", 28 | "Raw":"Cru", 29 | "can't parse JSON. Raw result":"no puc analitzar el JSON. Resultat cru", 30 | "Example Value":"Valor d'Exemple", 31 | "Model Schema":"Esquema del Model", 32 | "Model":"Model", 33 | "apply":"aplicar", 34 | "Username":"Nom d'usuari", 35 | "Password":"Contrasenya", 36 | "Terms of service":"Termes del servei", 37 | "Created by":"Creat per", 38 | "See more at":"Veure més en", 39 | "Contact the developer":"Contactar amb el desenvolupador", 40 | "api version":"versió de la api", 41 | "Response Content Type":"Tipus de Contingut de la Resposta", 42 | "fetching resource":"recollint recurs", 43 | "fetching resource list":"recollins llista de recursos", 44 | "Explore":"Explorant", 45 | "Show Swagger Petstore Example Apis":"Mostrar API d'Exemple Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"No es pot llegir del servidor. Potser no teniu la configuració de control d'accés apropiada.", 47 | "Please specify the protocol for":"Si us plau, especifiqueu el protocol per a", 48 | "Can't read swagger JSON from":"No es pot llegir el JSON de swagger des de", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Finalitzada la càrrega del recurs informatiu. Renderitzant Swagger UI", 50 | "Unable to read api":"No es pot llegir l'api", 51 | "from path":"des de la ruta", 52 | "server returned":"el servidor ha retornat" 53 | }); 54 | -------------------------------------------------------------------------------- /api/swaggerui/lang/en.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Warning: Deprecated", 6 | "Implementation Notes":"Implementation Notes", 7 | "Response Class":"Response Class", 8 | "Status":"Status", 9 | "Parameters":"Parameters", 10 | "Parameter":"Parameter", 11 | "Value":"Value", 12 | "Description":"Description", 13 | "Parameter Type":"Parameter Type", 14 | "Data Type":"Data Type", 15 | "Response Messages":"Response Messages", 16 | "HTTP Status Code":"HTTP Status Code", 17 | "Reason":"Reason", 18 | "Response Model":"Response Model", 19 | "Request URL":"Request URL", 20 | "Response Body":"Response Body", 21 | "Response Code":"Response Code", 22 | "Response Headers":"Response Headers", 23 | "Hide Response":"Hide Response", 24 | "Headers":"Headers", 25 | "Try it out!":"Try it out!", 26 | "Show/Hide":"Show/Hide", 27 | "List Operations":"List Operations", 28 | "Expand Operations":"Expand Operations", 29 | "Raw":"Raw", 30 | "can't parse JSON. Raw result":"can't parse JSON. Raw result", 31 | "Example Value":"Example Value", 32 | "Model Schema":"Model Schema", 33 | "Model":"Model", 34 | "Click to set as parameter value":"Click to set as parameter value", 35 | "apply":"apply", 36 | "Username":"Username", 37 | "Password":"Password", 38 | "Terms of service":"Terms of service", 39 | "Created by":"Created by", 40 | "See more at":"See more at", 41 | "Contact the developer":"Contact the developer", 42 | "api version":"api version", 43 | "Response Content Type":"Response Content Type", 44 | "Parameter content type:":"Parameter content type:", 45 | "fetching resource":"fetching resource", 46 | "fetching resource list":"fetching resource list", 47 | "Explore":"Explore", 48 | "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Can't read from server. It may not have the appropriate access-control-origin settings.", 50 | "Please specify the protocol for":"Please specify the protocol for", 51 | "Can't read swagger JSON from":"Can't read swagger JSON from", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"Finished Loading Resource Information. Rendering Swagger UI", 53 | "Unable to read api":"Unable to read api", 54 | "from path":"from path", 55 | "server returned":"server returned" 56 | }); 57 | -------------------------------------------------------------------------------- /api/swaggerui/lang/es.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Advertencia: Obsoleto", 6 | "Implementation Notes":"Notas de implementación", 7 | "Response Class":"Clase de la Respuesta", 8 | "Status":"Status", 9 | "Parameters":"Parámetros", 10 | "Parameter":"Parámetro", 11 | "Value":"Valor", 12 | "Description":"Descripción", 13 | "Parameter Type":"Tipo del Parámetro", 14 | "Data Type":"Tipo del Dato", 15 | "Response Messages":"Mensajes de la Respuesta", 16 | "HTTP Status Code":"Código de Status HTTP", 17 | "Reason":"Razón", 18 | "Response Model":"Modelo de la Respuesta", 19 | "Request URL":"URL de la Solicitud", 20 | "Response Body":"Cuerpo de la Respuesta", 21 | "Response Code":"Código de la Respuesta", 22 | "Response Headers":"Encabezados de la Respuesta", 23 | "Hide Response":"Ocultar Respuesta", 24 | "Try it out!":"Pruébalo!", 25 | "Show/Hide":"Mostrar/Ocultar", 26 | "List Operations":"Listar Operaciones", 27 | "Expand Operations":"Expandir Operaciones", 28 | "Raw":"Crudo", 29 | "can't parse JSON. Raw result":"no puede parsear el JSON. Resultado crudo", 30 | "Example Value":"Valor de Ejemplo", 31 | "Model Schema":"Esquema del Modelo", 32 | "Model":"Modelo", 33 | "apply":"aplicar", 34 | "Username":"Nombre de usuario", 35 | "Password":"Contraseña", 36 | "Terms of service":"Términos de Servicio", 37 | "Created by":"Creado por", 38 | "See more at":"Ver más en", 39 | "Contact the developer":"Contactar al desarrollador", 40 | "api version":"versión de la api", 41 | "Response Content Type":"Tipo de Contenido (Content Type) de la Respuesta", 42 | "fetching resource":"buscando recurso", 43 | "fetching resource list":"buscando lista del recurso", 44 | "Explore":"Explorar", 45 | "Show Swagger Petstore Example Apis":"Mostrar Api Ejemplo de Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"No se puede leer del servidor. Tal vez no tiene la configuración de control de acceso de origen (access-control-origin) apropiado.", 47 | "Please specify the protocol for":"Por favor, especificar el protocola para", 48 | "Can't read swagger JSON from":"No se puede leer el JSON de swagger desde", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Finalizada la carga del recurso de Información. Mostrando Swagger UI", 50 | "Unable to read api":"No se puede leer la api", 51 | "from path":"desde ruta", 52 | "server returned":"el servidor retornó" 53 | }); 54 | -------------------------------------------------------------------------------- /api/swaggerui/lang/fr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Avertissement : Obsolète", 6 | "Implementation Notes":"Notes d'implémentation", 7 | "Response Class":"Classe de la réponse", 8 | "Status":"Statut", 9 | "Parameters":"Paramètres", 10 | "Parameter":"Paramètre", 11 | "Value":"Valeur", 12 | "Description":"Description", 13 | "Parameter Type":"Type du paramètre", 14 | "Data Type":"Type de données", 15 | "Response Messages":"Messages de la réponse", 16 | "HTTP Status Code":"Code de statut HTTP", 17 | "Reason":"Raison", 18 | "Response Model":"Modèle de réponse", 19 | "Request URL":"URL appelée", 20 | "Response Body":"Corps de la réponse", 21 | "Response Code":"Code de la réponse", 22 | "Response Headers":"En-têtes de la réponse", 23 | "Hide Response":"Cacher la réponse", 24 | "Headers":"En-têtes", 25 | "Try it out!":"Testez !", 26 | "Show/Hide":"Afficher/Masquer", 27 | "List Operations":"Liste des opérations", 28 | "Expand Operations":"Développer les opérations", 29 | "Raw":"Brut", 30 | "can't parse JSON. Raw result":"impossible de décoder le JSON. Résultat brut", 31 | "Example Value":"Exemple la valeur", 32 | "Model Schema":"Définition du modèle", 33 | "Model":"Modèle", 34 | "apply":"appliquer", 35 | "Username":"Nom d'utilisateur", 36 | "Password":"Mot de passe", 37 | "Terms of service":"Conditions de service", 38 | "Created by":"Créé par", 39 | "See more at":"Voir plus sur", 40 | "Contact the developer":"Contacter le développeur", 41 | "api version":"version de l'api", 42 | "Response Content Type":"Content Type de la réponse", 43 | "fetching resource":"récupération de la ressource", 44 | "fetching resource list":"récupération de la liste de ressources", 45 | "Explore":"Explorer", 46 | "Show Swagger Petstore Example Apis":"Montrer les Apis de l'exemple Petstore de Swagger", 47 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Impossible de lire à partir du serveur. Il se peut que les réglages access-control-origin ne soient pas appropriés.", 48 | "Please specify the protocol for":"Veuillez spécifier un protocole pour", 49 | "Can't read swagger JSON from":"Impossible de lire le JSON swagger à partir de", 50 | "Finished Loading Resource Information. Rendering Swagger UI":"Chargement des informations terminé. Affichage de Swagger UI", 51 | "Unable to read api":"Impossible de lire l'api", 52 | "from path":"à partir du chemin", 53 | "server returned":"réponse du serveur" 54 | }); 55 | -------------------------------------------------------------------------------- /api/swaggerui/lang/geo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"ყურადღება: აღარ გამოიყენება", 6 | "Implementation Notes":"იმპლემენტაციის აღწერა", 7 | "Response Class":"რესპონს კლასი", 8 | "Status":"სტატუსი", 9 | "Parameters":"პარამეტრები", 10 | "Parameter":"პარამეტრი", 11 | "Value":"მნიშვნელობა", 12 | "Description":"აღწერა", 13 | "Parameter Type":"პარამეტრის ტიპი", 14 | "Data Type":"მონაცემის ტიპი", 15 | "Response Messages":"პასუხი", 16 | "HTTP Status Code":"HTTP სტატუსი", 17 | "Reason":"მიზეზი", 18 | "Response Model":"რესპონს მოდელი", 19 | "Request URL":"მოთხოვნის URL", 20 | "Response Body":"პასუხის სხეული", 21 | "Response Code":"პასუხის კოდი", 22 | "Response Headers":"პასუხის ჰედერები", 23 | "Hide Response":"დამალე პასუხი", 24 | "Headers":"ჰედერები", 25 | "Try it out!":"ცადე !", 26 | "Show/Hide":"გამოჩენა/დამალვა", 27 | "List Operations":"ოპერაციების სია", 28 | "Expand Operations":"ოპერაციები ვრცლად", 29 | "Raw":"ნედლი", 30 | "can't parse JSON. Raw result":"JSON-ის დამუშავება ვერ მოხერხდა. ნედლი პასუხი", 31 | "Example Value":"მაგალითი", 32 | "Model Schema":"მოდელის სტრუქტურა", 33 | "Model":"მოდელი", 34 | "Click to set as parameter value":"პარამეტრისთვის მნიშვნელობის მისანიჭებლად, დააკლიკე", 35 | "apply":"გამოყენება", 36 | "Username":"მოხმარებელი", 37 | "Password":"პაროლი", 38 | "Terms of service":"მომსახურების პირობები", 39 | "Created by":"შექმნა", 40 | "See more at":"ნახე ვრცლად", 41 | "Contact the developer":"დაუკავშირდი დეველოპერს", 42 | "api version":"api ვერსია", 43 | "Response Content Type":"პასუხის კონტენტის ტიპი", 44 | "Parameter content type:":"პარამეტრის კონტენტის ტიპი:", 45 | "fetching resource":"რესურსების მიღება", 46 | "fetching resource list":"რესურსების სიის მიღება", 47 | "Explore":"ნახვა", 48 | "Show Swagger Petstore Example Apis":"ნახე Swagger Petstore სამაგალითო Api", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"სერვერთან დაკავშირება ვერ ხერხდება. შეამოწმეთ access-control-origin.", 50 | "Please specify the protocol for":"მიუთითეთ პროტოკოლი", 51 | "Can't read swagger JSON from":"swagger JSON წაკითხვა ვერ მოხერხდა", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"რესურსების ჩატვირთვა სრულდება. Swagger UI რენდერდება", 53 | "Unable to read api":"api წაკითხვა ვერ მოხერხდა", 54 | "from path":"მისამართიდან", 55 | "server returned":"სერვერმა დააბრუნა" 56 | }); 57 | -------------------------------------------------------------------------------- /api/swaggerui/lang/it.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Attenzione: Deprecato", 6 | "Implementation Notes":"Note di implementazione", 7 | "Response Class":"Classe della risposta", 8 | "Status":"Stato", 9 | "Parameters":"Parametri", 10 | "Parameter":"Parametro", 11 | "Value":"Valore", 12 | "Description":"Descrizione", 13 | "Parameter Type":"Tipo di parametro", 14 | "Data Type":"Tipo di dato", 15 | "Response Messages":"Messaggi della risposta", 16 | "HTTP Status Code":"Codice stato HTTP", 17 | "Reason":"Motivo", 18 | "Response Model":"Modello di risposta", 19 | "Request URL":"URL della richiesta", 20 | "Response Body":"Corpo della risposta", 21 | "Response Code":"Oggetto della risposta", 22 | "Response Headers":"Intestazioni della risposta", 23 | "Hide Response":"Nascondi risposta", 24 | "Try it out!":"Provalo!", 25 | "Show/Hide":"Mostra/Nascondi", 26 | "List Operations":"Mostra operazioni", 27 | "Expand Operations":"Espandi operazioni", 28 | "Raw":"Grezzo (raw)", 29 | "can't parse JSON. Raw result":"non è possibile parsare il JSON. Risultato grezzo (raw).", 30 | "Model Schema":"Schema del modello", 31 | "Model":"Modello", 32 | "apply":"applica", 33 | "Username":"Nome utente", 34 | "Password":"Password", 35 | "Terms of service":"Condizioni del servizio", 36 | "Created by":"Creato da", 37 | "See more at":"Informazioni aggiuntive:", 38 | "Contact the developer":"Contatta lo sviluppatore", 39 | "api version":"versione api", 40 | "Response Content Type":"Tipo di contenuto (content type) della risposta", 41 | "fetching resource":"recuperando la risorsa", 42 | "fetching resource list":"recuperando lista risorse", 43 | "Explore":"Esplora", 44 | "Show Swagger Petstore Example Apis":"Mostra le api di esempio di Swagger Petstore", 45 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Non è possibile leggere dal server. Potrebbe non avere le impostazioni di controllo accesso origine (access-control-origin) appropriate.", 46 | "Please specify the protocol for":"Si prega di specificare il protocollo per", 47 | "Can't read swagger JSON from":"Impossibile leggere JSON swagger da:", 48 | "Finished Loading Resource Information. Rendering Swagger UI":"Lettura informazioni risorse termianta. Swagger UI viene mostrata", 49 | "Unable to read api":"Impossibile leggere la api", 50 | "from path":"da cartella", 51 | "server returned":"il server ha restituito" 52 | }); 53 | -------------------------------------------------------------------------------- /api/swaggerui/lang/ja.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"警告: 廃止予定", 6 | "Implementation Notes":"実装メモ", 7 | "Response Class":"レスポンスクラス", 8 | "Status":"ステータス", 9 | "Parameters":"パラメータ群", 10 | "Parameter":"パラメータ", 11 | "Value":"値", 12 | "Description":"説明", 13 | "Parameter Type":"パラメータタイプ", 14 | "Data Type":"データタイプ", 15 | "Response Messages":"レスポンスメッセージ", 16 | "HTTP Status Code":"HTTPステータスコード", 17 | "Reason":"理由", 18 | "Response Model":"レスポンスモデル", 19 | "Request URL":"リクエストURL", 20 | "Response Body":"レスポンスボディ", 21 | "Response Code":"レスポンスコード", 22 | "Response Headers":"レスポンスヘッダ", 23 | "Hide Response":"レスポンスを隠す", 24 | "Headers":"ヘッダ", 25 | "Try it out!":"実際に実行!", 26 | "Show/Hide":"表示/非表示", 27 | "List Operations":"操作一覧", 28 | "Expand Operations":"操作の展開", 29 | "Raw":"Raw", 30 | "can't parse JSON. Raw result":"JSONへ解釈できません. 未加工の結果", 31 | "Model Schema":"モデルスキーマ", 32 | "Model":"モデル", 33 | "apply":"実行", 34 | "Username":"ユーザ名", 35 | "Password":"パスワード", 36 | "Terms of service":"サービス利用規約", 37 | "Created by":"Created by", 38 | "See more at":"See more at", 39 | "Contact the developer":"開発者に連絡", 40 | "api version":"APIバージョン", 41 | "Response Content Type":"レスポンス コンテンツタイプ", 42 | "fetching resource":"リソースの取得", 43 | "fetching resource list":"リソース一覧の取得", 44 | "Explore":"Explore", 45 | "Show Swagger Petstore Example Apis":"SwaggerペットストアAPIの表示", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"サーバから読み込めません. 適切なaccess-control-origin設定を持っていない可能性があります.", 47 | "Please specify the protocol for":"プロトコルを指定してください", 48 | "Can't read swagger JSON from":"次からswagger JSONを読み込めません", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"リソース情報の読み込みが完了しました. Swagger UIを描画しています", 50 | "Unable to read api":"APIを読み込めません", 51 | "from path":"次のパスから", 52 | "server returned":"サーバからの返答" 53 | }); 54 | -------------------------------------------------------------------------------- /api/swaggerui/lang/ko-kr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"경고:폐기예정됨", 6 | "Implementation Notes":"구현 노트", 7 | "Response Class":"응답 클래스", 8 | "Status":"상태", 9 | "Parameters":"매개변수들", 10 | "Parameter":"매개변수", 11 | "Value":"값", 12 | "Description":"설명", 13 | "Parameter Type":"매개변수 타입", 14 | "Data Type":"데이터 타입", 15 | "Response Messages":"응답 메세지", 16 | "HTTP Status Code":"HTTP 상태 코드", 17 | "Reason":"원인", 18 | "Response Model":"응답 모델", 19 | "Request URL":"요청 URL", 20 | "Response Body":"응답 본문", 21 | "Response Code":"응답 코드", 22 | "Response Headers":"응답 헤더", 23 | "Hide Response":"응답 숨기기", 24 | "Headers":"헤더", 25 | "Try it out!":"써보기!", 26 | "Show/Hide":"보이기/숨기기", 27 | "List Operations":"목록 작업", 28 | "Expand Operations":"전개 작업", 29 | "Raw":"원본", 30 | "can't parse JSON. Raw result":"JSON을 파싱할수 없음. 원본결과:", 31 | "Model Schema":"모델 스키마", 32 | "Model":"모델", 33 | "apply":"적용", 34 | "Username":"사용자 이름", 35 | "Password":"암호", 36 | "Terms of service":"이용약관", 37 | "Created by":"작성자", 38 | "See more at":"추가정보:", 39 | "Contact the developer":"개발자에게 문의", 40 | "api version":"api버전", 41 | "Response Content Type":"응답Content Type", 42 | "fetching resource":"리소스 가져오기", 43 | "fetching resource list":"리소스 목록 가져오기", 44 | "Explore":"탐색", 45 | "Show Swagger Petstore Example Apis":"Swagger Petstore 예제 보기", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"서버로부터 읽어들일수 없습니다. access-control-origin 설정이 올바르지 않을수 있습니다.", 47 | "Please specify the protocol for":"다음을 위한 프로토콜을 정하세요", 48 | "Can't read swagger JSON from":"swagger JSON 을 다음으로 부터 읽을수 없습니다", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"리소스 정보 불러오기 완료. Swagger UI 랜더링", 50 | "Unable to read api":"api를 읽을 수 없습니다.", 51 | "from path":"다음 경로로 부터", 52 | "server returned":"서버 응답함." 53 | }); 54 | -------------------------------------------------------------------------------- /api/swaggerui/lang/pl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Uwaga: Wycofane", 6 | "Implementation Notes":"Uwagi Implementacji", 7 | "Response Class":"Klasa Odpowiedzi", 8 | "Status":"Status", 9 | "Parameters":"Parametry", 10 | "Parameter":"Parametr", 11 | "Value":"Wartość", 12 | "Description":"Opis", 13 | "Parameter Type":"Typ Parametru", 14 | "Data Type":"Typ Danych", 15 | "Response Messages":"Wiadomości Odpowiedzi", 16 | "HTTP Status Code":"Kod Statusu HTTP", 17 | "Reason":"Przyczyna", 18 | "Response Model":"Model Odpowiedzi", 19 | "Request URL":"URL Wywołania", 20 | "Response Body":"Treść Odpowiedzi", 21 | "Response Code":"Kod Odpowiedzi", 22 | "Response Headers":"Nagłówki Odpowiedzi", 23 | "Hide Response":"Ukryj Odpowiedź", 24 | "Headers":"Nagłówki", 25 | "Try it out!":"Wypróbuj!", 26 | "Show/Hide":"Pokaż/Ukryj", 27 | "List Operations":"Lista Operacji", 28 | "Expand Operations":"Rozwiń Operacje", 29 | "Raw":"Nieprzetworzone", 30 | "can't parse JSON. Raw result":"nie można przetworzyć pliku JSON. Nieprzetworzone dane", 31 | "Model Schema":"Schemat Modelu", 32 | "Model":"Model", 33 | "apply":"użyj", 34 | "Username":"Nazwa użytkownika", 35 | "Password":"Hasło", 36 | "Terms of service":"Warunki używania", 37 | "Created by":"Utworzone przez", 38 | "See more at":"Zobacz więcej na", 39 | "Contact the developer":"Kontakt z deweloperem", 40 | "api version":"wersja api", 41 | "Response Content Type":"Typ Zasobu Odpowiedzi", 42 | "fetching resource":"ładowanie zasobu", 43 | "fetching resource list":"ładowanie listy zasobów", 44 | "Explore":"Eksploruj", 45 | "Show Swagger Petstore Example Apis":"Pokaż Przykładowe Api Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Brak połączenia z serwerem. Może on nie mieć odpowiednich ustawień access-control-origin.", 47 | "Please specify the protocol for":"Proszę podać protokół dla", 48 | "Can't read swagger JSON from":"Nie można odczytać swagger JSON z", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Ukończono Ładowanie Informacji o Zasobie. Renderowanie Swagger UI", 50 | "Unable to read api":"Nie można odczytać api", 51 | "from path":"ze ścieżki", 52 | "server returned":"serwer zwrócił" 53 | }); 54 | -------------------------------------------------------------------------------- /api/swaggerui/lang/pt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Aviso: Depreciado", 6 | "Implementation Notes":"Notas de Implementação", 7 | "Response Class":"Classe de resposta", 8 | "Status":"Status", 9 | "Parameters":"Parâmetros", 10 | "Parameter":"Parâmetro", 11 | "Value":"Valor", 12 | "Description":"Descrição", 13 | "Parameter Type":"Tipo de parâmetro", 14 | "Data Type":"Tipo de dados", 15 | "Response Messages":"Mensagens de resposta", 16 | "HTTP Status Code":"Código de status HTTP", 17 | "Reason":"Razão", 18 | "Response Model":"Modelo resposta", 19 | "Request URL":"URL requisição", 20 | "Response Body":"Corpo da resposta", 21 | "Response Code":"Código da resposta", 22 | "Response Headers":"Cabeçalho da resposta", 23 | "Headers":"Cabeçalhos", 24 | "Hide Response":"Esconder resposta", 25 | "Try it out!":"Tente agora!", 26 | "Show/Hide":"Mostrar/Esconder", 27 | "List Operations":"Listar operações", 28 | "Expand Operations":"Expandir operações", 29 | "Raw":"Cru", 30 | "can't parse JSON. Raw result":"Falha ao analisar JSON. Resulto cru", 31 | "Model Schema":"Modelo esquema", 32 | "Model":"Modelo", 33 | "apply":"Aplicar", 34 | "Username":"Usuário", 35 | "Password":"Senha", 36 | "Terms of service":"Termos do serviço", 37 | "Created by":"Criado por", 38 | "See more at":"Veja mais em", 39 | "Contact the developer":"Contate o desenvolvedor", 40 | "api version":"Versão api", 41 | "Response Content Type":"Tipo de conteúdo da resposta", 42 | "fetching resource":"busca recurso", 43 | "fetching resource list":"buscando lista de recursos", 44 | "Explore":"Explorar", 45 | "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Não é possível ler do servidor. Pode não ter as apropriadas configurações access-control-origin", 47 | "Please specify the protocol for":"Por favor especifique o protocolo", 48 | "Can't read swagger JSON from":"Não é possível ler o JSON Swagger de", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Carregar informação de recurso finalizada. Renderizando Swagger UI", 50 | "Unable to read api":"Não foi possível ler api", 51 | "from path":"do caminho", 52 | "server returned":"servidor retornou" 53 | }); 54 | -------------------------------------------------------------------------------- /api/swaggerui/lang/ru.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Предупреждение: Устарело", 6 | "Implementation Notes":"Заметки", 7 | "Response Class":"Пример ответа", 8 | "Status":"Статус", 9 | "Parameters":"Параметры", 10 | "Parameter":"Параметр", 11 | "Value":"Значение", 12 | "Description":"Описание", 13 | "Parameter Type":"Тип параметра", 14 | "Data Type":"Тип данных", 15 | "HTTP Status Code":"HTTP код", 16 | "Reason":"Причина", 17 | "Response Model":"Структура ответа", 18 | "Request URL":"URL запроса", 19 | "Response Body":"Тело ответа", 20 | "Response Code":"HTTP код ответа", 21 | "Response Headers":"Заголовки ответа", 22 | "Hide Response":"Спрятать ответ", 23 | "Headers":"Заголовки", 24 | "Response Messages":"Что может прийти в ответ", 25 | "Try it out!":"Попробовать!", 26 | "Show/Hide":"Показать/Скрыть", 27 | "List Operations":"Операции кратко", 28 | "Expand Operations":"Операции подробно", 29 | "Raw":"В сыром виде", 30 | "can't parse JSON. Raw result":"Не удается распарсить ответ:", 31 | "Example Value":"Пример", 32 | "Model Schema":"Структура", 33 | "Model":"Описание", 34 | "Click to set as parameter value":"Нажмите, чтобы испльзовать в качестве значения параметра", 35 | "apply":"применить", 36 | "Username":"Имя пользователя", 37 | "Password":"Пароль", 38 | "Terms of service":"Условия использования", 39 | "Created by":"Разработано", 40 | "See more at":"Еще тут", 41 | "Contact the developer":"Связаться с разработчиком", 42 | "api version":"Версия API", 43 | "Response Content Type":"Content Type ответа", 44 | "Parameter content type:":"Content Type параметра:", 45 | "fetching resource":"Получение ресурса", 46 | "fetching resource list":"Получение ресурсов", 47 | "Explore":"Показать", 48 | "Show Swagger Petstore Example Apis":"Показать примеры АПИ", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Не удается получить ответ от сервера. Возможно, проблема с настройками доступа", 50 | "Please specify the protocol for":"Пожалуйста, укажите протокол для", 51 | "Can't read swagger JSON from":"Не получается прочитать swagger json из", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"Загрузка информации о ресурсах завершена. Рендерим", 53 | "Unable to read api":"Не удалось прочитать api", 54 | "from path":"по адресу", 55 | "server returned":"сервер сказал" 56 | }); 57 | -------------------------------------------------------------------------------- /api/swaggerui/lang/tr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Uyarı: Deprecated", 6 | "Implementation Notes":"Gerçekleştirim Notları", 7 | "Response Class":"Dönen Sınıf", 8 | "Status":"Statü", 9 | "Parameters":"Parametreler", 10 | "Parameter":"Parametre", 11 | "Value":"Değer", 12 | "Description":"Açıklama", 13 | "Parameter Type":"Parametre Tipi", 14 | "Data Type":"Veri Tipi", 15 | "Response Messages":"Dönüş Mesajı", 16 | "HTTP Status Code":"HTTP Statü Kodu", 17 | "Reason":"Gerekçe", 18 | "Response Model":"Dönüş Modeli", 19 | "Request URL":"İstek URL", 20 | "Response Body":"Dönüş İçeriği", 21 | "Response Code":"Dönüş Kodu", 22 | "Response Headers":"Dönüş Üst Bilgileri", 23 | "Hide Response":"Dönüşü Gizle", 24 | "Headers":"Üst Bilgiler", 25 | "Try it out!":"Dene!", 26 | "Show/Hide":"Göster/Gizle", 27 | "List Operations":"Operasyonları Listele", 28 | "Expand Operations":"Operasyonları Aç", 29 | "Raw":"Ham", 30 | "can't parse JSON. Raw result":"JSON çözümlenemiyor. Ham sonuç", 31 | "Model Schema":"Model Şema", 32 | "Model":"Model", 33 | "apply":"uygula", 34 | "Username":"Kullanıcı Adı", 35 | "Password":"Parola", 36 | "Terms of service":"Servis şartları", 37 | "Created by":"Oluşturan", 38 | "See more at":"Daha fazlası için", 39 | "Contact the developer":"Geliştirici ile İletişime Geçin", 40 | "api version":"api versiyon", 41 | "Response Content Type":"Dönüş İçerik Tipi", 42 | "fetching resource":"kaynak getiriliyor", 43 | "fetching resource list":"kaynak listesi getiriliyor", 44 | "Explore":"Keşfet", 45 | "Show Swagger Petstore Example Apis":"Swagger Petstore Örnek Api'yi Gör", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Sunucudan okuma yapılamıyor. Sunucu access-control-origin ayarlarınızı kontrol edin.", 47 | "Please specify the protocol for":"Lütfen istenen adres için protokol belirtiniz", 48 | "Can't read swagger JSON from":"Swagger JSON bu kaynaktan okunamıyor", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Kaynak baglantısı tamamlandı. Swagger UI gösterime hazırlanıyor", 50 | "Unable to read api":"api okunamadı", 51 | "from path":"yoldan", 52 | "server returned":"sunucuya dönüldü" 53 | }); 54 | -------------------------------------------------------------------------------- /api/swaggerui/lang/translator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Translator for documentation pages. 5 | * 6 | * To enable translation you should include one of language-files in your index.html 7 | * after . 8 | * For example - 9 | * 10 | * If you wish to translate some new texts you should do two things: 11 | * 1. Add a new phrase pair ("New Phrase": "New Translation") into your language file (for example lang/ru.js). It will be great if you add it in other language files too. 12 | * 2. Mark that text it templates this way New Phrase or . 13 | * The main thing here is attribute data-sw-translate. Only inner html, title-attribute and value-attribute are going to translate. 14 | * 15 | */ 16 | window.SwaggerTranslator = { 17 | 18 | _words:[], 19 | 20 | translate: function(sel) { 21 | var $this = this; 22 | sel = sel || '[data-sw-translate]'; 23 | 24 | $(sel).each(function() { 25 | $(this).html($this._tryTranslate($(this).html())); 26 | 27 | $(this).val($this._tryTranslate($(this).val())); 28 | $(this).attr('title', $this._tryTranslate($(this).attr('title'))); 29 | }); 30 | }, 31 | 32 | _tryTranslate: function(word) { 33 | return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word; 34 | }, 35 | 36 | learn: function(wordsMap) { 37 | this._words = wordsMap; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /api/swaggerui/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"警告:已过时", 6 | "Implementation Notes":"实现备注", 7 | "Response Class":"响应类", 8 | "Status":"状态", 9 | "Parameters":"参数", 10 | "Parameter":"参数", 11 | "Value":"值", 12 | "Description":"描述", 13 | "Parameter Type":"参数类型", 14 | "Data Type":"数据类型", 15 | "Response Messages":"响应消息", 16 | "HTTP Status Code":"HTTP状态码", 17 | "Reason":"原因", 18 | "Response Model":"响应模型", 19 | "Request URL":"请求URL", 20 | "Response Body":"响应体", 21 | "Response Code":"响应码", 22 | "Response Headers":"响应头", 23 | "Hide Response":"隐藏响应", 24 | "Headers":"头", 25 | "Try it out!":"试一下!", 26 | "Show/Hide":"显示/隐藏", 27 | "List Operations":"显示操作", 28 | "Expand Operations":"展开操作", 29 | "Raw":"原始", 30 | "can't parse JSON. Raw result":"无法解析JSON. 原始结果", 31 | "Model Schema":"模型架构", 32 | "Model":"模型", 33 | "apply":"应用", 34 | "Username":"用户名", 35 | "Password":"密码", 36 | "Terms of service":"服务条款", 37 | "Created by":"创建者", 38 | "See more at":"查看更多:", 39 | "Contact the developer":"联系开发者", 40 | "api version":"api版本", 41 | "Response Content Type":"响应Content Type", 42 | "fetching resource":"正在获取资源", 43 | "fetching resource list":"正在获取资源列表", 44 | "Explore":"浏览", 45 | "Show Swagger Petstore Example Apis":"显示 Swagger Petstore 示例 Apis", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"无法从服务器读取。可能没有正确设置access-control-origin。", 47 | "Please specify the protocol for":"请指定协议:", 48 | "Can't read swagger JSON from":"无法读取swagger JSON于", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"已加载资源信息。正在渲染Swagger UI", 50 | "Unable to read api":"无法读取api", 51 | "from path":"从路径", 52 | "server returned":"服务器返回" 53 | }); 54 | -------------------------------------------------------------------------------- /api/swaggerui/lib/highlight.9.1.0.pack_extended.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function () { 4 | var configure, highlightBlock; 5 | 6 | configure = hljs.configure; 7 | // "extending" hljs.configure method 8 | hljs.configure = function _configure (options) { 9 | var size = options.highlightSizeThreshold; 10 | 11 | // added highlightSizeThreshold option to set maximum size 12 | // of processed string. Set to null if not a number 13 | hljs.highlightSizeThreshold = size === +size ? size : null; 14 | 15 | configure.call(this, options); 16 | }; 17 | 18 | highlightBlock = hljs.highlightBlock; 19 | 20 | // "extending" hljs.highlightBlock method 21 | hljs.highlightBlock = function _highlightBlock (el) { 22 | var innerHTML = el.innerHTML; 23 | var size = hljs.highlightSizeThreshold; 24 | 25 | // check if highlightSizeThreshold is not set or element innerHTML 26 | // is less than set option highlightSizeThreshold 27 | if (size == null || size > innerHTML.length) { 28 | // proceed with hljs.highlightBlock 29 | highlightBlock.call(hljs, el); 30 | } 31 | }; 32 | 33 | })(); 34 | 35 | -------------------------------------------------------------------------------- /api/swaggerui/lib/jquery.slideto.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery); 2 | -------------------------------------------------------------------------------- /api/swaggerui/lib/jquery.wiggle.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery Wiggle 3 | Author: WonderGroup, Jordan Thomas 4 | URL: http://labs.wondergroup.com/demos/mini-ui/index.html 5 | License: MIT (http://en.wikipedia.org/wiki/MIT_License) 6 | */ 7 | jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('
').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);} 8 | if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});}; -------------------------------------------------------------------------------- /api/swaggerui/lib/object-assign-pollyfill.js: -------------------------------------------------------------------------------- 1 | if (typeof Object.assign != 'function') { 2 | (function () { 3 | Object.assign = function (target) { 4 | 'use strict'; 5 | if (target === undefined || target === null) { 6 | throw new TypeError('Cannot convert undefined or null to object'); 7 | } 8 | 9 | var output = Object(target); 10 | for (var index = 1; index < arguments.length; index++) { 11 | var source = arguments[index]; 12 | if (source !== undefined && source !== null) { 13 | for (var nextKey in source) { 14 | if (Object.prototype.hasOwnProperty.call(source, nextKey)) { 15 | output[nextKey] = source[nextKey]; 16 | } 17 | } 18 | } 19 | } 20 | return output; 21 | }; 22 | })(); 23 | } 24 | -------------------------------------------------------------------------------- /api/swaggerui/o2c.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/views/img/neo4j-swagger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/api/views/img/neo4j-swagger.jpg -------------------------------------------------------------------------------- /api/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | 6 | p 7 | | This is a template app showing the use of  8 | a(href='http://www.neo4j.org/', target='_blank') Neo4j 9 | | from Node.js. It uses the  10 | a(href='https://github.com/thingdom/node-neo4j', target='_blank') node-neo4j 11 | | library, available on npm as  12 | code neo4j 13 | . 14 | 15 | p. 16 | This app is a simple social network manager: it lets you add and remove 17 | users and "follows" relationships between them. 18 | 19 | p 20 | strong 21 | | Get started:  22 | a(href='/users') View all users -------------------------------------------------------------------------------- /api/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype 5 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | 9 | // GitHub ribbon! via https://github.com/blog/273-github-ribbons 10 | a(href='https://github.com/aseemk/node-neo4j-template') 11 | img( 12 | style='position: absolute; top: 0; right: 0; border: 0;' 13 | src='https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png' 14 | alt='Fork me on GitHub' 15 | ) -------------------------------------------------------------------------------- /api/views/user.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1 #{user.name} 5 | 6 | p 7 | a(href='/users') Back to everyone 8 | 9 | if following.length 10 | p #{user.name} is following #{following.length} users: 11 | // TODO should say 'user' if only one! ;) 12 | ul.users 13 | for other in following 14 | li.user 15 | form(action='/users/#{user.id}/unfollow', method='POST') 16 | a(href='/users/#{other.id}') #{other.name} 17 | input(type='hidden', name='user[id]', value='#{other.id}') 18 | input(type='submit', class='unfollow', value='x') 19 | else 20 | p #{user.name} isn't following anyone currently. 21 | 22 | if others.length 23 | form(action='/users/#{user.id}/follow', method='POST') 24 | p Add someone for #{user.name} to follow: 25 | label 26 | select(name='user[id]', required) 27 | option(value='') 28 | for user in others 29 | option(value='#{user.id}') #{user.name} 30 | input(type='submit', value='Follow') 31 | else 32 | p There's no one else left for #{user.name} to follow! 33 | 34 | form(action='/users/#{user.id}', method='POST') 35 | p Edit this user: 36 | input(type='text', name='name', placeholder='#{user.name}', required) 37 | input(type='submit', value='Update') 38 | 39 | form(action='/users/#{user.id}', method='POST', onsubmit='return confirm("Are you sure?");') 40 | p And if you're feeling destructive… 41 | input(type='hidden', name='_method', value='DELETE') 42 | input(type='submit', value='Delete User') -------------------------------------------------------------------------------- /api/views/users.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1 Users 5 | 6 | if users.length 7 | p Here are the current users: 8 | ul.users 9 | for user in users 10 | li.user 11 | a(href='/users/#{user.id}') #{user.name} 12 | else 13 | p There are no users currently. 14 | 15 | form(action='', method='POST') 16 | p Create a new user: 17 | input(type='text', name='name', placeholder='Name', required) 18 | input(type='submit', value='Create') 19 | -------------------------------------------------------------------------------- /flask-api/.env: -------------------------------------------------------------------------------- 1 | SECRET_KEY="super secret guy" 2 | MOVIE_DATABASE_USERNAME="neo4j" 3 | MOVIE_DATABASE_PASSWORD="pan-track-orders" 4 | MOVIE_DATABASE_URL="bolt://localhost" 5 | -------------------------------------------------------------------------------- /flask-api/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | -------------------------------------------------------------------------------- /flask-api/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app:app 2 | -------------------------------------------------------------------------------- /flask-api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/__init__.py -------------------------------------------------------------------------------- /flask-api/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.2 2 | blinker==1.4 3 | Flask-RESTful==0.3.8 4 | Flask-Cors==3.0.9 5 | flask-restful-swagger-2==0.35 6 | Flask-JSON==0.3.4 7 | neo4j==4.1.1 8 | gunicorn==19.10.0 9 | python-dotenv==0.14.0 10 | -------------------------------------------------------------------------------- /flask-api/swaggerui/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ 2 | html, 3 | body, 4 | div, 5 | span, 6 | applet, 7 | object, 8 | iframe, 9 | h1, 10 | h2, 11 | h3, 12 | h4, 13 | h5, 14 | h6, 15 | p, 16 | blockquote, 17 | pre, 18 | a, 19 | abbr, 20 | acronym, 21 | address, 22 | big, 23 | cite, 24 | code, 25 | del, 26 | dfn, 27 | em, 28 | img, 29 | ins, 30 | kbd, 31 | q, 32 | s, 33 | samp, 34 | small, 35 | strike, 36 | strong, 37 | sub, 38 | sup, 39 | tt, 40 | var, 41 | b, 42 | u, 43 | i, 44 | center, 45 | dl, 46 | dt, 47 | dd, 48 | ol, 49 | ul, 50 | li, 51 | fieldset, 52 | form, 53 | label, 54 | legend, 55 | table, 56 | caption, 57 | tbody, 58 | tfoot, 59 | thead, 60 | tr, 61 | th, 62 | td, 63 | article, 64 | aside, 65 | canvas, 66 | details, 67 | embed, 68 | figure, 69 | figcaption, 70 | footer, 71 | header, 72 | hgroup, 73 | menu, 74 | nav, 75 | output, 76 | ruby, 77 | section, 78 | summary, 79 | time, 80 | mark, 81 | audio, 82 | video { 83 | margin: 0; 84 | padding: 0; 85 | border: 0; 86 | font-size: 100%; 87 | font: inherit; 88 | vertical-align: baseline; 89 | } 90 | /* HTML5 display-role reset for older browsers */ 91 | article, 92 | aside, 93 | details, 94 | figcaption, 95 | figure, 96 | footer, 97 | header, 98 | hgroup, 99 | menu, 100 | nav, 101 | section { 102 | display: block; 103 | } 104 | body { 105 | line-height: 1; 106 | } 107 | ol, 108 | ul { 109 | list-style: none; 110 | } 111 | blockquote, 112 | q { 113 | quotes: none; 114 | } 115 | blockquote:before, 116 | blockquote:after, 117 | q:before, 118 | q:after { 119 | content: ''; 120 | content: none; 121 | } 122 | table { 123 | border-collapse: collapse; 124 | border-spacing: 0; 125 | } 126 | -------------------------------------------------------------------------------- /flask-api/swaggerui/css/typography.css: -------------------------------------------------------------------------------- 1 | /* Google Font's Droid Sans */ 2 | @font-face { 3 | font-family: 'Droid Sans'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf'), format('truetype'); 7 | } 8 | /* Google Font's Droid Sans Bold */ 9 | @font-face { 10 | font-family: 'Droid Sans'; 11 | font-style: normal; 12 | font-weight: 700; 13 | src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf'), format('truetype'); 14 | } 15 | -------------------------------------------------------------------------------- /flask-api/swaggerui/fonts/DroidSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/fonts/DroidSans-Bold.ttf -------------------------------------------------------------------------------- /flask-api/swaggerui/fonts/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/fonts/DroidSans.ttf -------------------------------------------------------------------------------- /flask-api/swaggerui/images/collapse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/collapse.gif -------------------------------------------------------------------------------- /flask-api/swaggerui/images/expand.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/expand.gif -------------------------------------------------------------------------------- /flask-api/swaggerui/images/explorer_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/explorer_icons.png -------------------------------------------------------------------------------- /flask-api/swaggerui/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/favicon-16x16.png -------------------------------------------------------------------------------- /flask-api/swaggerui/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/favicon-32x32.png -------------------------------------------------------------------------------- /flask-api/swaggerui/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/favicon.ico -------------------------------------------------------------------------------- /flask-api/swaggerui/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/logo_small.png -------------------------------------------------------------------------------- /flask-api/swaggerui/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/pet_store_api.png -------------------------------------------------------------------------------- /flask-api/swaggerui/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/throbber.gif -------------------------------------------------------------------------------- /flask-api/swaggerui/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/flask-api/swaggerui/images/wordnik_api.png -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/ca.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Advertència: Obsolet", 6 | "Implementation Notes":"Notes d'implementació", 7 | "Response Class":"Classe de la Resposta", 8 | "Status":"Estatus", 9 | "Parameters":"Paràmetres", 10 | "Parameter":"Paràmetre", 11 | "Value":"Valor", 12 | "Description":"Descripció", 13 | "Parameter Type":"Tipus del Paràmetre", 14 | "Data Type":"Tipus de la Dada", 15 | "Response Messages":"Missatges de la Resposta", 16 | "HTTP Status Code":"Codi d'Estatus HTTP", 17 | "Reason":"Raó", 18 | "Response Model":"Model de la Resposta", 19 | "Request URL":"URL de la Sol·licitud", 20 | "Response Body":"Cos de la Resposta", 21 | "Response Code":"Codi de la Resposta", 22 | "Response Headers":"Capçaleres de la Resposta", 23 | "Hide Response":"Amagar Resposta", 24 | "Try it out!":"Prova-ho!", 25 | "Show/Hide":"Mostrar/Amagar", 26 | "List Operations":"Llista Operacions", 27 | "Expand Operations":"Expandir Operacions", 28 | "Raw":"Cru", 29 | "can't parse JSON. Raw result":"no puc analitzar el JSON. Resultat cru", 30 | "Example Value":"Valor d'Exemple", 31 | "Model Schema":"Esquema del Model", 32 | "Model":"Model", 33 | "apply":"aplicar", 34 | "Username":"Nom d'usuari", 35 | "Password":"Contrasenya", 36 | "Terms of service":"Termes del servei", 37 | "Created by":"Creat per", 38 | "See more at":"Veure més en", 39 | "Contact the developer":"Contactar amb el desenvolupador", 40 | "api version":"versió de la api", 41 | "Response Content Type":"Tipus de Contingut de la Resposta", 42 | "fetching resource":"recollint recurs", 43 | "fetching resource list":"recollins llista de recursos", 44 | "Explore":"Explorant", 45 | "Show Swagger Petstore Example Apis":"Mostrar API d'Exemple Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"No es pot llegir del servidor. Potser no teniu la configuració de control d'accés apropiada.", 47 | "Please specify the protocol for":"Si us plau, especifiqueu el protocol per a", 48 | "Can't read swagger JSON from":"No es pot llegir el JSON de swagger des de", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Finalitzada la càrrega del recurs informatiu. Renderitzant Swagger UI", 50 | "Unable to read api":"No es pot llegir l'api", 51 | "from path":"des de la ruta", 52 | "server returned":"el servidor ha retornat" 53 | }); 54 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/en.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Warning: Deprecated", 6 | "Implementation Notes":"Implementation Notes", 7 | "Response Class":"Response Class", 8 | "Status":"Status", 9 | "Parameters":"Parameters", 10 | "Parameter":"Parameter", 11 | "Value":"Value", 12 | "Description":"Description", 13 | "Parameter Type":"Parameter Type", 14 | "Data Type":"Data Type", 15 | "Response Messages":"Response Messages", 16 | "HTTP Status Code":"HTTP Status Code", 17 | "Reason":"Reason", 18 | "Response Model":"Response Model", 19 | "Request URL":"Request URL", 20 | "Response Body":"Response Body", 21 | "Response Code":"Response Code", 22 | "Response Headers":"Response Headers", 23 | "Hide Response":"Hide Response", 24 | "Headers":"Headers", 25 | "Try it out!":"Try it out!", 26 | "Show/Hide":"Show/Hide", 27 | "List Operations":"List Operations", 28 | "Expand Operations":"Expand Operations", 29 | "Raw":"Raw", 30 | "can't parse JSON. Raw result":"can't parse JSON. Raw result", 31 | "Example Value":"Example Value", 32 | "Model Schema":"Model Schema", 33 | "Model":"Model", 34 | "Click to set as parameter value":"Click to set as parameter value", 35 | "apply":"apply", 36 | "Username":"Username", 37 | "Password":"Password", 38 | "Terms of service":"Terms of service", 39 | "Created by":"Created by", 40 | "See more at":"See more at", 41 | "Contact the developer":"Contact the developer", 42 | "api version":"api version", 43 | "Response Content Type":"Response Content Type", 44 | "Parameter content type:":"Parameter content type:", 45 | "fetching resource":"fetching resource", 46 | "fetching resource list":"fetching resource list", 47 | "Explore":"Explore", 48 | "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Can't read from server. It may not have the appropriate access-control-origin settings.", 50 | "Please specify the protocol for":"Please specify the protocol for", 51 | "Can't read swagger JSON from":"Can't read swagger JSON from", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"Finished Loading Resource Information. Rendering Swagger UI", 53 | "Unable to read api":"Unable to read api", 54 | "from path":"from path", 55 | "server returned":"server returned" 56 | }); 57 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/es.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Advertencia: Obsoleto", 6 | "Implementation Notes":"Notas de implementación", 7 | "Response Class":"Clase de la Respuesta", 8 | "Status":"Status", 9 | "Parameters":"Parámetros", 10 | "Parameter":"Parámetro", 11 | "Value":"Valor", 12 | "Description":"Descripción", 13 | "Parameter Type":"Tipo del Parámetro", 14 | "Data Type":"Tipo del Dato", 15 | "Response Messages":"Mensajes de la Respuesta", 16 | "HTTP Status Code":"Código de Status HTTP", 17 | "Reason":"Razón", 18 | "Response Model":"Modelo de la Respuesta", 19 | "Request URL":"URL de la Solicitud", 20 | "Response Body":"Cuerpo de la Respuesta", 21 | "Response Code":"Código de la Respuesta", 22 | "Response Headers":"Encabezados de la Respuesta", 23 | "Hide Response":"Ocultar Respuesta", 24 | "Try it out!":"Pruébalo!", 25 | "Show/Hide":"Mostrar/Ocultar", 26 | "List Operations":"Listar Operaciones", 27 | "Expand Operations":"Expandir Operaciones", 28 | "Raw":"Crudo", 29 | "can't parse JSON. Raw result":"no puede parsear el JSON. Resultado crudo", 30 | "Example Value":"Valor de Ejemplo", 31 | "Model Schema":"Esquema del Modelo", 32 | "Model":"Modelo", 33 | "apply":"aplicar", 34 | "Username":"Nombre de usuario", 35 | "Password":"Contraseña", 36 | "Terms of service":"Términos de Servicio", 37 | "Created by":"Creado por", 38 | "See more at":"Ver más en", 39 | "Contact the developer":"Contactar al desarrollador", 40 | "api version":"versión de la api", 41 | "Response Content Type":"Tipo de Contenido (Content Type) de la Respuesta", 42 | "fetching resource":"buscando recurso", 43 | "fetching resource list":"buscando lista del recurso", 44 | "Explore":"Explorar", 45 | "Show Swagger Petstore Example Apis":"Mostrar Api Ejemplo de Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"No se puede leer del servidor. Tal vez no tiene la configuración de control de acceso de origen (access-control-origin) apropiado.", 47 | "Please specify the protocol for":"Por favor, especificar el protocola para", 48 | "Can't read swagger JSON from":"No se puede leer el JSON de swagger desde", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Finalizada la carga del recurso de Información. Mostrando Swagger UI", 50 | "Unable to read api":"No se puede leer la api", 51 | "from path":"desde ruta", 52 | "server returned":"el servidor retornó" 53 | }); 54 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/fr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Avertissement : Obsolète", 6 | "Implementation Notes":"Notes d'implémentation", 7 | "Response Class":"Classe de la réponse", 8 | "Status":"Statut", 9 | "Parameters":"Paramètres", 10 | "Parameter":"Paramètre", 11 | "Value":"Valeur", 12 | "Description":"Description", 13 | "Parameter Type":"Type du paramètre", 14 | "Data Type":"Type de données", 15 | "Response Messages":"Messages de la réponse", 16 | "HTTP Status Code":"Code de statut HTTP", 17 | "Reason":"Raison", 18 | "Response Model":"Modèle de réponse", 19 | "Request URL":"URL appelée", 20 | "Response Body":"Corps de la réponse", 21 | "Response Code":"Code de la réponse", 22 | "Response Headers":"En-têtes de la réponse", 23 | "Hide Response":"Cacher la réponse", 24 | "Headers":"En-têtes", 25 | "Try it out!":"Testez !", 26 | "Show/Hide":"Afficher/Masquer", 27 | "List Operations":"Liste des opérations", 28 | "Expand Operations":"Développer les opérations", 29 | "Raw":"Brut", 30 | "can't parse JSON. Raw result":"impossible de décoder le JSON. Résultat brut", 31 | "Example Value":"Exemple la valeur", 32 | "Model Schema":"Définition du modèle", 33 | "Model":"Modèle", 34 | "apply":"appliquer", 35 | "Username":"Nom d'utilisateur", 36 | "Password":"Mot de passe", 37 | "Terms of service":"Conditions de service", 38 | "Created by":"Créé par", 39 | "See more at":"Voir plus sur", 40 | "Contact the developer":"Contacter le développeur", 41 | "api version":"version de l'api", 42 | "Response Content Type":"Content Type de la réponse", 43 | "fetching resource":"récupération de la ressource", 44 | "fetching resource list":"récupération de la liste de ressources", 45 | "Explore":"Explorer", 46 | "Show Swagger Petstore Example Apis":"Montrer les Apis de l'exemple Petstore de Swagger", 47 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Impossible de lire à partir du serveur. Il se peut que les réglages access-control-origin ne soient pas appropriés.", 48 | "Please specify the protocol for":"Veuillez spécifier un protocole pour", 49 | "Can't read swagger JSON from":"Impossible de lire le JSON swagger à partir de", 50 | "Finished Loading Resource Information. Rendering Swagger UI":"Chargement des informations terminé. Affichage de Swagger UI", 51 | "Unable to read api":"Impossible de lire l'api", 52 | "from path":"à partir du chemin", 53 | "server returned":"réponse du serveur" 54 | }); 55 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/geo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"ყურადღება: აღარ გამოიყენება", 6 | "Implementation Notes":"იმპლემენტაციის აღწერა", 7 | "Response Class":"რესპონს კლასი", 8 | "Status":"სტატუსი", 9 | "Parameters":"პარამეტრები", 10 | "Parameter":"პარამეტრი", 11 | "Value":"მნიშვნელობა", 12 | "Description":"აღწერა", 13 | "Parameter Type":"პარამეტრის ტიპი", 14 | "Data Type":"მონაცემის ტიპი", 15 | "Response Messages":"პასუხი", 16 | "HTTP Status Code":"HTTP სტატუსი", 17 | "Reason":"მიზეზი", 18 | "Response Model":"რესპონს მოდელი", 19 | "Request URL":"მოთხოვნის URL", 20 | "Response Body":"პასუხის სხეული", 21 | "Response Code":"პასუხის კოდი", 22 | "Response Headers":"პასუხის ჰედერები", 23 | "Hide Response":"დამალე პასუხი", 24 | "Headers":"ჰედერები", 25 | "Try it out!":"ცადე !", 26 | "Show/Hide":"გამოჩენა/დამალვა", 27 | "List Operations":"ოპერაციების სია", 28 | "Expand Operations":"ოპერაციები ვრცლად", 29 | "Raw":"ნედლი", 30 | "can't parse JSON. Raw result":"JSON-ის დამუშავება ვერ მოხერხდა. ნედლი პასუხი", 31 | "Example Value":"მაგალითი", 32 | "Model Schema":"მოდელის სტრუქტურა", 33 | "Model":"მოდელი", 34 | "Click to set as parameter value":"პარამეტრისთვის მნიშვნელობის მისანიჭებლად, დააკლიკე", 35 | "apply":"გამოყენება", 36 | "Username":"მოხმარებელი", 37 | "Password":"პაროლი", 38 | "Terms of service":"მომსახურების პირობები", 39 | "Created by":"შექმნა", 40 | "See more at":"ნახე ვრცლად", 41 | "Contact the developer":"დაუკავშირდი დეველოპერს", 42 | "api version":"api ვერსია", 43 | "Response Content Type":"პასუხის კონტენტის ტიპი", 44 | "Parameter content type:":"პარამეტრის კონტენტის ტიპი:", 45 | "fetching resource":"რესურსების მიღება", 46 | "fetching resource list":"რესურსების სიის მიღება", 47 | "Explore":"ნახვა", 48 | "Show Swagger Petstore Example Apis":"ნახე Swagger Petstore სამაგალითო Api", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"სერვერთან დაკავშირება ვერ ხერხდება. შეამოწმეთ access-control-origin.", 50 | "Please specify the protocol for":"მიუთითეთ პროტოკოლი", 51 | "Can't read swagger JSON from":"swagger JSON წაკითხვა ვერ მოხერხდა", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"რესურსების ჩატვირთვა სრულდება. Swagger UI რენდერდება", 53 | "Unable to read api":"api წაკითხვა ვერ მოხერხდა", 54 | "from path":"მისამართიდან", 55 | "server returned":"სერვერმა დააბრუნა" 56 | }); 57 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/it.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Attenzione: Deprecato", 6 | "Implementation Notes":"Note di implementazione", 7 | "Response Class":"Classe della risposta", 8 | "Status":"Stato", 9 | "Parameters":"Parametri", 10 | "Parameter":"Parametro", 11 | "Value":"Valore", 12 | "Description":"Descrizione", 13 | "Parameter Type":"Tipo di parametro", 14 | "Data Type":"Tipo di dato", 15 | "Response Messages":"Messaggi della risposta", 16 | "HTTP Status Code":"Codice stato HTTP", 17 | "Reason":"Motivo", 18 | "Response Model":"Modello di risposta", 19 | "Request URL":"URL della richiesta", 20 | "Response Body":"Corpo della risposta", 21 | "Response Code":"Oggetto della risposta", 22 | "Response Headers":"Intestazioni della risposta", 23 | "Hide Response":"Nascondi risposta", 24 | "Try it out!":"Provalo!", 25 | "Show/Hide":"Mostra/Nascondi", 26 | "List Operations":"Mostra operazioni", 27 | "Expand Operations":"Espandi operazioni", 28 | "Raw":"Grezzo (raw)", 29 | "can't parse JSON. Raw result":"non è possibile parsare il JSON. Risultato grezzo (raw).", 30 | "Model Schema":"Schema del modello", 31 | "Model":"Modello", 32 | "apply":"applica", 33 | "Username":"Nome utente", 34 | "Password":"Password", 35 | "Terms of service":"Condizioni del servizio", 36 | "Created by":"Creato da", 37 | "See more at":"Informazioni aggiuntive:", 38 | "Contact the developer":"Contatta lo sviluppatore", 39 | "api version":"versione api", 40 | "Response Content Type":"Tipo di contenuto (content type) della risposta", 41 | "fetching resource":"recuperando la risorsa", 42 | "fetching resource list":"recuperando lista risorse", 43 | "Explore":"Esplora", 44 | "Show Swagger Petstore Example Apis":"Mostra le api di esempio di Swagger Petstore", 45 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Non è possibile leggere dal server. Potrebbe non avere le impostazioni di controllo accesso origine (access-control-origin) appropriate.", 46 | "Please specify the protocol for":"Si prega di specificare il protocollo per", 47 | "Can't read swagger JSON from":"Impossibile leggere JSON swagger da:", 48 | "Finished Loading Resource Information. Rendering Swagger UI":"Lettura informazioni risorse termianta. Swagger UI viene mostrata", 49 | "Unable to read api":"Impossibile leggere la api", 50 | "from path":"da cartella", 51 | "server returned":"il server ha restituito" 52 | }); 53 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/ja.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"警告: 廃止予定", 6 | "Implementation Notes":"実装メモ", 7 | "Response Class":"レスポンスクラス", 8 | "Status":"ステータス", 9 | "Parameters":"パラメータ群", 10 | "Parameter":"パラメータ", 11 | "Value":"値", 12 | "Description":"説明", 13 | "Parameter Type":"パラメータタイプ", 14 | "Data Type":"データタイプ", 15 | "Response Messages":"レスポンスメッセージ", 16 | "HTTP Status Code":"HTTPステータスコード", 17 | "Reason":"理由", 18 | "Response Model":"レスポンスモデル", 19 | "Request URL":"リクエストURL", 20 | "Response Body":"レスポンスボディ", 21 | "Response Code":"レスポンスコード", 22 | "Response Headers":"レスポンスヘッダ", 23 | "Hide Response":"レスポンスを隠す", 24 | "Headers":"ヘッダ", 25 | "Try it out!":"実際に実行!", 26 | "Show/Hide":"表示/非表示", 27 | "List Operations":"操作一覧", 28 | "Expand Operations":"操作の展開", 29 | "Raw":"Raw", 30 | "can't parse JSON. Raw result":"JSONへ解釈できません. 未加工の結果", 31 | "Model Schema":"モデルスキーマ", 32 | "Model":"モデル", 33 | "apply":"実行", 34 | "Username":"ユーザ名", 35 | "Password":"パスワード", 36 | "Terms of service":"サービス利用規約", 37 | "Created by":"Created by", 38 | "See more at":"See more at", 39 | "Contact the developer":"開発者に連絡", 40 | "api version":"APIバージョン", 41 | "Response Content Type":"レスポンス コンテンツタイプ", 42 | "fetching resource":"リソースの取得", 43 | "fetching resource list":"リソース一覧の取得", 44 | "Explore":"Explore", 45 | "Show Swagger Petstore Example Apis":"SwaggerペットストアAPIの表示", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"サーバから読み込めません. 適切なaccess-control-origin設定を持っていない可能性があります.", 47 | "Please specify the protocol for":"プロトコルを指定してください", 48 | "Can't read swagger JSON from":"次からswagger JSONを読み込めません", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"リソース情報の読み込みが完了しました. Swagger UIを描画しています", 50 | "Unable to read api":"APIを読み込めません", 51 | "from path":"次のパスから", 52 | "server returned":"サーバからの返答" 53 | }); 54 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/ko-kr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"경고:폐기예정됨", 6 | "Implementation Notes":"구현 노트", 7 | "Response Class":"응답 클래스", 8 | "Status":"상태", 9 | "Parameters":"매개변수들", 10 | "Parameter":"매개변수", 11 | "Value":"값", 12 | "Description":"설명", 13 | "Parameter Type":"매개변수 타입", 14 | "Data Type":"데이터 타입", 15 | "Response Messages":"응답 메세지", 16 | "HTTP Status Code":"HTTP 상태 코드", 17 | "Reason":"원인", 18 | "Response Model":"응답 모델", 19 | "Request URL":"요청 URL", 20 | "Response Body":"응답 본문", 21 | "Response Code":"응답 코드", 22 | "Response Headers":"응답 헤더", 23 | "Hide Response":"응답 숨기기", 24 | "Headers":"헤더", 25 | "Try it out!":"써보기!", 26 | "Show/Hide":"보이기/숨기기", 27 | "List Operations":"목록 작업", 28 | "Expand Operations":"전개 작업", 29 | "Raw":"원본", 30 | "can't parse JSON. Raw result":"JSON을 파싱할수 없음. 원본결과:", 31 | "Model Schema":"모델 스키마", 32 | "Model":"모델", 33 | "apply":"적용", 34 | "Username":"사용자 이름", 35 | "Password":"암호", 36 | "Terms of service":"이용약관", 37 | "Created by":"작성자", 38 | "See more at":"추가정보:", 39 | "Contact the developer":"개발자에게 문의", 40 | "api version":"api버전", 41 | "Response Content Type":"응답Content Type", 42 | "fetching resource":"리소스 가져오기", 43 | "fetching resource list":"리소스 목록 가져오기", 44 | "Explore":"탐색", 45 | "Show Swagger Petstore Example Apis":"Swagger Petstore 예제 보기", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"서버로부터 읽어들일수 없습니다. access-control-origin 설정이 올바르지 않을수 있습니다.", 47 | "Please specify the protocol for":"다음을 위한 프로토콜을 정하세요", 48 | "Can't read swagger JSON from":"swagger JSON 을 다음으로 부터 읽을수 없습니다", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"리소스 정보 불러오기 완료. Swagger UI 랜더링", 50 | "Unable to read api":"api를 읽을 수 없습니다.", 51 | "from path":"다음 경로로 부터", 52 | "server returned":"서버 응답함." 53 | }); 54 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/pl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Uwaga: Wycofane", 6 | "Implementation Notes":"Uwagi Implementacji", 7 | "Response Class":"Klasa Odpowiedzi", 8 | "Status":"Status", 9 | "Parameters":"Parametry", 10 | "Parameter":"Parametr", 11 | "Value":"Wartość", 12 | "Description":"Opis", 13 | "Parameter Type":"Typ Parametru", 14 | "Data Type":"Typ Danych", 15 | "Response Messages":"Wiadomości Odpowiedzi", 16 | "HTTP Status Code":"Kod Statusu HTTP", 17 | "Reason":"Przyczyna", 18 | "Response Model":"Model Odpowiedzi", 19 | "Request URL":"URL Wywołania", 20 | "Response Body":"Treść Odpowiedzi", 21 | "Response Code":"Kod Odpowiedzi", 22 | "Response Headers":"Nagłówki Odpowiedzi", 23 | "Hide Response":"Ukryj Odpowiedź", 24 | "Headers":"Nagłówki", 25 | "Try it out!":"Wypróbuj!", 26 | "Show/Hide":"Pokaż/Ukryj", 27 | "List Operations":"Lista Operacji", 28 | "Expand Operations":"Rozwiń Operacje", 29 | "Raw":"Nieprzetworzone", 30 | "can't parse JSON. Raw result":"nie można przetworzyć pliku JSON. Nieprzetworzone dane", 31 | "Model Schema":"Schemat Modelu", 32 | "Model":"Model", 33 | "apply":"użyj", 34 | "Username":"Nazwa użytkownika", 35 | "Password":"Hasło", 36 | "Terms of service":"Warunki używania", 37 | "Created by":"Utworzone przez", 38 | "See more at":"Zobacz więcej na", 39 | "Contact the developer":"Kontakt z deweloperem", 40 | "api version":"wersja api", 41 | "Response Content Type":"Typ Zasobu Odpowiedzi", 42 | "fetching resource":"ładowanie zasobu", 43 | "fetching resource list":"ładowanie listy zasobów", 44 | "Explore":"Eksploruj", 45 | "Show Swagger Petstore Example Apis":"Pokaż Przykładowe Api Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Brak połączenia z serwerem. Może on nie mieć odpowiednich ustawień access-control-origin.", 47 | "Please specify the protocol for":"Proszę podać protokół dla", 48 | "Can't read swagger JSON from":"Nie można odczytać swagger JSON z", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Ukończono Ładowanie Informacji o Zasobie. Renderowanie Swagger UI", 50 | "Unable to read api":"Nie można odczytać api", 51 | "from path":"ze ścieżki", 52 | "server returned":"serwer zwrócił" 53 | }); 54 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/pt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Aviso: Depreciado", 6 | "Implementation Notes":"Notas de Implementação", 7 | "Response Class":"Classe de resposta", 8 | "Status":"Status", 9 | "Parameters":"Parâmetros", 10 | "Parameter":"Parâmetro", 11 | "Value":"Valor", 12 | "Description":"Descrição", 13 | "Parameter Type":"Tipo de parâmetro", 14 | "Data Type":"Tipo de dados", 15 | "Response Messages":"Mensagens de resposta", 16 | "HTTP Status Code":"Código de status HTTP", 17 | "Reason":"Razão", 18 | "Response Model":"Modelo resposta", 19 | "Request URL":"URL requisição", 20 | "Response Body":"Corpo da resposta", 21 | "Response Code":"Código da resposta", 22 | "Response Headers":"Cabeçalho da resposta", 23 | "Headers":"Cabeçalhos", 24 | "Hide Response":"Esconder resposta", 25 | "Try it out!":"Tente agora!", 26 | "Show/Hide":"Mostrar/Esconder", 27 | "List Operations":"Listar operações", 28 | "Expand Operations":"Expandir operações", 29 | "Raw":"Cru", 30 | "can't parse JSON. Raw result":"Falha ao analisar JSON. Resulto cru", 31 | "Model Schema":"Modelo esquema", 32 | "Model":"Modelo", 33 | "apply":"Aplicar", 34 | "Username":"Usuário", 35 | "Password":"Senha", 36 | "Terms of service":"Termos do serviço", 37 | "Created by":"Criado por", 38 | "See more at":"Veja mais em", 39 | "Contact the developer":"Contate o desenvolvedor", 40 | "api version":"Versão api", 41 | "Response Content Type":"Tipo de conteúdo da resposta", 42 | "fetching resource":"busca recurso", 43 | "fetching resource list":"buscando lista de recursos", 44 | "Explore":"Explorar", 45 | "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Não é possível ler do servidor. Pode não ter as apropriadas configurações access-control-origin", 47 | "Please specify the protocol for":"Por favor especifique o protocolo", 48 | "Can't read swagger JSON from":"Não é possível ler o JSON Swagger de", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Carregar informação de recurso finalizada. Renderizando Swagger UI", 50 | "Unable to read api":"Não foi possível ler api", 51 | "from path":"do caminho", 52 | "server returned":"servidor retornou" 53 | }); 54 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/ru.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Предупреждение: Устарело", 6 | "Implementation Notes":"Заметки", 7 | "Response Class":"Пример ответа", 8 | "Status":"Статус", 9 | "Parameters":"Параметры", 10 | "Parameter":"Параметр", 11 | "Value":"Значение", 12 | "Description":"Описание", 13 | "Parameter Type":"Тип параметра", 14 | "Data Type":"Тип данных", 15 | "HTTP Status Code":"HTTP код", 16 | "Reason":"Причина", 17 | "Response Model":"Структура ответа", 18 | "Request URL":"URL запроса", 19 | "Response Body":"Тело ответа", 20 | "Response Code":"HTTP код ответа", 21 | "Response Headers":"Заголовки ответа", 22 | "Hide Response":"Спрятать ответ", 23 | "Headers":"Заголовки", 24 | "Response Messages":"Что может прийти в ответ", 25 | "Try it out!":"Попробовать!", 26 | "Show/Hide":"Показать/Скрыть", 27 | "List Operations":"Операции кратко", 28 | "Expand Operations":"Операции подробно", 29 | "Raw":"В сыром виде", 30 | "can't parse JSON. Raw result":"Не удается распарсить ответ:", 31 | "Example Value":"Пример", 32 | "Model Schema":"Структура", 33 | "Model":"Описание", 34 | "Click to set as parameter value":"Нажмите, чтобы испльзовать в качестве значения параметра", 35 | "apply":"применить", 36 | "Username":"Имя пользователя", 37 | "Password":"Пароль", 38 | "Terms of service":"Условия использования", 39 | "Created by":"Разработано", 40 | "See more at":"Еще тут", 41 | "Contact the developer":"Связаться с разработчиком", 42 | "api version":"Версия API", 43 | "Response Content Type":"Content Type ответа", 44 | "Parameter content type:":"Content Type параметра:", 45 | "fetching resource":"Получение ресурса", 46 | "fetching resource list":"Получение ресурсов", 47 | "Explore":"Показать", 48 | "Show Swagger Petstore Example Apis":"Показать примеры АПИ", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Не удается получить ответ от сервера. Возможно, проблема с настройками доступа", 50 | "Please specify the protocol for":"Пожалуйста, укажите протокол для", 51 | "Can't read swagger JSON from":"Не получается прочитать swagger json из", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"Загрузка информации о ресурсах завершена. Рендерим", 53 | "Unable to read api":"Не удалось прочитать api", 54 | "from path":"по адресу", 55 | "server returned":"сервер сказал" 56 | }); 57 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/tr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Uyarı: Deprecated", 6 | "Implementation Notes":"Gerçekleştirim Notları", 7 | "Response Class":"Dönen Sınıf", 8 | "Status":"Statü", 9 | "Parameters":"Parametreler", 10 | "Parameter":"Parametre", 11 | "Value":"Değer", 12 | "Description":"Açıklama", 13 | "Parameter Type":"Parametre Tipi", 14 | "Data Type":"Veri Tipi", 15 | "Response Messages":"Dönüş Mesajı", 16 | "HTTP Status Code":"HTTP Statü Kodu", 17 | "Reason":"Gerekçe", 18 | "Response Model":"Dönüş Modeli", 19 | "Request URL":"İstek URL", 20 | "Response Body":"Dönüş İçeriği", 21 | "Response Code":"Dönüş Kodu", 22 | "Response Headers":"Dönüş Üst Bilgileri", 23 | "Hide Response":"Dönüşü Gizle", 24 | "Headers":"Üst Bilgiler", 25 | "Try it out!":"Dene!", 26 | "Show/Hide":"Göster/Gizle", 27 | "List Operations":"Operasyonları Listele", 28 | "Expand Operations":"Operasyonları Aç", 29 | "Raw":"Ham", 30 | "can't parse JSON. Raw result":"JSON çözümlenemiyor. Ham sonuç", 31 | "Model Schema":"Model Şema", 32 | "Model":"Model", 33 | "apply":"uygula", 34 | "Username":"Kullanıcı Adı", 35 | "Password":"Parola", 36 | "Terms of service":"Servis şartları", 37 | "Created by":"Oluşturan", 38 | "See more at":"Daha fazlası için", 39 | "Contact the developer":"Geliştirici ile İletişime Geçin", 40 | "api version":"api versiyon", 41 | "Response Content Type":"Dönüş İçerik Tipi", 42 | "fetching resource":"kaynak getiriliyor", 43 | "fetching resource list":"kaynak listesi getiriliyor", 44 | "Explore":"Keşfet", 45 | "Show Swagger Petstore Example Apis":"Swagger Petstore Örnek Api'yi Gör", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Sunucudan okuma yapılamıyor. Sunucu access-control-origin ayarlarınızı kontrol edin.", 47 | "Please specify the protocol for":"Lütfen istenen adres için protokol belirtiniz", 48 | "Can't read swagger JSON from":"Swagger JSON bu kaynaktan okunamıyor", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Kaynak baglantısı tamamlandı. Swagger UI gösterime hazırlanıyor", 50 | "Unable to read api":"api okunamadı", 51 | "from path":"yoldan", 52 | "server returned":"sunucuya dönüldü" 53 | }); 54 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/translator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Translator for documentation pages. 5 | * 6 | * To enable translation you should include one of language-files in your index.html 7 | * after . 8 | * For example - 9 | * 10 | * If you wish to translate some new texts you should do two things: 11 | * 1. Add a new phrase pair ("New Phrase": "New Translation") into your language file (for example lang/ru.js). It will be great if you add it in other language files too. 12 | * 2. Mark that text it templates this way New Phrase or . 13 | * The main thing here is attribute data-sw-translate. Only inner html, title-attribute and value-attribute are going to translate. 14 | * 15 | */ 16 | window.SwaggerTranslator = { 17 | 18 | _words:[], 19 | 20 | translate: function(sel) { 21 | var $this = this; 22 | sel = sel || '[data-sw-translate]'; 23 | 24 | $(sel).each(function() { 25 | $(this).html($this._tryTranslate($(this).html())); 26 | 27 | $(this).val($this._tryTranslate($(this).val())); 28 | $(this).attr('title', $this._tryTranslate($(this).attr('title'))); 29 | }); 30 | }, 31 | 32 | _tryTranslate: function(word) { 33 | return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word; 34 | }, 35 | 36 | learn: function(wordsMap) { 37 | this._words = wordsMap; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"警告:已过时", 6 | "Implementation Notes":"实现备注", 7 | "Response Class":"响应类", 8 | "Status":"状态", 9 | "Parameters":"参数", 10 | "Parameter":"参数", 11 | "Value":"值", 12 | "Description":"描述", 13 | "Parameter Type":"参数类型", 14 | "Data Type":"数据类型", 15 | "Response Messages":"响应消息", 16 | "HTTP Status Code":"HTTP状态码", 17 | "Reason":"原因", 18 | "Response Model":"响应模型", 19 | "Request URL":"请求URL", 20 | "Response Body":"响应体", 21 | "Response Code":"响应码", 22 | "Response Headers":"响应头", 23 | "Hide Response":"隐藏响应", 24 | "Headers":"头", 25 | "Try it out!":"试一下!", 26 | "Show/Hide":"显示/隐藏", 27 | "List Operations":"显示操作", 28 | "Expand Operations":"展开操作", 29 | "Raw":"原始", 30 | "can't parse JSON. Raw result":"无法解析JSON. 原始结果", 31 | "Model Schema":"模型架构", 32 | "Model":"模型", 33 | "apply":"应用", 34 | "Username":"用户名", 35 | "Password":"密码", 36 | "Terms of service":"服务条款", 37 | "Created by":"创建者", 38 | "See more at":"查看更多:", 39 | "Contact the developer":"联系开发者", 40 | "api version":"api版本", 41 | "Response Content Type":"响应Content Type", 42 | "fetching resource":"正在获取资源", 43 | "fetching resource list":"正在获取资源列表", 44 | "Explore":"浏览", 45 | "Show Swagger Petstore Example Apis":"显示 Swagger Petstore 示例 Apis", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"无法从服务器读取。可能没有正确设置access-control-origin。", 47 | "Please specify the protocol for":"请指定协议:", 48 | "Can't read swagger JSON from":"无法读取swagger JSON于", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"已加载资源信息。正在渲染Swagger UI", 50 | "Unable to read api":"无法读取api", 51 | "from path":"从路径", 52 | "server returned":"服务器返回" 53 | }); 54 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lib/highlight.9.1.0.pack_extended.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function () { 4 | var configure, highlightBlock; 5 | 6 | configure = hljs.configure; 7 | // "extending" hljs.configure method 8 | hljs.configure = function _configure (options) { 9 | var size = options.highlightSizeThreshold; 10 | 11 | // added highlightSizeThreshold option to set maximum size 12 | // of processed string. Set to null if not a number 13 | hljs.highlightSizeThreshold = size === +size ? size : null; 14 | 15 | configure.call(this, options); 16 | }; 17 | 18 | highlightBlock = hljs.highlightBlock; 19 | 20 | // "extending" hljs.highlightBlock method 21 | hljs.highlightBlock = function _highlightBlock (el) { 22 | var innerHTML = el.innerHTML; 23 | var size = hljs.highlightSizeThreshold; 24 | 25 | // check if highlightSizeThreshold is not set or element innerHTML 26 | // is less than set option highlightSizeThreshold 27 | if (size == null || size > innerHTML.length) { 28 | // proceed with hljs.highlightBlock 29 | highlightBlock.call(hljs, el); 30 | } 31 | }; 32 | 33 | })(); 34 | 35 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lib/jquery.slideto.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery); 2 | -------------------------------------------------------------------------------- /flask-api/swaggerui/lib/jquery.wiggle.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery Wiggle 3 | Author: WonderGroup, Jordan Thomas 4 | URL: http://labs.wondergroup.com/demos/mini-ui/index.html 5 | License: MIT (http://en.wikipedia.org/wiki/MIT_License) 6 | */ 7 | jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('
').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);} 8 | if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});}; -------------------------------------------------------------------------------- /flask-api/swaggerui/lib/object-assign-pollyfill.js: -------------------------------------------------------------------------------- 1 | if (typeof Object.assign != 'function') { 2 | (function () { 3 | Object.assign = function (target) { 4 | 'use strict'; 5 | if (target === undefined || target === null) { 6 | throw new TypeError('Cannot convert undefined or null to object'); 7 | } 8 | 9 | var output = Object(target); 10 | for (var index = 1; index < arguments.length; index++) { 11 | var source = arguments[index]; 12 | if (source !== undefined && source !== null) { 13 | for (var nextKey in source) { 14 | if (Object.prototype.hasOwnProperty.call(source, nextKey)) { 15 | output[nextKey] = source[nextKey]; 16 | } 17 | } 18 | } 19 | } 20 | return output; 21 | }; 22 | })(); 23 | } 24 | -------------------------------------------------------------------------------- /flask-api/swaggerui/o2c.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/img/model.png -------------------------------------------------------------------------------- /img/neo4jBrowser_CypherCmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/img/neo4jBrowser_CypherCmd.png -------------------------------------------------------------------------------- /img/ratings_csv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/img/ratings_csv.png -------------------------------------------------------------------------------- /img/verifyCloudAtlasMovie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/img/verifyCloudAtlasMovie.png -------------------------------------------------------------------------------- /img/webUX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/img/webUX.png -------------------------------------------------------------------------------- /import_neo4j.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | $NEO4J_HOME/bin/neo4j-import --into $NEO4J_HOME/data/databases/graph.db --nodes:Person csv/person_node.csv --nodes:Movie csv/movie_node.csv --nodes:Genre csv/genre_node.csv --nodes:Keyword csv/keyword_node.csv --relationships:ACTED_IN csv/acted_in_rels.csv --relationships:DIRECTED csv/directed_rels.csv --relationships:HAS_GENRE csv/has_genre_rels.csv --relationships:HAS_KEYWORD csv/has_keyword_rels.csv --relationships:PRODUCED csv/produced_rels.csv --relationships:WRITER_OF csv/writer_of_rels.csv --delimiter ";" --array-delimiter "|" --id-type INTEGER -------------------------------------------------------------------------------- /setup.cql: -------------------------------------------------------------------------------- 1 | // setup for movie template 2 | 3 | CREATE CONSTRAINT ON (n:Movie) ASSERT n.id IS UNIQUE; 4 | 5 | CREATE CONSTRAINT ON (n:Person) ASSERT n.id IS UNIQUE; 6 | 7 | CREATE CONSTRAINT ON (n:Keyword) ASSERT n.id IS UNIQUE; 8 | 9 | CREATE CONSTRAINT ON (n:User) ASSERT n.id IS UNIQUE; 10 | 11 | CREATE CONSTRAINT ON (n:User) ASSERT n.username IS UNIQUE; 12 | 13 | CREATE CONSTRAINT ON (n:Genre) ASSERT n.id IS UNIQUE; 14 | 15 | CREATE CONSTRAINT ON ()-[r:RATED]-() ASSERT exists(r.rating); 16 | 17 | // LOAD CSV WITH HEADERS FROM 'file:///ratings.csv' AS line 18 | // MATCH (m:Movie {id:toInt(line.movie_id)}) 19 | // MERGE (u:User {id:line.user_id, username:line.user_username}) // user ids are strings 20 | // MERGE (u)-[r:RATED]->(m) 21 | // SET r.rating = toInt(line.rating) 22 | // RETURN m.title, r.rating, u.username; 23 | -------------------------------------------------------------------------------- /web/.env: -------------------------------------------------------------------------------- 1 | # Express: http://localhost:3000/api/v0 2 | # Flask: http://localhost:5000/api/v0 3 | 4 | REACT_APP_API_BASE_URL=http://localhost:5000/api/v0 5 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /web/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/erbium 2 | -------------------------------------------------------------------------------- /web/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NT Movie APP", 3 | "env": { 4 | "API_URL": { 5 | "description": "URL of the API endpoint", 6 | "required": true 7 | } 8 | }, 9 | "buildpacks": [ 10 | { 11 | "url": "mars/create-react-app" 12 | } 13 | ] 14 | } 15 | 16 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "axios": "^0.21.2", 10 | "connected-react-router": "^6.8.0", 11 | "humps": "^2.0.1", 12 | "node-sass": "^4.14.1", 13 | "prop-types": "^15.7.2", 14 | "react": "^16.13.1", 15 | "react-dom": "^16.13.1", 16 | "react-redux": "^7.2.0", 17 | "react-router": "^5.2.0", 18 | "react-router-dom": "^5.2.0", 19 | "react-scripts": "^3.4.3", 20 | "redux": "^4.0.5", 21 | "redux-logger": "^3.0.6", 22 | "redux-saga": "^1.1.3", 23 | "redux-thunk": "^2.3.0" 24 | }, 25 | "scripts": { 26 | "start": "react-scripts start", 27 | "build": "react-scripts build", 28 | "test": "react-scripts test", 29 | "eject": "react-scripts eject" 30 | }, 31 | "eslintConfig": { 32 | "extends": "react-app" 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /web/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/web/public/logo192.png -------------------------------------------------------------------------------- /web/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/web/public/logo512.png -------------------------------------------------------------------------------- /web/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /web/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /web/src/UserSession.js: -------------------------------------------------------------------------------- 1 | class UserSession { 2 | static setToken(token) { 3 | if (token) { 4 | window.localStorage.setItem('token', token); 5 | } else { 6 | window.localStorage.removeItem('token'); 7 | } 8 | } 9 | 10 | static getToken() { 11 | return window.localStorage.getItem('token'); 12 | } 13 | } 14 | 15 | export default UserSession; 16 | -------------------------------------------------------------------------------- /web/src/api/AuthApi.js: -------------------------------------------------------------------------------- 1 | import settings from '../config/settings'; 2 | import axios from './axios'; 3 | 4 | const {apiBaseURL} = settings; 5 | 6 | export default class AuthApi { 7 | static login(username, password) { 8 | return axios.post(`${apiBaseURL}/login`, 9 | { 10 | username, password 11 | } 12 | ); 13 | } 14 | 15 | static register(profile) { 16 | return axios.post(`${apiBaseURL}/register`, profile); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /web/src/api/MoviesApi.js: -------------------------------------------------------------------------------- 1 | import settings from '../config/settings'; 2 | import axios from './axios'; 3 | import _ from 'lodash'; 4 | 5 | const {apiBaseURL} = settings; 6 | 7 | export default class MoviesApi { 8 | static getGenres() { 9 | return axios.get(`${apiBaseURL}/genres`); 10 | } 11 | 12 | static getMoviesByGenres(genreNames) { 13 | return MoviesApi.getGenres() 14 | .then(genres => { 15 | var movieGenres = _.filter(genres, g => { 16 | return genreNames.indexOf(g.name) > -1; 17 | }); 18 | 19 | return Promise.all( 20 | movieGenres.map(genre => { 21 | return axios.get(`${apiBaseURL}/movies/genre/${genre.id}/`); 22 | } 23 | )) 24 | .then(genreResults => { 25 | var result = {}; 26 | genreResults.forEach((movies, i) => { 27 | result[movieGenres[i].name] = movies; 28 | }); 29 | 30 | return result; 31 | }); 32 | }); 33 | } 34 | 35 | // convert this to top 3 most rated movies 36 | static getFeaturedMovies() { 37 | return Promise.all([ 38 | axios.get(`${apiBaseURL}/movies/13380`), 39 | axios.get(`${apiBaseURL}/movies/15292`), 40 | axios.get(`${apiBaseURL}/movies/11398`) 41 | ]); 42 | } 43 | 44 | static getMovie(id) { 45 | return axios.get(`${apiBaseURL}/movies/${id}`); 46 | } 47 | 48 | static rateMovie(id, rating) { 49 | return axios.post(`${apiBaseURL}/movies/${id}/rate`, {rating}); 50 | } 51 | 52 | static deleteRating(id) { 53 | return axios.delete(`${apiBaseURL}/movies/${id}/rate`); 54 | } 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /web/src/api/PersonApi.js: -------------------------------------------------------------------------------- 1 | import settings from '../config/settings'; 2 | import axios from './axios'; 3 | 4 | const {apiBaseURL} = settings; 5 | 6 | export default class PersonApi { 7 | static getPerson(id) { 8 | return axios.get(`${apiBaseURL}/people/${id}`); 9 | } 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /web/src/api/ProfileApi.js: -------------------------------------------------------------------------------- 1 | import settings from '../config/settings'; 2 | import axios from './axios'; 3 | 4 | const {apiBaseURL} = settings; 5 | 6 | export default class ProfileApi { 7 | 8 | static getProfile() { 9 | return axios.get(`${apiBaseURL}/users/me`); 10 | } 11 | 12 | static getProfileRatings() { 13 | return axios.get(`${apiBaseURL}/movies/rated`); 14 | } 15 | 16 | static getProfileRecommendations() { 17 | return axios.get(`${apiBaseURL}/movies/recommended`); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /web/src/api/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import humps from 'humps'; 3 | import UserSession from '../UserSession'; 4 | 5 | const instance = axios.create(); 6 | instance.defaults.headers.post['Content-Type'] = 'application/json'; 7 | instance.defaults.headers.patch['Content-Type'] = 'application/json'; 8 | 9 | instance.interceptors.request.use(function (request) { 10 | const authToken = UserSession.getToken(); 11 | if (authToken) { 12 | if (request.headers && !request.headers.Authorization) { 13 | request.headers['Authorization'] = `Token ${authToken}`; 14 | } 15 | } 16 | 17 | if (request.data) { 18 | const data = typeof request.data === "string" ? JSON.parse(request.data) : request.data 19 | request.data = JSON.stringify(humps.decamelizeKeys(data)); 20 | } 21 | return request; 22 | }); 23 | 24 | instance.interceptors.response.use(function (response) { 25 | if (response.data) { 26 | return humps.camelizeKeys(response.data); 27 | } 28 | }); 29 | 30 | export default instance; 31 | -------------------------------------------------------------------------------- /web/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/web/src/assets/favicon.ico -------------------------------------------------------------------------------- /web/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/web/src/assets/logo.png -------------------------------------------------------------------------------- /web/src/assets/neo4j_background3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/web/src/assets/neo4j_background3.gif -------------------------------------------------------------------------------- /web/src/components/Breadcrumbs.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | export default class Breadcrumbs extends React.Component { 6 | render() { 7 | var {movie, person} = this.props; 8 | 9 | return ( 10 |
    11 |
  • Home
  • 12 | { 13 | movie ? 14 |
  • {movie.title}
  • 15 | : null 16 | } 17 | { 18 | person ? 19 |
  • {person.name}
  • 20 | : null 21 | } 22 |
23 | ); 24 | } 25 | } 26 | 27 | Breadcrumbs.displayName = 'Breadcrumbs'; 28 | Breadcrumbs.propTypes = { 29 | movie: PropTypes.object, 30 | person: PropTypes.object 31 | }; 32 | -------------------------------------------------------------------------------- /web/src/components/Carousel.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Carousel extends React.Component { 4 | constructor() { 5 | super(); 6 | 7 | this.state = { 8 | startIndex: 0, 9 | visibleCount: 0 10 | }; 11 | 12 | this.onWidthChange = this.onWidthChange.bind(this); 13 | } 14 | 15 | componentDidMount() { 16 | if (matchMedia) { 17 | var mq = window.matchMedia('(max-width: 600px)'); 18 | mq.addListener(this.onWidthChange); 19 | this.onWidthChange(mq); 20 | } 21 | } 22 | 23 | onWidthChange(mq) { 24 | if (mq.matches) { 25 | this.setState({visibleCount: 2}); 26 | } else { 27 | this.setState({visibleCount: 5}); 28 | } 29 | } 30 | 31 | render() { 32 | var {children} = this.props; 33 | var {startIndex, visibleCount} = this.state; 34 | 35 | return ( 36 |
37 | 40 | 43 |
    44 | { 45 | React.Children.map(children, (c, i) => { 46 | var style = { 47 | width: (100 / visibleCount).toFixed(0) + '%', 48 | display: (i >= startIndex && i - startIndex < visibleCount) ? 'inline-block' : 'none' 49 | }; 50 | 51 | return ( 52 |
  • 53 | {c} 54 |
  • ); 55 | }) 56 | } 57 |
58 |
59 | ); 60 | } 61 | 62 | onRightClick(e) { 63 | e.preventDefault(); 64 | var {startIndex, visibleCount} = this.state; 65 | 66 | if(startIndex + visibleCount < React.Children.count(this.props.children)) { 67 | this.setState({startIndex: startIndex + 1}); 68 | } 69 | } 70 | 71 | onLeftClick(e) { 72 | e.preventDefault(); 73 | var {startIndex} = this.state; 74 | 75 | if (startIndex > 0) { 76 | this.setState({startIndex: startIndex - 1}); 77 | } 78 | } 79 | } 80 | 81 | Carousel.displayName = 'Carousel'; 82 | -------------------------------------------------------------------------------- /web/src/components/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Footer extends React.Component { 4 | render() { 5 | return ( 6 |
7 | Such Footer 8 |
9 | ); 10 | } 11 | } 12 | 13 | Footer.displayName = 'Footer'; 14 | -------------------------------------------------------------------------------- /web/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Link} from 'react-router-dom'; 3 | import {logout} from '../redux/actions/AuthActions'; 4 | import {connect} from 'react-redux'; 5 | import _ from 'lodash'; 6 | import logoImg from '../assets/logo.png'; 7 | 8 | class Header extends React.Component { 9 | render() { 10 | var {props} = this; 11 | var profile = _.get(props, 'profile'); 12 | var isLoggedIn = !!_.get(props, 'auth.token'); 13 | 14 | return ( 15 | 49 | ); 50 | } 51 | 52 | getAvatarStyle(profile) { 53 | return {background: `url(${_.get(profile, 'avatar.fullSize')}) center`}; 54 | } 55 | 56 | logout() { 57 | this.props.dispatch(logout()); 58 | } 59 | } 60 | 61 | Header.displayName = 'Header'; 62 | 63 | export default connect()(Header); 64 | -------------------------------------------------------------------------------- /web/src/components/Loading.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Loading extends React.Component { 4 | 5 | render() { 6 | return ( 7 |
8 |
9 |
10 |
11 | ); 12 | } 13 | 14 | } 15 | 16 | Loading.displayName = 'Loading'; 17 | -------------------------------------------------------------------------------- /web/src/components/UserRating.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const minRating = 1; 5 | const maxRating = 5; 6 | 7 | export default class UserRating extends React.Component { 8 | render() { 9 | var stars = []; 10 | var {movieId, savedRating} = this.props; 11 | 12 | for (let i = minRating; i <= maxRating; i++) { 13 | stars.push(this.renderStar(!savedRating || i > savedRating, movieId, i)); 14 | } 15 | 16 | return ( 17 | 18 | {stars} 19 | { 20 | savedRating ? 21 | 26 | : 27 | null 28 | } 29 | 30 | ); 31 | } 32 | 33 | renderStar(isEmpty, movieId, rating) { 34 | return ( 35 | ); 40 | } 41 | 42 | onRateClick(movieId, rating, e) { 43 | e.preventDefault(); 44 | this.props.onSubmitRating(movieId, rating); 45 | } 46 | 47 | onDeleteRatingClick(movieId, e) { 48 | e.preventDefault(); 49 | this.props.onDeleteRating(movieId); 50 | } 51 | } 52 | 53 | UserRating.propTypes = { 54 | movieId: PropTypes.number.isRequired, 55 | savedRating: PropTypes.number, 56 | onSubmitRating: PropTypes.func.isRequired, 57 | onDeleteRating: PropTypes.func.isRequired 58 | }; 59 | 60 | UserRating.displayName = 'UserRating'; 61 | 62 | -------------------------------------------------------------------------------- /web/src/components/common/Notification.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {NotificationType} from '../../redux/actions/NotificationActionTypes'; 4 | 5 | export default class Notification extends Component { 6 | 7 | constructor(props) { 8 | super(props); 9 | this.dismiss = this.dismiss.bind(this); 10 | } 11 | 12 | componentDidMount() { 13 | var {notification, dismiss, timeout} = this.props; 14 | 15 | this.dismissNotification = setTimeout(function () { 16 | dismiss(notification); 17 | }, timeout); 18 | } 19 | 20 | dismiss() { 21 | this.props.dismiss(this.props.notification); 22 | clearTimeout(this.dismissNotification); 23 | } 24 | 25 | render() { 26 | var {notification} = this.props; 27 | 28 | if (!notification) { 29 | return null; 30 | } 31 | 32 | //can use enum key or value 33 | var classes = NotificationType[notification.type] || notification.type; 34 | 35 | return ( 36 |
37 | {notification.message} 38 | 39 |
40 | ); 41 | } 42 | } 43 | 44 | 45 | Notification.displayName = 'Notification'; 46 | Notification.propTypes = { 47 | notification: PropTypes.object.isRequired, 48 | timeout: PropTypes.number, 49 | dismiss: PropTypes.func 50 | }; 51 | Notification.defaultProps = { 52 | timeout: 6000 53 | }; 54 | -------------------------------------------------------------------------------- /web/src/components/common/NotificationContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Notification from './Notification.jsx'; 3 | import { connect } from 'react-redux'; 4 | import { bindActionCreators } from 'redux'; 5 | import * as NotificationActions from '../../redux/actions/NotificationActions'; 6 | 7 | const NotificationContainer = ({dismiss, notifications}) => { 8 | return ( 9 |
10 | { 11 | notifications.map((notification, index) => ) 12 | } 13 |
14 | ); 15 | }; 16 | 17 | NotificationContainer.displayName = 'NotificationContainer'; 18 | 19 | function mapStateToProps(state) { 20 | return { 21 | notifications: state.notifications 22 | }; 23 | } 24 | 25 | function mapDispatchToProps(dispatch) { 26 | return bindActionCreators(NotificationActions, dispatch); 27 | } 28 | 29 | // Wrap the component to inject dispatch and state into it 30 | export default connect(mapStateToProps, mapDispatchToProps)(NotificationContainer); 31 | 32 | -------------------------------------------------------------------------------- /web/src/components/validation/ValidatedComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import _ from 'lodash' 3 | 4 | /** 5 | * A higher ordered component containing boilerplate code for handling validation of internal input fields. 6 | * 7 | * About higher order components: 8 | * It's a way of implementing mixin-like enhancements for React components (mixins are not possible with ES6 and they have other issues) 9 | * Check here for more: https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750 10 | */ 11 | export const ValidatorContext = React.createContext({ 12 | registerValidator: () => {}, 13 | unregisterValidator: () => {}, 14 | }) 15 | 16 | const ValidatedComponent = ComposedComponent => { 17 | class ValidatedComponent extends React.Component { 18 | constructor() { 19 | super() 20 | this.validators = [] 21 | } 22 | 23 | getValidationFormProps() { 24 | return { 25 | isComponentValid: this.isValid.bind(this), 26 | getValidationMessages: this.getValidationMessages.bind(this), 27 | } 28 | } 29 | 30 | getFieldValidatorContext() { 31 | return { 32 | registerValidator: this.onRegisterValidator.bind(this), 33 | unregisterValidator: this.onUnregisterValidator.bind(this), 34 | } 35 | } 36 | 37 | onRegisterValidator(validator) { 38 | this.validators.push(validator) 39 | } 40 | 41 | onUnregisterValidator(owner) { 42 | _.remove(this.validators, {owner}) 43 | } 44 | 45 | isValid() { 46 | let isValid = true 47 | this.validators.forEach(v => { 48 | v.owner.validate() 49 | isValid = isValid && v.owner.isValid() 50 | }) 51 | return isValid 52 | } 53 | 54 | getValidationMessages() { 55 | const results = [] 56 | this.validators.forEach(v => { 57 | const message = v.owner.getValidationMessage() 58 | if (message) { 59 | results.push(message) 60 | } 61 | }) 62 | 63 | return results 64 | } 65 | 66 | render() { 67 | const newProps = {...this.props, ...this.getValidationFormProps()} 68 | return ( 69 | 70 | 71 | 72 | ) 73 | } 74 | } 75 | 76 | ValidatedComponent.displayName = 'ValidatedComponent' 77 | 78 | return ValidatedComponent 79 | } 80 | 81 | export default ValidatedComponent 82 | -------------------------------------------------------------------------------- /web/src/config/settings.example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Express: http://localhost:3000/api/v0 3 | // Flask: http://localhost:5000/api/v0 4 | 5 | module.exports = { 6 | apiBaseURL: process.env.REACT_APP_API_BASE_URL 7 | }; 8 | -------------------------------------------------------------------------------- /web/src/config/settings.js: -------------------------------------------------------------------------------- 1 | // Express: http://localhost:3000/api/v0 2 | // Flask: http://localhost:5000/api/v0 3 | 4 | module.exports = { 5 | apiBaseURL: process.env.REACT_APP_API_BASE_URL || "http://localhost:5000/api/v0" 6 | }; 7 | -------------------------------------------------------------------------------- /web/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { createStore, applyMiddleware } from 'redux'; 4 | import thunkMiddleware from 'redux-thunk'; 5 | import { Provider } from 'react-redux'; 6 | import { createLogger } from 'redux-logger'; 7 | import createSagaMiddleware from 'redux-saga'; 8 | import { ConnectedRouter, routerMiddleware } from 'connected-react-router'; 9 | import { createBrowserHistory } from "history"; 10 | 11 | import sagas from './redux/sagas'; 12 | import createRootReducer from './redux/reducers'; 13 | import Routes from './routes/Routes.jsx'; 14 | 15 | const reduxLoggerMiddleware = createLogger(); 16 | const sagaMiddleware = createSagaMiddleware(); 17 | const history = createBrowserHistory(); 18 | 19 | const store = createStore( 20 | createRootReducer(history), 21 | applyMiddleware( 22 | routerMiddleware(history), 23 | thunkMiddleware, 24 | sagaMiddleware, 25 | reduxLoggerMiddleware, 26 | ), 27 | ) 28 | 29 | sagaMiddleware.run(sagas); 30 | 31 | ReactDOM.render( 32 | 33 | 34 | 35 | 36 | , 37 | document.getElementById('root') 38 | ); 39 | -------------------------------------------------------------------------------- /web/src/pages/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import _ from 'lodash'; 4 | 5 | import Header from '../components/Header.jsx'; 6 | import {getProfile} from '../redux/actions/ProfileActions'; 7 | import UserSession from '../UserSession'; 8 | // import Footer from '../components/Footer.jsx'; 9 | import Breadcrumbs from '../components/Breadcrumbs.jsx'; 10 | import NotificationContainer from '../components/common/NotificationContainer.jsx'; 11 | 12 | import { connect } from 'react-redux'; 13 | import "../styles/main.scss"; 14 | 15 | class App extends React.Component { 16 | componentDidMount() { 17 | if (UserSession.getToken() && !this.props.profile) { 18 | this.props.dispatch(getProfile()); 19 | } 20 | } 21 | 22 | render() { 23 | var {auth, profile, movie, person} = this.props; 24 | 25 | return ( 26 |
27 |
29 | 31 |
32 | {this.props.children} 33 |
34 | {/*
*/} 35 | 36 |
37 | ); 38 | } 39 | } 40 | 41 | App.displayName = 'App'; 42 | App.propTypes = { 43 | movie: PropTypes.object, 44 | person: PropTypes.object 45 | }; 46 | 47 | function mapStateToProps(state) { 48 | return { 49 | movie: state.movies.detail, 50 | person: state.person.detail, 51 | auth: state.auth, 52 | profile: _.get(state.profile, 'profile', null) 53 | }; 54 | } 55 | 56 | export default connect(mapStateToProps)(App); 57 | -------------------------------------------------------------------------------- /web/src/pages/AuthenticatedPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withRouter } from 'react-router'; 4 | import {connect} from 'react-redux'; 5 | 6 | /** 7 | * Higher ordered component for pages requiring authentication. 8 | */ 9 | var AuthenticatedPage = (PageComponent) => { 10 | 11 | class AuthenticatedPage extends React.Component { 12 | 13 | constructor() { 14 | super(); 15 | 16 | this.redirectOnLogout = this.redirectOnLogout.bind(this); 17 | } 18 | 19 | componentWillMount() { 20 | var {auth, location, history} = this.props; 21 | 22 | if (!auth.token) { 23 | var query = {redirectTo: (location.pathname + location.search)}; 24 | history.push({pathname: '/login', query}); 25 | } 26 | } 27 | 28 | componentWillReceiveProps(nextProps) { 29 | this.redirectOnLogout(nextProps); 30 | } 31 | 32 | redirectOnLogout(props) { 33 | var {auth, location, history} = props; 34 | 35 | if (!auth.token && location.pathname !== '/login') { 36 | history.push('/login'); 37 | } 38 | } 39 | 40 | render() { 41 | var {auth} = this.props; 42 | if (!auth.token) { 43 | return null; 44 | } 45 | 46 | return (); 47 | } 48 | } 49 | 50 | AuthenticatedPage.displayName = 'AuthenticatedPage'; 51 | AuthenticatedPage.contextTypes = { 52 | router: PropTypes.object.isRequired 53 | }; 54 | 55 | function mapStateToProps(state) { 56 | return { 57 | auth: state.auth 58 | }; 59 | } 60 | 61 | // Wrap the component to inject dispatch and state into it 62 | return connect(mapStateToProps)(withRouter(AuthenticatedPage)); 63 | }; 64 | 65 | export default AuthenticatedPage; 66 | 67 | -------------------------------------------------------------------------------- /web/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import Loading from '../components/Loading.jsx'; 4 | import Carousel from '../components/Carousel.jsx'; 5 | import _ from 'lodash'; 6 | 7 | import * as MovieActions from '../redux/actions/MovieActions'; 8 | import { bindActionCreators } from 'redux'; 9 | import { connect } from 'react-redux'; 10 | 11 | class Home extends React.Component { 12 | constructor() { 13 | super(); 14 | 15 | this.renderFeatured = this.renderFeatured.bind(this); 16 | this.renderByGenre = this.renderByGenre.bind(this); 17 | } 18 | 19 | componentWillMount() { 20 | this.props.getFeaturedMovies(); 21 | this.props.getMoviesByGenres(['Adventure', 'Drama']); 22 | } 23 | 24 | render() { 25 | var {movies} = this.props; 26 | return ( 27 |
28 |
29 |
30 | {movies.isFetching ? : null} 31 | {this.renderFeatured()} 32 |
33 |
34 | {this.renderByGenre('Adventure')} 35 | {this.renderByGenre('Drama')} 36 |
37 |
38 |
39 | ); 40 | } 41 | 42 | renderFeatured() { 43 | var {movies} = this.props; 44 | 45 | return ( 46 |
47 |

Featured Movies

48 |
    49 | { _.compact(movies.featured).map(f => { 50 | return ( 51 |
  • 52 | 53 | 54 | 55 |
  • 56 | ); 57 | })} 58 |
59 |
60 | ); 61 | } 62 | 63 | renderByGenre(name) { 64 | var {movies} = this.props; 65 | var moviesByGenre = movies.byGenre[name]; 66 | 67 | if (_.isEmpty(moviesByGenre)) { 68 | return null; 69 | } 70 | 71 | return ( 72 |
73 |
74 |
75 | {name} 76 |
77 | 78 | { moviesByGenre.map(m => { 79 | return ( 80 |
81 | 82 | 83 | 84 |
85 | {m.title} 86 |
87 |
88 | ); 89 | })} 90 |
91 |
92 |
); 93 | } 94 | } 95 | Home.displayName = 'Home'; 96 | 97 | function mapStateToProps(state) { 98 | return { 99 | genres: state.genres.items, 100 | movies: state.movies 101 | }; 102 | } 103 | 104 | function mapDispatchToProps(dispatch) { 105 | return bindActionCreators(MovieActions, dispatch); 106 | } 107 | 108 | // Wrap the component to inject dispatch and state into it 109 | export default connect(mapStateToProps, mapDispatchToProps)(Home); 110 | -------------------------------------------------------------------------------- /web/src/pages/SignupStatus.jsx: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import React from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import {Link} from 'react-router-dom'; 5 | import {createProfileInit} from '../redux/actions/ProfileActions'; 6 | import {connect} from 'react-redux'; 7 | 8 | class SignupStatus extends React.Component { 9 | componentWillMount() { 10 | var {profile} = this.props; 11 | 12 | if (!profile) { 13 | this.context.router.replace('/signup'); 14 | } 15 | } 16 | 17 | componentWillUnmount() { 18 | this.props.dispatch(createProfileInit()); 19 | } 20 | 21 | render() { 22 | var {profile} = this.props; 23 | 24 | return ( 25 |
26 | {profile ? 27 |
28 |
29 |
30 |

Congratulations!

31 |
32 |
33 | {profile.name ? 34 |
35 | {profile.name}, 36 |
37 | : null } 38 |

You have successfully created an account.

39 |
40 | Homepage 41 |
42 |
43 |
44 |
: null } 45 |
46 |
47 | ); 48 | } 49 | } 50 | 51 | SignupStatus.displayName = 'SignupStatus'; 52 | SignupStatus.contextTypes = { 53 | router: PropTypes.object.isRequired 54 | }; 55 | 56 | function mapStateToProps(state) { 57 | return { 58 | profile: _.get(state.signup, 'savedProfile') 59 | }; 60 | } 61 | 62 | export default connect(mapStateToProps)(SignupStatus); 63 | -------------------------------------------------------------------------------- /web/src/pages/index.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Movie App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /web/src/redux/actions/AuthActionTypes.js: -------------------------------------------------------------------------------- 1 | export const LOGIN = 'LOGIN'; 2 | export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; 3 | export const LOGIN_FAILURE = 'LOGIN_FAILURE'; 4 | export const LOGOUT = 'LOGOUT'; 5 | -------------------------------------------------------------------------------- /web/src/redux/actions/AuthActions.js: -------------------------------------------------------------------------------- 1 | import * as Types from './AuthActionTypes'; 2 | 3 | export function login(username, password) { 4 | return {type: Types.LOGIN, username, password}; 5 | } 6 | 7 | export function loginSuccess(token) { 8 | return {type: Types.LOGIN_SUCCESS, token}; 9 | } 10 | 11 | export function loginFailure(error) { 12 | return {type: Types.LOGIN_FAILURE, error}; 13 | } 14 | 15 | export function logout() { 16 | return {type: Types.LOGOUT}; 17 | } 18 | -------------------------------------------------------------------------------- /web/src/redux/actions/MovieActionTypes.js: -------------------------------------------------------------------------------- 1 | export const MOVIE_GENRES_GET_REQUEST = 'MOVIE_GENRES_GET_REQUEST'; 2 | export const MOVIE_GENRES_GET_SUCCESS = 'MOVIE_GENRES_GET_SUCCESS'; 3 | export const MOVIE_GENRES_GET_FAILURE = 'MOVIE_GENRES_GET_FAILURE'; 4 | 5 | export const MOVIES_BY_GENRES_GET_REQUEST = 'MOVIES_BY_GENRES_GET_REQUEST'; 6 | export const MOVIES_BY_GENRES_GET_SUCCESS = 'MOVIES_BY_GENRES_GET_SUCCESS'; 7 | export const MOVIES_BY_GENRES_GET_FAILURE = 'MOVIES_BY_GENRES_GET_FAILURE'; 8 | 9 | export const MOVIES_FEATURED_GET_REQUEST = 'MOVIES_FEATURED_GET_REQUEST'; 10 | export const MOVIES_FEATURED_GET_SUCCESS = 'MOVIES_FEATURED_GET_SUCCESS'; 11 | export const MOVIES_FEATURED_GET_FAILURE = 'MOVIES_FEATURED_GET_FAILURE'; 12 | 13 | export const MOVIE_DETAIL_CLEAR = 'MOVIE_DETAIL_CLEAR'; 14 | export const MOVIE_DETAIL_GET_REQUEST = 'MOVIE_DETAIL_GET_REQUEST'; 15 | export const MOVIE_DETAIL_GET_SUCCESS = 'MOVIE_DETAIL_GET_SUCCESS'; 16 | export const MOVIE_DETAIL_GET_FAILURE = 'MOVIE_DETAIL_GET_FAILURE'; 17 | 18 | export const MOVIE_RATE = 'MOVIE_RATE'; 19 | export const MOVIE_RATE_SUCCESS = 'MOVIE_RATE_SUCCESS'; 20 | export const MOVIE_RATE_FAILURE = 'MOVIE_RATE_FAILURE'; 21 | 22 | export const MOVIE_DELETE_RATING = 'MOVIE_DELETE_RATING'; 23 | export const MOVIE_DELETE_RATING_SUCCESS = 'MOVIE_DELETE_RATING_SUCCESS'; 24 | export const MOVIE_DELETE_RATING_FAILURE = 'MOVIE_DELETE_RATING_FAILURE'; -------------------------------------------------------------------------------- /web/src/redux/actions/MovieActions.js: -------------------------------------------------------------------------------- 1 | import * as Types from './MovieActionTypes'; 2 | 3 | export function clearMovie() { 4 | return {type: Types.MOVIE_DETAIL_CLEAR}; 5 | } 6 | 7 | export function getGenres() { 8 | return {type: Types.MOVIE_GENRES_GET_REQUEST}; 9 | } 10 | 11 | export function getGenresSuccess(genres) { 12 | return {type: Types.MOVIE_GENRES_GET_SUCCESS, genres}; 13 | } 14 | 15 | export function getGenresFailure(error) { 16 | return {type: Types.MOVIE_GENRES_GET_FAILURE, error}; 17 | } 18 | 19 | export function getMoviesByGenres(names) { 20 | return {type: Types.MOVIES_BY_GENRES_GET_REQUEST, names}; 21 | } 22 | 23 | export function getMoviesByGenresSuccess(response) { 24 | return {type: Types.MOVIES_BY_GENRES_GET_SUCCESS, response}; 25 | } 26 | 27 | export function getMoviesByGenresFailure(error) { 28 | return {type: Types.MOVIES_BY_GENRES_GET_FAILURE, error}; 29 | } 30 | 31 | export function getFeaturedMovies() { 32 | return {type: Types.MOVIES_FEATURED_GET_REQUEST}; 33 | } 34 | 35 | export function getFeaturedMoviesSuccess(response) { 36 | return {type: Types.MOVIES_FEATURED_GET_SUCCESS, response}; 37 | } 38 | 39 | export function getFeaturedMoviesFailure(error) { 40 | return {type: Types.MOVIES_FEATURED_GET_FAILURE, error}; 41 | } 42 | 43 | export function getMovie(id) { 44 | return {type: Types.MOVIE_DETAIL_GET_REQUEST, id}; 45 | } 46 | 47 | export function getMovieSuccess(response) { 48 | return {type: Types.MOVIE_DETAIL_GET_SUCCESS, response}; 49 | } 50 | 51 | export function getMovieFailure(error) { 52 | return {type: Types.MOVIE_DETAIL_GET_FAILURE, error}; 53 | } 54 | 55 | export function rateMovie(id, rating) { 56 | return {type: Types.MOVIE_RATE, id, rating}; 57 | } 58 | 59 | export function rateMovieSuccess() { 60 | return {type: Types.MOVIE_RATE_SUCCESS}; 61 | } 62 | 63 | export function rateMovieFailure() { 64 | return {type: Types.MOVIE_RATE_FAILURE}; 65 | } 66 | 67 | export function deleteMovieRating(id) { 68 | return {type: Types.MOVIE_DELETE_RATING, id}; 69 | } 70 | 71 | export function deleteMovieRatingSuccess() { 72 | return {type: Types.MOVIE_DELETE_RATING_SUCCESS}; 73 | } 74 | 75 | export function deleteMovieRatingFailure() { 76 | return {type: Types.MOVIE_DELETE_RATING_FAILURE}; 77 | } 78 | -------------------------------------------------------------------------------- /web/src/redux/actions/NotificationActionTypes.js: -------------------------------------------------------------------------------- 1 | export const NotificationActionTypes = { 2 | CREATE_NOTIFICATION: 'CREATE_NOTIFICATION', 3 | DISMISS_NOTIFICATION: 'DISMISS_NOTIFICATION' 4 | }; 5 | 6 | export const NotificationType = { 7 | error: 'alert round', 8 | success: 'success round', 9 | information: 'info', 10 | warning: 'warning', 11 | secondary: 'secondary' 12 | }; 13 | -------------------------------------------------------------------------------- /web/src/redux/actions/NotificationActions.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import {NotificationActionTypes, NotificationType} from './NotificationActionTypes'; 3 | 4 | export function create(type, message) { 5 | return { 6 | type: NotificationActionTypes.CREATE_NOTIFICATION, 7 | notification: { 8 | id: _.uniqueId(), 9 | message, 10 | type 11 | } 12 | }; 13 | } 14 | 15 | export function createError(message) { 16 | return { 17 | type: NotificationActionTypes.CREATE_NOTIFICATION, 18 | notification: { 19 | id: _.uniqueId(), 20 | message, 21 | type: NotificationType.error 22 | } 23 | }; 24 | } 25 | 26 | export function createSuccess(message) { 27 | return { 28 | type: NotificationActionTypes.CREATE_NOTIFICATION, 29 | notification: { 30 | id: _.uniqueId(), 31 | message, 32 | type: NotificationType.success 33 | } 34 | }; 35 | } 36 | 37 | export function dismiss(notification) { 38 | return { 39 | type: NotificationActionTypes.DISMISS_NOTIFICATION, 40 | notification 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /web/src/redux/actions/PersonActionTypes.js: -------------------------------------------------------------------------------- 1 | export const PERSON_DETAIL_CLEAR = 'PERSON_DETAIL_CLEAR'; 2 | export const PERSON_DETAIL_GET_REQUEST = 'PERSON_DETAIL_GET_REQUEST'; 3 | export const PERSON_DETAIL_GET_SUCCESS = 'PERSON_DETAIL_GET_SUCCESS'; 4 | export const PERSON_DETAIL_GET_FAILURE = 'PERSON_DETAIL_GET_FAILURE'; 5 | export const PERSON_RELATED_GET_REQUEST = 'PERSON_RELATED_GET_REQUEST'; 6 | export const PERSON_RELATED_GET_SUCCESS = 'PERSON_RELATED_GET_SUCCESS'; 7 | export const PERSON_RELATED_GET_FAILURE = 'PERSON_RELATED_GET_FAILURE'; -------------------------------------------------------------------------------- /web/src/redux/actions/PersonActions.js: -------------------------------------------------------------------------------- 1 | import * as Types from './PersonActionTypes'; 2 | 3 | export function clearPerson() { 4 | return { 5 | type: Types.PERSON_DETAIL_CLEAR 6 | }; 7 | } 8 | 9 | export function getPerson(id) { 10 | return {type: Types.PERSON_DETAIL_GET_REQUEST, id}; 11 | } 12 | 13 | export function getPersonSuccess(response) { 14 | return {type: Types.PERSON_DETAIL_GET_SUCCESS, response}; 15 | } 16 | 17 | export function getPersonFailure(error) { 18 | return {type: Types.PERSON_DETAIL_GET_FAILURE, error}; 19 | } 20 | 21 | export function getRelated(id) { 22 | return {type: Types.PERSON_RELATED_GET_REQUEST, id}; 23 | } 24 | 25 | export function getRelatedSuccess(response) { 26 | return {type: Types.PERSON_RELATED_GET_SUCCESS, response}; 27 | } 28 | 29 | export function getRelatedFailure(error) { 30 | return {type: Types.PERSON_RELATED_GET_FAILURE, error}; 31 | } 32 | -------------------------------------------------------------------------------- /web/src/redux/actions/ProfileActionTypes.js: -------------------------------------------------------------------------------- 1 | export const PROFILE_GET = 'PROFILE_GET'; 2 | export const PROFILE_GET_SUCCESS = 'PROFILE_GET_SUCCESS'; 3 | export const PROFILE_GET_FAILURE = 'PROFILE_GET_FAILURE'; 4 | 5 | export const PROFILE_CREATE = 'PROFILE_CREATE'; 6 | export const PROFILE_CREATE_SUCCESS = 'PROFILE_CREATE_SUCCESS'; 7 | export const PROFILE_CREATE_FAILURE = 'PROFILE_CREATE_FAILURE'; 8 | export const PROFILE_CREATE_INIT = 'PROFILE_CREATE_INIT'; 9 | 10 | export const PROFILE_GET_RATINGS = 'PROFILE_GET_RATINGS'; 11 | export const PROFILE_GET_RATINGS_SUCCESS = 'PROFILE_GET_RATINGS_SUCCESS'; 12 | export const PROFILE_GET_RATINGS_FAILURE = 'PROFILE_GET_RATINGS_FAILURE'; 13 | 14 | export const PROFILE_MOVIE_RATE = 'PROFILE_MOVIE_RATE'; 15 | export const PROFILE_MOVIE_RATE_SUCCESS = 'PROFILE_MOVIE_RATE_SUCCESS'; 16 | export const PROFILE_MOVIE_RATE_FAILURE = 'PROFILE_MOVIE_RATE_FAILURE'; 17 | 18 | export const PROFILE_MOVIE_DELETE_RATING = 'PROFILE_MOVIE_DELETE_RATING'; 19 | export const PROFILE_MOVIE_DELETE_RATING_SUCCESS = 'PROFILE_MOVIE_DELETE_RATING_SUCCESS'; 20 | export const PROFILE_MOVIE_DELETE_RATING_FAILURE = 'PROFILE_MOVIE_DELETE_RATING_FAILURE'; 21 | 22 | export const PROFILE_GET_RECOMMENDATIONS = 'PROFILE_GET_RECOMMENDATIONS'; 23 | export const PROFILE_GET_RECOMMENDATIONS_SUCCESS = 'PROFILE_GET_RECOMMENDATIONS_SUCCESS'; 24 | export const PROFILE_GET_RECOMMENDATIONS_FAILURE = 'PROFILE_GET_RECOMMENDATIONS_FAILURE'; -------------------------------------------------------------------------------- /web/src/redux/actions/ProfileActions.js: -------------------------------------------------------------------------------- 1 | import * as Types from './ProfileActionTypes'; 2 | 3 | export function getProfile() { 4 | return {type: Types.PROFILE_GET}; 5 | } 6 | 7 | export function getProfileSuccess(payload) { 8 | return {type: Types.PROFILE_GET_SUCCESS, payload}; 9 | } 10 | 11 | export function getProfileFailure(error) { 12 | return {type: Types.PROFILE_GET_FAILURE, error}; 13 | } 14 | 15 | export function createProfile(payload) { 16 | return {type: Types.PROFILE_CREATE, payload}; 17 | } 18 | 19 | export function createProfileSuccess(payload) { 20 | return {type: Types.PROFILE_CREATE_SUCCESS, payload}; 21 | } 22 | 23 | export function createProfileFailure(error) { 24 | return {type: Types.PROFILE_CREATE_FAILURE, error}; 25 | } 26 | 27 | export function createProfileInit() { 28 | return {type: Types.PROFILE_CREATE_INIT}; 29 | } 30 | 31 | export function getProfileRatings() { 32 | return {type: Types.PROFILE_GET_RATINGS}; 33 | } 34 | 35 | export function getProfileRatingsSuccess(payload) { 36 | return {type: Types.PROFILE_GET_RATINGS_SUCCESS, payload}; 37 | } 38 | 39 | export function getProfileRatingsFailure() { 40 | return {type: Types.PROFILE_GET_RATINGS_FAILURE}; 41 | } 42 | 43 | export function profileRateMovie(id, rating) { 44 | return {type: Types.PROFILE_MOVIE_RATE, id, rating}; 45 | } 46 | 47 | export function profileRateMovieSuccess() { 48 | return {type: Types.PROFILE_MOVIE_RATE_SUCCESS}; 49 | } 50 | 51 | export function profileRateMovieFailure() { 52 | return {type: Types.PROFILE_MOVIE_RATE_FAILURE}; 53 | } 54 | 55 | export function profileDeleteMovieRating(id) { 56 | return {type: Types.PROFILE_MOVIE_DELETE_RATING, id}; 57 | } 58 | 59 | export function profileDeleteMovieRatingSuccess() { 60 | return {type: Types.PROFILE_MOVIE_DELETE_RATING_SUCCESS}; 61 | } 62 | 63 | export function profileDeleteMovieRatingFailure() { 64 | return {type: Types.PROFILE_MOVIE_DELETE_RATING_FAILURE}; 65 | } 66 | 67 | export function getProfileRecommendations(id) { 68 | return {type: Types.PROFILE_GET_RECOMMENDATIONS, id}; 69 | } 70 | 71 | export function getProfileRecommendationsSuccess(payload) { 72 | return {type: Types.PROFILE_GET_RECOMMENDATIONS_SUCCESS, payload}; 73 | } 74 | 75 | export function getProfileRecommendationsFailure() { 76 | return {type: Types.PROFILE_GET_RECOMMENDATIONS_FAILURE}; 77 | } 78 | -------------------------------------------------------------------------------- /web/src/redux/reducers/auth.js: -------------------------------------------------------------------------------- 1 | import * as Types from '../actions/AuthActionTypes'; 2 | import UserSession from '../../UserSession'; 3 | import ErrorUtils from '../../utils/ErrorUtils'; 4 | 5 | export default function auth(state = getInitialState(), action) { 6 | switch (action.type) { 7 | case Types.LOGIN: 8 | return { 9 | ...state, 10 | isFetching: true, 11 | errors: {} 12 | }; 13 | case Types.LOGIN_SUCCESS: 14 | return { 15 | ...getInitialState(), 16 | token: action.token 17 | }; 18 | case Types.LOGIN_FAILURE: 19 | return { 20 | ...state, 21 | isFetching: false, 22 | errors: ErrorUtils.getApiErrors(action.error), 23 | }; 24 | case Types.LOGOUT: 25 | return { 26 | ...state, 27 | token: null 28 | }; 29 | default: 30 | return state; 31 | } 32 | } 33 | 34 | function getInitialState() { 35 | return { 36 | isFetching: false, 37 | errors: {}, 38 | token: UserSession.getToken() 39 | }; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /web/src/redux/reducers/genres.js: -------------------------------------------------------------------------------- 1 | import { MOVIE_GENRES_GET_REQUEST, MOVIE_GENRES_GET_SUCCESS } from '../actions/MovieActionTypes'; 2 | 3 | const initialState = { 4 | isFetching: false, 5 | items: [] 6 | }; 7 | 8 | export default function genres(state = initialState, action) { 9 | switch (action.type) { 10 | case MOVIE_GENRES_GET_REQUEST: 11 | return { 12 | ...state, 13 | isFetching: true 14 | }; 15 | case MOVIE_GENRES_GET_SUCCESS: 16 | return { 17 | ...state, 18 | isFetching: false, 19 | items: action.genres 20 | }; 21 | default: 22 | return state; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /web/src/redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux'; 2 | import { connectRouter } from 'connected-react-router' 3 | import notifications from './notifications'; 4 | import auth from './auth'; 5 | import signup from './signup'; 6 | import profile from './profile'; 7 | import genres from './genres'; 8 | import movies from './movies'; 9 | import person from './person'; 10 | 11 | const createRootReducer = (history) => combineReducers({ 12 | router: connectRouter(history), 13 | notifications, 14 | auth, 15 | signup, 16 | profile, 17 | genres, 18 | movies, 19 | person 20 | }); 21 | 22 | export default createRootReducer; 23 | -------------------------------------------------------------------------------- /web/src/redux/reducers/movies.js: -------------------------------------------------------------------------------- 1 | import * as Types from '../actions/MovieActionTypes'; 2 | 3 | const initialState = { 4 | isFetchingFeatured: false, 5 | isFetchingByGenre: false, 6 | isFetching: false, 7 | featured: [], 8 | byGenre: {}, 9 | detail: null 10 | }; 11 | 12 | export default function movies(state = initialState, action) { 13 | switch (action.type) { 14 | case Types.MOVIES_FEATURED_GET_REQUEST: 15 | return { 16 | ...state, 17 | isFetchingFeatured: true, 18 | isFetching: true 19 | }; 20 | case Types.MOVIES_FEATURED_GET_SUCCESS: 21 | return { 22 | ...state, 23 | isFetchingFeatured: false, 24 | isFetching: getIsFetching(false), 25 | featured: action.response 26 | }; 27 | case Types.MOVIES_BY_GENRES_GET_REQUEST: 28 | return { 29 | ...state, 30 | isFetchingByGenre: true, 31 | isFetching: true 32 | }; 33 | case Types.MOVIES_BY_GENRES_GET_SUCCESS: 34 | return { 35 | ...state, 36 | isFetchingByGenre: false, 37 | isFetching: getIsFetching(false), 38 | byGenre: action.response 39 | }; 40 | case Types.MOVIE_DETAIL_GET_REQUEST: 41 | return { 42 | ...state, 43 | isFetching: true 44 | }; 45 | case Types.MOVIE_DETAIL_GET_SUCCESS: 46 | return { 47 | ...state, 48 | isFetching: false, 49 | detail: action.response 50 | }; 51 | case Types.MOVIE_DETAIL_CLEAR: 52 | return { 53 | ...state, 54 | detail: null 55 | }; 56 | default: 57 | return state; 58 | } 59 | } 60 | 61 | function getIsFetching(state, isFetching) { 62 | return (state.isFetchingByGenre || state.isFetchingFeatured || isFetching); 63 | } 64 | -------------------------------------------------------------------------------- /web/src/redux/reducers/notifications.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import { NotificationActionTypes } from '../actions/NotificationActionTypes'; 3 | 4 | export default function notifications(state = [], action) { 5 | switch (action.type) { 6 | case NotificationActionTypes.CREATE_NOTIFICATION: 7 | return [ 8 | ...state, 9 | {...action.notification} 10 | ]; 11 | case NotificationActionTypes.DISMISS_NOTIFICATION: 12 | return _.without(state, action.notification); 13 | default: 14 | return state; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /web/src/redux/reducers/person.js: -------------------------------------------------------------------------------- 1 | import * as Types from '../actions/PersonActionTypes'; 2 | 3 | const initialState = { 4 | isFetching: false, 5 | isFetchingRelated: false, 6 | detail: null, 7 | related: [] 8 | }; 9 | 10 | export default function person(state = {...initialState}, action) { 11 | switch (action.type) { 12 | case Types.PERSON_DETAIL_GET_REQUEST: 13 | return { 14 | ...state, 15 | isFetching: true 16 | }; 17 | case Types.PERSON_DETAIL_GET_SUCCESS: 18 | return { 19 | ...state, 20 | isFetching: false, 21 | detail: action.response 22 | }; 23 | case Types.PERSON_DETAIL_CLEAR: 24 | return { 25 | ...initialState 26 | }; 27 | case Types.PERSON_RELATED_GET_REQUEST: 28 | return { 29 | ...state, 30 | isFetchingRelated: true 31 | }; 32 | case Types.PERSON_RELATED_GET_SUCCESS: 33 | return { 34 | ...state, 35 | isFetchingRelated: false, 36 | related: action.response.related 37 | }; 38 | 39 | default: 40 | return state; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /web/src/redux/reducers/profile.js: -------------------------------------------------------------------------------- 1 | import * as Types from '../actions/ProfileActionTypes'; 2 | import {LOGOUT} from '../actions/AuthActionTypes'; 3 | 4 | export default function profile(state = getInitialState(null), action) { 5 | switch (action.type) { 6 | case LOGOUT: 7 | return getInitialState(); 8 | default: 9 | return state; 10 | case Types.PROFILE_GET: 11 | case Types.PROFILE_GET_RATINGS: 12 | case Types.PROFILE_GET_RECOMMENDATIONS: 13 | return { 14 | ...state, 15 | isFetching: true 16 | }; 17 | case Types.PROFILE_GET_FAILURE: 18 | case Types.PROFILE_GET_RATINGS_FAILURE: 19 | case Types.PROFILE_GET_RECOMMENDATIONS_FAILURE: 20 | return { 21 | ...state, 22 | isFetching: false 23 | }; 24 | case Types.PROFILE_GET_SUCCESS: 25 | return { 26 | ...state, 27 | isFetching: false, 28 | profile: action.payload 29 | }; 30 | case Types.PROFILE_GET_RATINGS_SUCCESS: 31 | return { 32 | ...state, 33 | isFetching: false, 34 | ratedMovies: action.payload 35 | }; 36 | case Types.PROFILE_GET_RECOMMENDATIONS_SUCCESS: 37 | return { 38 | ...state, 39 | isFetching: false, 40 | recommendedMovies: action.payload 41 | }; 42 | } 43 | } 44 | 45 | function getInitialState() { 46 | return { 47 | isFetching: false, 48 | profile: null, 49 | ratedMovies: [], 50 | recommendedMovies: [] 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /web/src/redux/reducers/signup.js: -------------------------------------------------------------------------------- 1 | import * as Types from '../actions/ProfileActionTypes'; 2 | import ErrorUtils from '../../utils/ErrorUtils'; 3 | 4 | function getInitialState() { 5 | return { 6 | isSaving: false, 7 | savedProfile: null, 8 | errors: {} 9 | }; 10 | } 11 | 12 | export default function createProfile(state = getInitialState(), action) { 13 | switch (action.type) { 14 | case Types.PROFILE_CREATE_INIT: 15 | return getInitialState(); 16 | case Types.PROFILE_CREATE: 17 | return { 18 | ...state, 19 | isSaving: true 20 | }; 21 | case Types.PROFILE_CREATE_SUCCESS: 22 | return { 23 | ...state, 24 | isSaving: false, 25 | savedProfile: action.payload 26 | }; 27 | case Types.PROFILE_CREATE_FAILURE: 28 | return { 29 | isSaving: false, 30 | savedProfile: null, 31 | errors: ErrorUtils.getApiErrors(action.error) 32 | }; 33 | default: 34 | return state; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /web/src/redux/sagas/authFlow.js: -------------------------------------------------------------------------------- 1 | import {all, call, put, takeEvery} from 'redux-saga/effects'; 2 | import AuthApi from '../../api/AuthApi'; 3 | import * as Actions from '../actions/AuthActions'; 4 | import * as ProfileActions from '../actions/ProfileActions'; 5 | import * as Types from '../actions/AuthActionTypes'; 6 | import UserSession from '../../UserSession'; 7 | 8 | export default function* authFlow() { 9 | yield all([ 10 | takeEvery(Types.LOGIN, login), 11 | takeEvery(Types.LOGOUT, logout) 12 | ]); 13 | } 14 | 15 | function* login(action) { 16 | var {username, password} = action; 17 | try { 18 | const response = yield call(AuthApi.login, username, password); 19 | UserSession.setToken(response.token); 20 | 21 | yield put(Actions.loginSuccess(response.token)); 22 | yield put(ProfileActions.getProfile()); 23 | } 24 | catch (error) { 25 | yield put(Actions.loginFailure(error)); 26 | } 27 | } 28 | 29 | function* logout() { 30 | yield UserSession.setToken(null); 31 | } 32 | -------------------------------------------------------------------------------- /web/src/redux/sagas/errorFlow.js: -------------------------------------------------------------------------------- 1 | import { takeEvery, put } from 'redux-saga/effects'; 2 | import * as NotificationActions from '../actions/NotificationActions'; 3 | import _ from 'lodash'; 4 | 5 | export default function* watchErrors(getState) { 6 | yield takeEvery('*', function* logger(action) { 7 | if(action.error) { 8 | var errorAction = createErrorNotification(action.error); 9 | yield put(errorAction); 10 | } 11 | }); 12 | } 13 | 14 | function createErrorNotification(err) { 15 | if(err.status === 500) { 16 | return NotificationActions.createError(err.statusText || 'Internal Server Error'); 17 | } 18 | 19 | if (err.status !== 401) { 20 | var errMessages = []; 21 | if (err.data) { 22 | try { 23 | for (var prop in err.data) { 24 | var msg = err.data[prop]; 25 | if (_.isArray(msg)) { 26 | msg = msg.join(' '); 27 | } 28 | 29 | errMessages.push(`${prop}: ${msg}`); 30 | } 31 | } catch (ex) { 32 | console.error(ex); 33 | } 34 | } 35 | 36 | var errMessage = ''; 37 | if (errMessages.length === 1) { 38 | errMessage = errMessages[0]; 39 | } 40 | else { 41 | errMessage = errMessages.join('\n\n'); 42 | } 43 | 44 | var message = err.message || errMessage || (err.responseText ? ('Error: ' + err.responseText) : 'Error (see console logs)'); 45 | 46 | return NotificationActions.createError(message); 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /web/src/redux/sagas/index.js: -------------------------------------------------------------------------------- 1 | import {all, fork} from 'redux-saga/effects'; 2 | import errorFlow from './errorFlow'; 3 | import authFlow from './authFlow'; 4 | import profileFlow from './profileFlow'; 5 | import signupFlow from './signupFlow'; 6 | import movieFlow from './movieFlow'; 7 | import personFlow from './personFlow'; 8 | 9 | export default function* root() { 10 | yield all([ 11 | fork(errorFlow), 12 | fork(authFlow), 13 | fork(profileFlow), 14 | fork(signupFlow), 15 | fork(movieFlow), 16 | fork(personFlow) 17 | ]); 18 | } 19 | -------------------------------------------------------------------------------- /web/src/redux/sagas/movieFlow.js: -------------------------------------------------------------------------------- 1 | import {all, call, put, takeEvery} from 'redux-saga/effects'; 2 | import MoviesApi from '../../api/MoviesApi'; 3 | import * as Actions from '../actions/MovieActions'; 4 | import * as Types from '../actions/MovieActionTypes'; 5 | 6 | export default function* movieFlow() { 7 | yield all([ 8 | takeEvery(Types.MOVIE_GENRES_GET_REQUEST, getGenres), 9 | takeEvery(Types.MOVIES_BY_GENRES_GET_REQUEST, getMoviesByGenre), 10 | takeEvery(Types.MOVIES_FEATURED_GET_REQUEST, getFeaturedMovies), 11 | takeEvery(Types.MOVIE_DETAIL_GET_REQUEST, getMovie), 12 | takeEvery(Types.MOVIE_RATE, rateMovie), 13 | takeEvery(Types.MOVIE_DELETE_RATING, deleteRating), 14 | ]); 15 | } 16 | 17 | function* getGenres() { 18 | try { 19 | const response = yield call(MoviesApi.getGenres); 20 | yield put(Actions.getGenresSuccess(response)); 21 | } 22 | catch (error) { 23 | yield put(Actions.getGenresFailure(error)); 24 | } 25 | } 26 | 27 | function* getMoviesByGenre(action) { 28 | var {names} = action; 29 | try { 30 | const response = yield call(MoviesApi.getMoviesByGenres, names); 31 | yield put(Actions.getMoviesByGenresSuccess(response)); 32 | } 33 | catch (error) { 34 | yield put(Actions.getMoviesByGenresFailure(error)); 35 | } 36 | } 37 | 38 | function* getFeaturedMovies() { 39 | try { 40 | const response = yield call(MoviesApi.getFeaturedMovies); 41 | yield put(Actions.getFeaturedMoviesSuccess(response)); 42 | } 43 | catch (error) { 44 | yield put(Actions.getFeaturedMoviesFailure(error)); 45 | } 46 | } 47 | 48 | function* getMovie(action) { 49 | var {id} = action; 50 | try { 51 | const response = yield call(MoviesApi.getMovie, id); 52 | yield put(Actions.getMovieSuccess(response)); 53 | } 54 | catch (error) { 55 | yield put(Actions.getMovieFailure(error)); 56 | } 57 | } 58 | 59 | function* rateMovie(action) { 60 | var {id, rating} = action; 61 | try { 62 | const response = yield call(MoviesApi.rateMovie, id, rating); 63 | yield put(Actions.rateMovieSuccess(response)); 64 | yield put(Actions.getMovie(id)); 65 | } 66 | catch (error) { 67 | yield put(Actions.rateMovieFailure(error)); 68 | } 69 | } 70 | 71 | function* deleteRating(action) { 72 | var {id} = action; 73 | try { 74 | const response = yield call(MoviesApi.deleteRating, id); 75 | yield put(Actions.deleteMovieRatingSuccess(response)); 76 | yield put(Actions.getMovie(id)); 77 | } 78 | catch (error) { 79 | yield put(Actions.deleteMovieRatingFailure(error)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /web/src/redux/sagas/personFlow.js: -------------------------------------------------------------------------------- 1 | import {all, call, put, takeEvery} from 'redux-saga/effects'; 2 | import PersonApi from '../../api/PersonApi'; 3 | import * as Actions from '../actions/PersonActions'; 4 | import * as Types from '../actions/PersonActionTypes'; 5 | 6 | export default function* movieFlow() { 7 | yield all([ 8 | takeEvery(Types.PERSON_DETAIL_GET_REQUEST, getPerson), 9 | takeEvery(Types.PERSON_RELATED_GET_REQUEST, getRelated) 10 | ]); 11 | } 12 | 13 | function* getPerson(action) { 14 | var {id} = action; 15 | try { 16 | const response = yield call(PersonApi.getPerson, id); 17 | yield put(Actions.getPersonSuccess(response)); 18 | } 19 | catch (error) { 20 | yield put(Actions.getPersonFailure(error)); 21 | } 22 | } 23 | 24 | function* getRelated(action) { 25 | var {id} = action; 26 | try { 27 | const response = yield call(PersonApi.getRelated, id); 28 | yield put(Actions.getRelatedSuccess(response)); 29 | } 30 | catch (error) { 31 | yield put(Actions.getRelatedFailure(error)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /web/src/redux/sagas/profileFlow.js: -------------------------------------------------------------------------------- 1 | import {all, call, put, takeEvery} from 'redux-saga/effects'; 2 | import ProfileApi from '../../api/ProfileApi'; 3 | import MoviesApi from '../../api/MoviesApi'; 4 | import * as Actions from '../actions/ProfileActions'; 5 | import * as Types from '../actions/ProfileActionTypes'; 6 | 7 | export default function* profileFlow() { 8 | yield all([ 9 | takeEvery(Types.PROFILE_GET, getProfile), 10 | takeEvery(Types.PROFILE_GET_RATINGS, getProfileRatings), 11 | takeEvery(Types.PROFILE_MOVIE_RATE, profileRateMovie), 12 | takeEvery(Types.PROFILE_MOVIE_DELETE_RATING, profileDeleteRating), 13 | takeEvery(Types.PROFILE_GET_RECOMMENDATIONS, getProfileRecommendations) 14 | ]); 15 | } 16 | 17 | function* getProfile() { 18 | try { 19 | const response = yield call(ProfileApi.getProfile); 20 | yield put(Actions.getProfileSuccess(response)); 21 | } 22 | catch (error) { 23 | yield put(Actions.getProfileFailure(error)); 24 | } 25 | } 26 | 27 | function* getProfileRatings() { 28 | try { 29 | const response = yield call(ProfileApi.getProfileRatings); 30 | yield put(Actions.getProfileRatingsSuccess(response)); 31 | } 32 | catch (error) { 33 | yield put(Actions.getProfileRatingsFailure(error)); 34 | } 35 | } 36 | 37 | function* profileRateMovie(action) { 38 | var {id, rating} = action; 39 | try { 40 | const response = yield call(MoviesApi.rateMovie, id, rating); 41 | yield put(Actions.profileRateMovieSuccess(response)); 42 | yield put(Actions.getProfileRatings()); 43 | } 44 | catch (error) { 45 | yield put(Actions.profileRateMovieFailure(error)); 46 | } 47 | } 48 | 49 | function* profileDeleteRating(action) { 50 | var {id} = action; 51 | try { 52 | const response = yield call(MoviesApi.deleteRating, id); 53 | yield put(Actions.profileDeleteMovieRatingSuccess(response)); 54 | yield put(Actions.getProfileRatings()); 55 | } 56 | catch (error) { 57 | yield put(Actions.profileDeleteMovieRatingFailure(error)); 58 | } 59 | } 60 | 61 | function* getProfileRecommendations() { 62 | try { 63 | const response = yield call(ProfileApi.getProfileRecommendations); 64 | yield put(Actions.getProfileRecommendationsSuccess(response)); 65 | } 66 | catch (error) { 67 | yield put(Actions.getProfileRecommendationsFailure(error)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /web/src/redux/sagas/signupFlow.js: -------------------------------------------------------------------------------- 1 | import {all, call, put, takeEvery} from 'redux-saga/effects'; 2 | import AuthApi from '../../api/AuthApi'; 3 | import * as Actions from '../actions/ProfileActions'; 4 | import * as Types from '../actions/ProfileActionTypes'; 5 | import * as AuthActions from '../actions/AuthActions'; 6 | import { push } from 'connected-react-router' 7 | 8 | export default function* signupFlow() { 9 | yield all([ 10 | takeEvery(Types.PROFILE_CREATE, createProfile), 11 | ]); 12 | } 13 | 14 | function* createProfile(action) { 15 | var {payload} = action; 16 | 17 | try { 18 | const response = yield call(AuthApi.register, payload); 19 | yield put(Actions.createProfileSuccess(response)); 20 | yield put(AuthActions.login(payload.username, payload.password)); 21 | yield put(push('/signup-status')); 22 | } 23 | catch (error) { 24 | yield put(Actions.createProfileFailure(error)); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /web/src/routes/Routes.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Route} from 'react-router'; 3 | import App from '../pages/App.jsx'; 4 | import Home from '../pages/Home.jsx'; 5 | import Movie from '../pages/Movie.jsx'; 6 | import Person from '../pages/Person.jsx'; 7 | import Login from '../pages/Login.jsx'; 8 | import Signup from '../pages/Signup.jsx'; 9 | import SignupStatus from '../pages/SignupStatus.jsx'; 10 | import Profile from '../pages/Profile.jsx'; 11 | 12 | export default class Routes extends React.Component { 13 | render() { 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | } 27 | 28 | Routes.displayName = 'Routes'; 29 | -------------------------------------------------------------------------------- /web/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /web/src/styles/_variables.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiyamotoSatoshi/react-python/0d166f79d89f03cda03a587c138680707e9f7ed6/web/src/styles/_variables.scss -------------------------------------------------------------------------------- /web/src/styles/components/_breadcrumbs.scss: -------------------------------------------------------------------------------- 1 | .breadcrumbs{ 2 | width: 100%; 3 | } -------------------------------------------------------------------------------- /web/src/styles/components/_buttonLink.scss: -------------------------------------------------------------------------------- 1 | .buttonLink { 2 | background-color: transparent; 3 | border: none; 4 | padding: 0; 5 | margin: 0; 6 | display: inline; 7 | cursor: pointer; 8 | color: #008CBA; 9 | 10 | &:hover { 11 | color: #0078a0; 12 | background-color: inherit; 13 | } 14 | } -------------------------------------------------------------------------------- /web/src/styles/components/_carousel.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | .nt-carousel { 4 | position: relative; 5 | 6 | &-list { 7 | position: relative; 8 | margin: 0; 9 | } 10 | 11 | &-item { 12 | vertical-align: top; 13 | } 14 | 15 | &-arrow { 16 | font-size: 1.5rem; 17 | font-weight: 700; 18 | color: white; 19 | } 20 | 21 | &-left { 22 | position: absolute; 23 | left: 0; 24 | top: 40%; 25 | display: inline-block; 26 | padding: 1rem; 27 | z-index: 2; 28 | background: rgba(0, 0, 0, 0.3); 29 | 30 | &:hover { 31 | background: rgba(0, 0, 0, 0.3); 32 | } 33 | } 34 | 35 | &-right { 36 | position: absolute; 37 | right: 0; 38 | top: 40%; 39 | display: inline-block; 40 | padding: 1rem; 41 | z-index: 2; 42 | background: rgba(0, 0, 0, 0.3); 43 | 44 | &:hover { 45 | background: rgba(0, 0, 0, 0.3); 46 | } 47 | } 48 | } 49 | 50 | .nt-carousel-movie { 51 | 52 | &-title { 53 | font-size: .8rem; 54 | height: 1.2rem; 55 | overflow: hidden; 56 | text-overflow: ellipsis; 57 | white-space: nowrap; 58 | } 59 | 60 | &-role { 61 | font-style: italic; 62 | font-size: .8rem; 63 | height: 1.2rem; 64 | overflow: hidden; 65 | text-overflow: ellipsis; 66 | white-space: nowrap; 67 | } 68 | } 69 | 70 | 71 | .nt-carousel-actor { 72 | &-name { 73 | font-size: 0.8rem; 74 | height: 1.2rem; 75 | overflow: hidden; 76 | text-overflow: ellipsis; 77 | white-space: nowrap; 78 | } 79 | &-role { 80 | font-style: italic; 81 | font-size: 0.8rem; 82 | height: 1.2rem; 83 | overflow: hidden; 84 | text-overflow: ellipsis; 85 | white-space: nowrap; 86 | } 87 | } 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /web/src/styles/components/_footer.scss: -------------------------------------------------------------------------------- 1 | .nt-app-footer { 2 | background: lightgrey; 3 | } -------------------------------------------------------------------------------- /web/src/styles/components/_header.scss: -------------------------------------------------------------------------------- 1 | .nt-app-header { 2 | background: lightgrey; 3 | padding: .5rem 1rem; 4 | height: 104px; 5 | @media (max-width: 385px) { 6 | height: 145px; 7 | } 8 | 9 | &-logo { 10 | max-width: 5rem; 11 | display: inline-block; 12 | @media (min-width: 386px) { 13 | padding-top: 26px; 14 | } 15 | } 16 | 17 | &-links { 18 | list-style: none; 19 | display: inline-block; 20 | margin: 0; 21 | padding-right: 8px; 22 | 23 | li { 24 | display: inline-block; 25 | padding: .5rem 0 .5rem .5rem; 26 | 27 | @media #{$small-only} { 28 | &::before { 29 | display: none; 30 | } 31 | } 32 | 33 | &::before { 34 | color: #AAAAAA; 35 | content: ""; 36 | border-right: 1px solid darkgray; 37 | margin: 0 1rem; 38 | position: relative; 39 | top: 1px; 40 | } 41 | } 42 | } 43 | 44 | &-avatar { 45 | width: 4rem; 46 | height: 4rem; 47 | border-radius: 2em; 48 | background-size: contain !important; 49 | a { 50 | display: block; 51 | width: 100%; 52 | height: 100%; 53 | } 54 | } 55 | 56 | &-link { 57 | } 58 | 59 | &-profile-links{ 60 | float: right; 61 | text-align: center; 62 | width: 64px; 63 | } 64 | } -------------------------------------------------------------------------------- /web/src/styles/components/_loading.scss: -------------------------------------------------------------------------------- 1 | .sk-spinner-chasing-dots.sk-spinner { 2 | margin: 0 auto; 3 | width: 40px; 4 | height: 40px; 5 | top: 10px; 6 | right: 10px; 7 | text-align: center; 8 | -webkit-animation: sk-chasingDotsRotate 2s infinite linear; 9 | animation: sk-chasingDotsRotate 2s infinite linear; } 10 | .sk-spinner-chasing-dots .sk-dot1, .sk-spinner-chasing-dots .sk-dot2 { 11 | width: 60%; 12 | height: 60%; 13 | display: inline-block; 14 | position: absolute; 15 | top: 0; 16 | background-color: #333; 17 | border-radius: 100%; 18 | -webkit-animation: sk-chasingDotsBounce 2s infinite ease-in-out; 19 | animation: sk-chasingDotsBounce 2s infinite ease-in-out; } 20 | .sk-spinner-chasing-dots .sk-dot2 { 21 | top: auto; 22 | bottom: 0px; 23 | -webkit-animation-delay: -1s; 24 | animation-delay: -1s; } 25 | 26 | @-webkit-keyframes sk-chasingDotsRotate { 27 | 100% { 28 | -webkit-transform: rotate(360deg); 29 | transform: rotate(360deg); } } 30 | 31 | @keyframes sk-chasingDotsRotate { 32 | 100% { 33 | -webkit-transform: rotate(360deg); 34 | transform: rotate(360deg); } } 35 | 36 | @-webkit-keyframes sk-chasingDotsBounce { 37 | 0%, 100% { 38 | -webkit-transform: scale(0); 39 | transform: scale(0); } 40 | 41 | 50% { 42 | -webkit-transform: scale(1); 43 | transform: scale(1); } } 44 | 45 | @keyframes sk-chasingDotsBounce { 46 | 0%, 100% { 47 | -webkit-transform: scale(0); 48 | transform: scale(0); } 49 | 50 | 50% { 51 | -webkit-transform: scale(1); 52 | transform: scale(1); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /web/src/styles/components/_notificationContainer.scss: -------------------------------------------------------------------------------- 1 | .notification-container{ 2 | position: fixed; 3 | right: 8em; 4 | top:2em; 5 | width: 15em; 6 | text-align: center; 7 | z-index: 999999999; 8 | 9 | .alert-box { 10 | opacity: 0.75; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /web/src/styles/components/_userRating.scss: -------------------------------------------------------------------------------- 1 | .nt-user-rating { 2 | &-star { 3 | font-size: 1.5rem; 4 | line-height: 1.5rem; 5 | float: left; 6 | } 7 | 8 | &-delete { 9 | float: left; 10 | font-size: .8rem; 11 | line-height: 1.5rem; 12 | padding-left: .5rem; 13 | text-decoration: underline; 14 | } 15 | } -------------------------------------------------------------------------------- /web/src/styles/components/_validation.scss: -------------------------------------------------------------------------------- 1 | .validation { 2 | input { 3 | margin-bottom: 0.1em; 4 | } 5 | } 6 | 7 | .validation-summary { 8 | padding: .8rem 0; 9 | } 10 | 11 | .validation-msg { 12 | color: #C70000; 13 | height: 1.2em; 14 | line-height: 1em; 15 | @extend .mark-validation-msg; 16 | } 17 | 18 | .mark-validation-msg { 19 | &::before { 20 | content: "\26a0"; 21 | font-size: 1em; 22 | margin-right: 0.5em; 23 | } 24 | 25 | &:empty { 26 | &::before { 27 | content: ""; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /web/src/styles/foundation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foundation", 3 | "main": [ 4 | "scss/foundation.scss", 5 | "js/foundation.js" 6 | ], 7 | "ignore": [], 8 | "dependencies": { 9 | "jquery": ">= 2.1.0", 10 | "modernizr": "2.8.3", 11 | "fastclick": ">=0.6.11", 12 | "jquery.cookie": "~1.4.0", 13 | "jquery-placeholder": "~2.0.7" 14 | }, 15 | "devDependencies": { 16 | "jquery.autocomplete": "devbridge/jQuery-Autocomplete#1.2.9", 17 | "lodash": "~2.4.1" 18 | }, 19 | "private": true, 20 | "homepage": "https://github.com/zurb/bower-foundation", 21 | "version": "5.5.3", 22 | "_release": "5.5.3", 23 | "_resolution": { 24 | "type": "version", 25 | "tag": "5.5.3", 26 | "commit": "b879716aa268e1f88fe43de98db2db4487af00ca" 27 | }, 28 | "_source": "https://github.com/zurb/bower-foundation.git", 29 | "_target": "~5.5.3", 30 | "_originalSource": "foundation" 31 | } -------------------------------------------------------------------------------- /web/src/styles/foundation/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2015 ZURB, inc. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /web/src/styles/foundation/README.md: -------------------------------------------------------------------------------- 1 | # [Foundation](http://foundation.zurb.com) 2 | 3 | [![Build Status](https://travis-ci.org/zurb/foundation.svg)](https://travis-ci.org/zurb/foundation) 4 | 5 | 6 | Foundation is the most advanced responsive front-end framework in the world. You can quickly prototype and build sites or apps that work on any kind of device with Foundation, which includes layout constructs (like a fully responsive grid), elements and best practices. 7 | 8 | To get started, check out 9 | 10 | 11 | ## Quickstart 12 | 13 | To get going with Foundation you can: 14 | 15 | * [Download the latest release](http://foundation.zurb.com/develop/download.html) 16 | * [Install with Bower](http://bower.io): `bower install foundation` 17 | * [Install with npm](http://npmjs.com): `npm install foundation-sites` 18 | 19 | ## Documentation 20 | 21 | Foundation uses [Assemble.io](http://assemble.io) and [Grunt](http://gruntjs.com/) to generate its [documentation pages](http://foundation.zurb.com/docs). Documentation can also be run from your local computer: 22 | 23 | ### View documentation locally 24 | 25 | You'll want to clone the Foundation repo first and install all the dependencies. You can do this using the following commands: 26 | 27 | ``` 28 | git clone git@github.com:zurb/foundation.git 29 | cd foundation 30 | npm install -g grunt-cli bower 31 | npm install 32 | bower install 33 | bundle install 34 | ``` 35 | 36 | Then just run `grunt build` and the documentation will be compiled: 37 | 38 | ``` 39 | foundation/ 40 | ├── dist/ 41 | │ └── ... 42 | ├────── docs/ 43 | │ └── ... 44 | ``` 45 | 46 | Copyright (c) 2015 ZURB, inc. 47 | -------------------------------------------------------------------------------- /web/src/styles/foundation/scss/foundation.scss: -------------------------------------------------------------------------------- 1 | // Foundation by ZURB 2 | // foundation.zurb.com 3 | // Licensed under MIT Open Source 4 | 5 | // Behold, here are all the Foundation components. 6 | @import 'foundation/components/grid'; 7 | @import 'foundation/components/accordion'; 8 | @import 'foundation/components/alert-boxes'; 9 | @import 'foundation/components/block-grid'; 10 | @import 'foundation/components/breadcrumbs'; 11 | @import 'foundation/components/button-groups'; 12 | @import 'foundation/components/buttons'; 13 | @import 'foundation/components/clearing'; 14 | @import 'foundation/components/dropdown'; 15 | @import 'foundation/components/dropdown-buttons'; 16 | @import 'foundation/components/flex-video'; 17 | @import 'foundation/components/forms'; 18 | @import 'foundation/components/icon-bar'; 19 | @import 'foundation/components/inline-lists'; 20 | @import 'foundation/components/joyride'; 21 | @import 'foundation/components/keystrokes'; 22 | @import 'foundation/components/labels'; 23 | @import 'foundation/components/magellan'; 24 | @import 'foundation/components/orbit'; 25 | @import 'foundation/components/pagination'; 26 | @import 'foundation/components/panels'; 27 | @import 'foundation/components/pricing-tables'; 28 | @import 'foundation/components/progress-bars'; 29 | @import 'foundation/components/range-slider'; 30 | @import 'foundation/components/reveal'; 31 | @import 'foundation/components/side-nav'; 32 | @import 'foundation/components/split-buttons'; 33 | @import 'foundation/components/sub-nav'; 34 | @import 'foundation/components/switches'; 35 | @import 'foundation/components/tables'; 36 | @import 'foundation/components/tabs'; 37 | @import 'foundation/components/thumbs'; 38 | @import 'foundation/components/tooltips'; 39 | @import 'foundation/components/top-bar'; 40 | @import 'foundation/components/type'; 41 | @import 'foundation/components/offcanvas'; 42 | @import 'foundation/components/visibility'; 43 | -------------------------------------------------------------------------------- /web/src/styles/foundation/scss/foundation/components/_flex-video.scss: -------------------------------------------------------------------------------- 1 | // Foundation by ZURB 2 | // foundation.zurb.com 3 | // Licensed under MIT Open Source 4 | 5 | @import 'global'; 6 | 7 | // 8 | // @variables 9 | // 10 | $include-html-media-classes: $include-html-classes !default; 11 | 12 | // We use these to control video container padding and margins 13 | $flex-video-padding-top: rem-calc(25) !default; 14 | $flex-video-padding-bottom: 67.5% !default; 15 | $flex-video-margin-bottom: rem-calc(16) !default; 16 | 17 | // We use this to control widescreen bottom padding 18 | $flex-video-widescreen-padding-bottom: 56.34% !default; 19 | 20 | // 21 | // @mixins 22 | // 23 | 24 | @mixin flex-video-container { 25 | height: 0; 26 | margin-bottom: $flex-video-margin-bottom; 27 | overflow: hidden; 28 | padding-bottom: $flex-video-padding-bottom; 29 | padding-top: $flex-video-padding-top; 30 | position: relative; 31 | 32 | &.widescreen { padding-bottom: $flex-video-widescreen-padding-bottom; } 33 | &.vimeo { padding-top: 0; } 34 | 35 | iframe, 36 | object, 37 | embed, 38 | video { 39 | height: 100%; 40 | position: absolute; 41 | top: 0; 42 | width: 100%; 43 | #{$default-float}: 0; 44 | } 45 | } 46 | 47 | @include exports("flex-video") { 48 | @if $include-html-media-classes { 49 | .flex-video { @include flex-video-container; } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /web/src/styles/foundation/scss/foundation/components/_inline-lists.scss: -------------------------------------------------------------------------------- 1 | // Foundation by ZURB 2 | // foundation.zurb.com 3 | // Licensed under MIT Open Source 4 | 5 | @import 'global'; 6 | 7 | // 8 | // @variables 9 | // 10 | $include-html-inline-list-classes: $include-html-classes !default; 11 | 12 | // We use this to control the margins and padding of the inline list. 13 | $inline-list-top-margin: 0 !default; 14 | $inline-list-opposite-margin: 0 !default; 15 | $inline-list-bottom-margin: rem-calc(17) !default; 16 | $inline-list-default-float-margin: rem-calc(-22) !default; 17 | $inline-list-default-float-list-margin: rem-calc(22) !default; 18 | 19 | $inline-list-padding: 0 !default; 20 | 21 | // We use this to control the overflow of the inline list. 22 | $inline-list-overflow: hidden !default; 23 | 24 | // We use this to control the list items 25 | $inline-list-display: block !default; 26 | 27 | // We use this to control any elements within list items 28 | $inline-list-children-display: block !default; 29 | 30 | // 31 | // @mixins 32 | // 33 | // We use this mixin to create inline lists 34 | @mixin inline-list { 35 | list-style: none; 36 | margin-top: $inline-list-top-margin; 37 | margin-bottom: $inline-list-bottom-margin; 38 | margin-#{$default-float}: $inline-list-default-float-margin; 39 | margin-#{$opposite-direction}: $inline-list-opposite-margin; 40 | overflow: $inline-list-overflow; 41 | padding: $inline-list-padding; 42 | 43 | > li { 44 | display: $inline-list-display; 45 | float: $default-float; 46 | list-style: none; 47 | margin-#{$default-float}: $inline-list-default-float-list-margin; 48 | > * { display: $inline-list-children-display; } 49 | } 50 | } 51 | 52 | @include exports("inline-list") { 53 | @if $include-html-inline-list-classes { 54 | .inline-list { 55 | @include inline-list(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /web/src/styles/foundation/scss/foundation/components/_keystrokes.scss: -------------------------------------------------------------------------------- 1 | // Foundation by ZURB 2 | // foundation.zurb.com 3 | // Licensed under MIT Open Source 4 | 5 | @import 'global'; 6 | 7 | // 8 | // @variables 9 | // 10 | $include-html-keystroke-classes: $include-html-classes !default; 11 | 12 | // We use these to control text styles. 13 | $keystroke-font: "Consolas", "Menlo", "Courier", monospace !default; 14 | $keystroke-font-size: inherit !default; 15 | $keystroke-font-color: $jet !default; 16 | $keystroke-font-color-alt: $white !default; 17 | $keystroke-function-factor: -7% !default; 18 | 19 | // We use this to control keystroke padding. 20 | $keystroke-padding: rem-calc(2 4 0) !default; 21 | 22 | // We use these to control background and border styles. 23 | $keystroke-bg: scale-color($white, $lightness: $keystroke-function-factor) !default; 24 | $keystroke-border-style: solid !default; 25 | $keystroke-border-width: 1px !default; 26 | $keystroke-border-color: scale-color($keystroke-bg, $lightness: $keystroke-function-factor) !default; 27 | $keystroke-radius: $global-radius !default; 28 | 29 | // 30 | // @mixins 31 | // 32 | // We use this mixin to create keystroke styles. 33 | // $bg - Default: $keystroke-bg || scale-color($white, $lightness: $keystroke-function-factor) !default; 34 | @mixin keystroke($bg:$keystroke-bg) { 35 | // This find the lightness percentage of the background color. 36 | $bg-lightness: lightness($bg); 37 | background-color: $bg; 38 | border-color: scale-color($bg, $lightness: $keystroke-function-factor); 39 | 40 | // We adjust the font color based on the brightness of the background. 41 | @if $bg-lightness > 70% { color: $keystroke-font-color; } 42 | @else { color: $keystroke-font-color-alt; } 43 | 44 | border-style: $keystroke-border-style; 45 | border-width: $keystroke-border-width; 46 | font-family: $keystroke-font; 47 | font-size: $keystroke-font-size; 48 | margin: 0; 49 | padding: $keystroke-padding; 50 | } 51 | 52 | @include exports("keystroke") { 53 | @if $include-html-keystroke-classes { 54 | .keystroke, 55 | kbd { 56 | @include keystroke; 57 | @include radius($keystroke-radius); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /web/src/styles/foundation/scss/foundation/components/_magellan.scss: -------------------------------------------------------------------------------- 1 | // Foundation by ZURB 2 | // foundation.zurb.com 3 | // Licensed under MIT Open Source 4 | 5 | @import 'global'; 6 | 7 | // 8 | // @variables 9 | // 10 | $include-html-magellan-classes: $include-html-classes !default; 11 | 12 | $magellan-bg: $white !default; 13 | $magellan-padding: 10px !default; 14 | 15 | @include exports("magellan") { 16 | @if $include-html-magellan-classes { 17 | 18 | #{data('magellan-expedition')}, #{data('magellan-expedition-clone')} { 19 | background: $magellan-bg; 20 | min-width: 100%; 21 | padding: $magellan-padding; 22 | z-index: 50; 23 | 24 | .sub-nav { 25 | margin-bottom: 0; 26 | dd { margin-bottom: 0; } 27 | a { 28 | line-height: 1.8em; 29 | } 30 | } 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /web/src/styles/foundation/scss/foundation/components/_progress-bars.scss: -------------------------------------------------------------------------------- 1 | // Foundation by ZURB 2 | // foundation.zurb.com 3 | // Licensed under MIT Open Source 4 | 5 | @import 'global'; 6 | 7 | // 8 | // @variables 9 | // 10 | $include-html-media-classes: $include-html-classes !default; 11 | 12 | // We use this to set the progress bar height 13 | $progress-bar-height: rem-calc(25) !default; 14 | $progress-bar-color: $vapor !default; 15 | 16 | // We use these to control the border styles 17 | $progress-bar-border-color: scale-color($white, $lightness: 20%) !default; 18 | $progress-bar-border-size: 1px !default; 19 | $progress-bar-border-style: solid !default; 20 | $progress-bar-border-radius: $global-radius !default; 21 | 22 | // We use these to control the margin & padding 23 | $progress-bar-pad: rem-calc(2) !default; 24 | $progress-bar-margin-bottom: rem-calc(10) !default; 25 | 26 | // We use these to set the meter colors 27 | $progress-meter-color: $primary-color !default; 28 | $progress-meter-secondary-color: $secondary-color !default; 29 | $progress-meter-success-color: $success-color !default; 30 | $progress-meter-alert-color: $alert-color !default; 31 | 32 | // @mixins 33 | // 34 | // We use this to set up the progress bar container 35 | @mixin progress-container { 36 | background-color: $progress-bar-color; 37 | border: $progress-bar-border-size $progress-bar-border-style $progress-bar-border-color; 38 | height: $progress-bar-height; 39 | margin-bottom: $progress-bar-margin-bottom; 40 | padding: $progress-bar-pad; 41 | } 42 | 43 | // @mixins 44 | // 45 | // $bg - Default: $progress-meter-color || $primary-color 46 | @mixin progress-meter($bg:$progress-meter-color) { 47 | background: $bg; 48 | display: block; 49 | height: 100%; 50 | float: left; 51 | width: 0%; 52 | } 53 | 54 | 55 | @include exports("progress-bar") { 56 | @if $include-html-media-classes { 57 | 58 | /* Progress Bar */ 59 | .progress { 60 | @include progress-container; 61 | 62 | // Meter 63 | .meter { 64 | @include progress-meter; 65 | 66 | &.secondary { @include progress-meter($bg:$progress-meter-secondary-color); } 67 | &.success { @include progress-meter($bg:$progress-meter-success-color); } 68 | &.alert { @include progress-meter($bg:$progress-meter-alert-color); } 69 | } 70 | &.secondary .meter { @include progress-meter($bg:$progress-meter-secondary-color); } 71 | &.success .meter { @include progress-meter($bg:$progress-meter-success-color); } 72 | &.alert .meter { @include progress-meter($bg:$progress-meter-alert-color); } 73 | 74 | &.radius { @include radius($progress-bar-border-radius); 75 | .meter { @include radius($progress-bar-border-radius - 1); } 76 | } 77 | 78 | &.round { @include radius(1000px); 79 | .meter { @include radius(999px); } 80 | } 81 | 82 | } 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /web/src/styles/foundation/scss/foundation/components/_thumbs.scss: -------------------------------------------------------------------------------- 1 | // Foundation by ZURB 2 | // foundation.zurb.com 3 | // Licensed under MIT Open Source 4 | 5 | @import 'global'; 6 | 7 | // 8 | // @name _thumbs.scss 9 | // @dependencies _globals.scss 10 | // 11 | 12 | // 13 | // @variables 14 | // 15 | 16 | $include-html-media-classes: $include-html-classes !default; 17 | 18 | // We use these to control border styles 19 | $thumb-border-style: solid !default; 20 | $thumb-border-width: 4px !default; 21 | $thumb-border-color: $white !default; 22 | $thumb-box-shadow: 0 0 0 1px rgba($black,.2) !default; 23 | $thumb-box-shadow-hover: 0 0 6px 1px rgba($primary-color,0.5) !default; 24 | 25 | // Radius and transition speed for thumbs 26 | $thumb-radius: $global-radius !default; 27 | $thumb-transition-speed: 200ms !default; 28 | 29 | // 30 | // @mixins 31 | // 32 | 33 | // We use this to create image thumbnail styles. 34 | // 35 | // $border-width - Width of border around thumbnail. Default: $thumb-border-width. 36 | // $box-shadow - Box shadow to apply to thumbnail. Default: $thumb-box-shadow. 37 | // $box-shadow-hover - Box shadow to apply on hover. Default: $thumb-box-shadow-hover. 38 | @mixin thumb( 39 | $border-width:$thumb-border-width, 40 | $box-shadow:$thumb-box-shadow, 41 | $box-shadow-hover:$thumb-box-shadow-hover) { 42 | border: $thumb-border-style $border-width $thumb-border-color; 43 | box-shadow: $box-shadow; 44 | display: inline-block; 45 | line-height: 0; 46 | max-width: 100%; 47 | 48 | &:hover, 49 | &:focus { 50 | box-shadow: $box-shadow-hover; 51 | } 52 | } 53 | 54 | 55 | @include exports("thumb") { 56 | @if $include-html-media-classes { 57 | 58 | /* Image Thumbnails */ 59 | .th { 60 | @include thumb; 61 | @include single-transition(all, $thumb-transition-speed, ease-out); 62 | 63 | &.radius { @include radius($thumb-radius); } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /web/src/styles/main.scss: -------------------------------------------------------------------------------- 1 | @import "components/buttonLink"; 2 | 3 | @import '_variables.scss'; 4 | @import './foundation/scss/normalize'; 5 | @import './foundation/scss/foundation'; 6 | 7 | @import "components/notificationContainer"; 8 | @import "components/header"; 9 | @import "components/breadcrumbs"; 10 | @import "components/loading"; 11 | @import "components/carousel"; 12 | @import "components/userRating"; 13 | @import "components/validation"; 14 | 15 | @import "views/home"; 16 | @import "views/movie"; 17 | @import "views/person"; 18 | @import "views/profile"; 19 | 20 | .nt-app { 21 | background-image: url("/neo4j_background3.gif"); 22 | } 23 | 24 | .nt-box { 25 | border: 1px solid lightgrey; 26 | border-radius: .5rem; 27 | padding: .2rem; 28 | background: #FFFFFF; 29 | 30 | &-title { 31 | padding: .5rem; 32 | color: saddlebrown; 33 | font-size: 1.2rem; 34 | } 35 | 36 | &-row { 37 | padding: .5rem; 38 | margin: 0; 39 | } 40 | } 41 | 42 | ul { 43 | list-style: none; 44 | li { 45 | display: inline-block; 46 | width: auto; 47 | padding: .5rem; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /web/src/styles/views/_home.scss: -------------------------------------------------------------------------------- 1 | .nt-home { 2 | 3 | &-header { 4 | text-align: center; 5 | } 6 | 7 | .nt-box { 8 | margin-bottom: 1rem; 9 | } 10 | 11 | &-featured { 12 | ul { 13 | list-style: none; 14 | display: -webkit-flex; 15 | display: flex; 16 | -webkit-justify-content: space-around; 17 | justify-content: space-around; 18 | li { 19 | padding: 1rem; 20 | } 21 | } 22 | } 23 | 24 | &-by-genre { 25 | ul { 26 | list-style: none; 27 | li { 28 | display: inline-block; 29 | width: auto; 30 | padding: .5rem; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /web/src/styles/views/_movie.scss: -------------------------------------------------------------------------------- 1 | .nt-movie { 2 | 3 | .nt-box { 4 | margin-bottom: 1rem; 5 | } 6 | 7 | &-title { 8 | text-align: center; 9 | } 10 | 11 | &-rating { 12 | overflow: hidden; 13 | 14 | strong { 15 | float: left; 16 | } 17 | } 18 | 19 | &-poster { 20 | width: 100%; 21 | } 22 | 23 | &-aside { 24 | .nt-box { 25 | margin-top: 1rem; 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /web/src/styles/views/_person.scss: -------------------------------------------------------------------------------- 1 | .nt-person { 2 | .nt-box { 3 | margin-bottom: 1rem; 4 | } 5 | 6 | &-poster { 7 | width: 100%; 8 | } 9 | 10 | &-header { 11 | text-align: center; 12 | } 13 | 14 | &-movies { 15 | 16 | &-movie-role { 17 | font-style: italic; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /web/src/styles/views/_profile.scss: -------------------------------------------------------------------------------- 1 | .nt-profile { 2 | .nt-box { 3 | margin: .5rem 0; 4 | } 5 | 6 | &-movie { 7 | padding-top: 1rem; 8 | 9 | &-title { 10 | font-size: .8rem; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /web/src/utils/ErrorUtils.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import humps from 'humps'; 3 | 4 | export default class ErrorUtils { 5 | static getApiErrors(apiError) { 6 | return humps.camelizeKeys(_.get(apiError, 'data', {})); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /web/src/utils/ValidationUtils.js: -------------------------------------------------------------------------------- 1 | export default class ValidationUtils { 2 | static checkEmail(email, message) { 3 | var regex = /[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}/igm; 4 | if (email && !regex.test(email)) { 5 | return message || 'Please enter a valid email address.'; 6 | } 7 | } 8 | 9 | static checkUrl(url, message) { 10 | var regex = /^(http)s?(:\/\/)([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/igm; // eslint-disable-line no-useless-escape 11 | if (url && !regex.test(url)) { 12 | return message || 'Please enter a valid URL. (ex. http(s)://domain.com)'; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /web/static.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "build/", 3 | "routes": { 4 | "/**": "index.html" 5 | }, 6 | "proxies": { 7 | "/api/v0": { 8 | "origin": "${REACT_APP_PROXY_URL}" 9 | } 10 | } 11 | } 12 | 13 | --------------------------------------------------------------------------------