[
  {
    "path": ".appveyor.yml",
    "content": "environment:\n  nodejs_version: \"11\"\n\ninstall:\n  - ps: Install-Product node $env:nodejs_version\n  - yarn install\n\nbuild_script:\n  - yarn build\n\ntest_script:\n  - yarn test\n\ncache:\n  - \"%LOCALAPPDATA%\\\\Yarn\"\n"
  },
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    \"@babel/preset-env\",\n    \"@babel/preset-react\",\n    \"@babel/preset-typescript\"\n  ],\n  \"plugins\": [\n    [\n      \"@babel/plugin-proposal-class-properties\",\n      {\n        \"loose\": true,\n      }\n    ],\n    \"@babel/plugin-syntax-dynamic-import\",\n    \"@babel/plugin-transform-runtime\",\n    \"react-hot-loader/babel\",\n    [\n      \"prismjs\",\n      {\n        \"languages\": [\n          \"javascript\",\n          \"css\",\n          \"markup\",\n          \"cpp\",\n          \"bash\",\n          \"docker\",\n          \"go\",\n          \"json\",\n          \"markdown\",\n          \"python\",\n          \"jsx\",\n          \"tsx\",\n          \"scss\",\n          \"typescript\"\n        ],\n        \"plugins\": [\n          \"autolinker\",\n          \"command-line\"\n        ],\n        \"theme\": \"default\",\n        \"css\": true\n      }\n    ]\n  ]\n}\n"
  },
  {
    "path": ".circleci/config.yml",
    "content": "# Javascript Node CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-javascript/ for more details\n#\n\nversion: 2\n\njobs:\n  build:\n    working_directory: ~\n    docker:\n      - image: circleci/node:11\n    steps:\n      - checkout\n      - run:\n          name: Install Docker client\n          command: |\n            set -x\n            VER=\"18.09.1\"\n            curl -L -o /tmp/docker.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz\n            tar -xz -C /tmp -f /tmp/docker.tgz\n            sudo cp -r /tmp/docker/* /usr/bin\n      - run:\n          name: Install Docker Compose\n          command: |\n            set -x\n            VER=\"1.23.2\"\n            curl -L https://github.com/docker/compose/releases/download/$VER/docker-compose-`uname -s`-`uname -m` > /tmp/docker-compose\n            sudo cp /tmp/docker-compose /usr/local/bin/docker-compose\n            sudo chmod +x /usr/local/bin/docker-compose\n      - setup_remote_docker\n      - run:\n          name: Run Docker Compose\n          command: docker-compose up --build -d\n      - run:\n          name: Install dependencies\n          command: yarn install\n      - run:\n          name: Run test\n          command: yarn test\n"
  },
  {
    "path": ".commitlintrc.yml",
    "content": "extends: ['armour']\n"
  },
  {
    "path": ".dockerignore",
    "content": "# General\n.DS_Store\n*~\n*.swp\n*.log\n\n# Thumbnails\n._*\nThumbs.db\n\n# Trash folder or files\n.Trashes\n.Trash-*\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Editor config folder\n.idea/\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n\n# Code coverage\ncoverage/\n.coveralls.yml\n\n# Misc\n__mocks__/\n.circleci/\n.github/\nnode_modules/\ndist/\n.appveyor.yml\n.*lintrc*\n.*lintignore*\nyarn.lock\n"
  },
  {
    "path": ".editorconfig",
    "content": "# http://editorconfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with a newline ending every file\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n# Trailing space override for markdown file\n[*.md]\ntrim_trailing_whitespace = false\n\n# Indentation override for js(x), ts(x) files\n[*.{js,jsx,ts,tsx}]\nindent_size = 2\nindent_style = space\n\n# Indentation override for css files\n[*.{css,styl,scss,less,sass}]\nindent_size = 2\nindent_style = space\n\n# Indentation override for html files\n[*.html]\nindent_size = 2\nindent_style = space\n\n# Indentation override for config files\n[*.{json,yml}]\nindent_size = 2\nindent_style = space\n\n# Minified JavaScript files shouldn't be changed\n[**.min.js]\nindent_style = ignore\ninsert_final_newline = ignore\n"
  },
  {
    "path": ".eslintignore",
    "content": "node_modules/\ndist/\ncoverage/\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"es6\": true\n  },\n  \"extends\": [\n    \"airbnb\"\n  ],\n  \"rules\": {\n    \"import/export\": \"error\",\n    \"import/first\": \"error\",\n    \"import/no-commonjs\": \"error\",\n    \"import/no-deprecated\": \"error\",\n    \"import/no-duplicates\": \"error\",\n    \"import/no-unresolved\": \"error\",\n    \"import/no-webpack-loader-syntax\": \"error\",\n    \"linebreak-style\": \"off\",\n    \"indent\": [\n      \"error\",\n      2\n    ],\n    \"no-console\": [\n      \"error\",\n      {\n        \"allow\": [\n          \"warn\",\n          \"error\",\n          \"info\"\n        ]\n      }\n    ],\n    \"quotes\": [\n      \"error\",\n      \"single\",\n      {\n        \"allowTemplateLiterals\": true\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Details per file setting:\n#   text    These files should be normalized (i.e. convert CRLF to LF).\n#   binary  These files are binary and should be left untouched.\n\n# Auto detect text files and perform LF normalization\n* text=auto\n# The above will handle all files NOT found below\n\n# SOURCE CODE\n*.bat      text eol=crlf\n*.css      text\n*.js       text\n*.jsx      text\n*.ejs      text\n*.html     text\n*.less     text\n*.sass     text\n*.scss     text\n*.sh       text eol=lf\n*.sql      text\n*.ts       text\n*.tsx      text\n\n# DOCKER\n*.dockerignore    text\nDockerfile        text\n\n# COMPILED FILES\n*.dll   binary\n*.dylib binary\n*.exe   binary\n*.so    binary\n\n# DOCUMENTATION\n*.md         text\n*.txt        text\nLICENSE      text\n\n# CONFIGS\n.editorconfig  text\n.gitattributes text\n.gitignore     text\n*.conf         text\n*.config       text\n*.json         text\n*.lock         text\n*.toml         text\n*.yaml         text\n*.yml          text\n\n# GRAPHICS\n*.ai   binary\n*.bmp  binary\n*.eps  binary\n*.gif  binary\n*.heic binary\n*.ico  binary\n*.jpeg binary\n*.jpg  binary\n*.pdf  binary\n*.png  binary\n*.psb  binary\n*.psd  binary\n*.svg  text\n*.tif  binary\n*.tiff binary\n*.wbmp binary\n*.webp binary\n\n# AUDIO\n*.aac  binary\n*.flac binary\n*.m4a  binary\n*.mid  binary\n*.midi binary\n*.mp3  binary\n*.mp4a binary\n*.mpga binary\n*.oga  binary\n*.ogg  binary\n*.wav  binary\n*.weba binary\n\n# VIDEO\n*.3g2  binary\n*.3gp  binary\n*.asf  binary\n*.avi  binary\n*.f4v  binary\n*.fla  binary\n*.flv  binary\n*.m4v  binary\n*.mov  binary\n*.mp4  binary\n*.mpeg binary\n*.mpg  binary\n*.ogv  binary\n*.qt   binary\n*.swf  binary\n*.webm binary\n\n# ARCHIVES\n*.7z  binary\n*.gz  binary\n*.jar binary\n*.rar binary\n*.tar binary\n*.zip binary\n\n# FONTS\n*.eot   binary\n*.otf   binary\n*.ttf   binary\n*.woff  binary\n*.woff2 binary\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of experience,\neducation, socio-economic status, nationality, personal appearance, race,\nreligion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at [INSERT EMAIL ADDRESS]. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html)\n\n[homepage]: https://www.contributor-covenant.org\n"
  },
  {
    "path": ".github/COMMIT_CONVENTION.md",
    "content": "# Git Commit Message Convention\n\n> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).\n\n## TL;DR\n\nMessages must be matched by the following regex:\n\n``` js\n/^(Revert: )?(Feature|Fix|Docs|Improve|Config|Example|Refactor|Style|Test|Build|CI)(\\(.+\\))?: .{1,80}/\n```\n\n## Commit Message Format\n\nEach commit message consists of a **header**, a **body** and a **footer**.  The header has a special format that includes a **type**, a **scope** and a **subject**:\n\n```text\n<type>(<scope>): <subject>\n<BLANK LINE>\n<body>\n<BLANK LINE>\n<footer>\n```\n\nThe **header** is mandatory and the **scope** of the header is optional.\n\nAny line of the commit message cannot be longer 100 characters! This allows the message to be easier\nto read on GitHub as well as in various git tools.\n\nThe footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.\n\nSamples: (even more [samples](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/commits/master))\n\n```text\nDocs(changelog): update changelog to beta.5\n```\n\n```text\nFeature($browser): onUrlChange event (popstate/hashchange/polling)\n\nAdded new event to $browser:\n- forward popstate event if available\n- forward hashchange event if popstate not available\n- do polling when neither popstate nor hashchange available\n\nBreaks $browser.onHashChange, which was removed (use onUrlChange instead)\n```\n\n```text\nFix(release): need to depend on latest rxjs and zone.js\n\nThe version in our package.json gets copied to the one we publish, and users need the latest of these.\n\nCloses #123, #245, #992\n\nBREAKING CHANGE: isolate scope bindings definition has changed and\nthe inject option for the directive controller injection was removed.\n\nTo migrate the code follow the example below:\n\nBefore:\n\nscope: {\n  myAttr: 'attribute',\n  myBind: 'bind',\n  myExpression: 'expression',\n  myEval: 'evaluate',\n  myAccessor: 'accessor'\n}\n\nAfter:\n\nscope: {\n  myAttr: '@',\n  myBind: '@',\n  myExpression: '&',\n  // myEval - usually not useful, but in cases where the expression is assignable, you can use '='\n  myAccessor: '=' // in directive's template change myAccessor() to myAccessor\n}\n\nThe removed `inject` wasn't generaly useful for directives so there should be no code using it.\n```\n\n### Revert\n\nIf the commit reverts a previous commit, it should begin with `Revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.\n\n### Type\n\nMust be one of the following:\n\n* **Build**: Changes that affect the build system or external dependencies (example scopes: gulp, npm, yarn)\n* **CI**: Changes to CI related configuration files and scripts (example scopes: travis, circle, browserstack)\n* **Config**: Changes to other configuration files (example scopes: webpack, babel, docker)\n* **Docs**: Documentation only changes (example scopes: readme, changelog)\n* **Example**: Changes for example code\n* **Feature**: A new feature\n* **Fix**: A bug fix\n* **Improve**: Backwards-compatible enhancement changes\n* **Refactor**: A code change that neither fixes a bug nor adds a feature\n* **Style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)\n* **Test**: Changes for testing code\n\nIf the prefix is `Feature` or `Fix`, it will appear in the changelog. However if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.\n\n### Scope\n\nThe scope could be anything specifying place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc...\n\n### Subject\n\nThe subject contains succinct description of the change:\n\n* use the imperative, present tense: \"change\" not \"changed\" nor \"changes\"\n* don't capitalize the first letter\n* no dot (.) at the end\n\n### Body\n\nJust as in the **subject**, use the imperative, present tense: \"change\" not \"changed\" nor \"changes\".\nThe body should include the motivation for the change and contrast this with previous behavior.\n\n### Footer\n\nThe footer should contain any information about **Breaking Changes** and is also the place to\nreference GitHub issues that this commit **Closes**.\n\n**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.\n\nA detailed explanation can be found in this [document](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit)\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing\n\n## Code of Conduct\n\nHelp us keep express-webpack-react-redux-typescript-boilerplate open and inclusive. Please read and follow the [Code of Conduct](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/blob/master/.github/CODE_OF_CONDUCT.md).\n\n## Found a Bug\n\nIf you find a bug in the source code, you can help us by [submitting an issue](#submitting-an-issue) to our [GitHub Repository](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate). Even better, you can [submit a Pull Request](#submitting-a-pull-request) with a fix.\n\n## Missing a Feature\n\nYou can *request* a new feature by [submitting an issue](#submitting-an-issue) to our GitHub Repository. If you would like to *implement* a new feature, please submit an issue with a proposal for your work first, to be sure that we can use it. Please consider what kind of change it is:\n\n* For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed. This will also allow us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.\n\n* **Small Features** can be crafted and directly [submitted as a Pull Request](#submitting-a-pull-request).\n\n## Submission Guidelines\n\n### Submitting an Issue\n\nBefore you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.\n\nWe want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs, we will systematically ask you to provide a minimal reproduction scenario. Having a live, reproducible scenario gives us a wealth of important information without going back & forth to you with additional questions.\n\nWe will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.\n\nUnfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that doesn't have enough info to be reproduced.\n\nYou can file new issues by filling out the [new issue form](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/issues/new).\n\n### Submitting a Pull Request\n\nBefore you submit your Pull Request (PR) consider the following guidelines:\n\n1. Search [GitHub](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate effort.\n\n1. Fork this repo.\n\n1. Make your changes in a new git branch.\n\n    ```shell\n    git checkout -b my-new-feature master\n    ```\n\n1. Commit your changes using a descriptive commit message that follows our [commit message convention](#commit-message-convention). Adherence to these conventions is necessary because release notes are automatically generated from these messages.\n\n    ```shell\n    git commit -am 'Add some feature'\n    ```\n\n1. Push your branch.\n\n    ```shell\n    git push origin my-new-feature\n    ```\n\n1. Send a pull request :D\n\nThat's it! Thank you for your contribution!\n\n#### After your pull request is merged\n\nAfter your pull request is merged, you can safely delete your branch and pull the changes\nfrom the main (upstream) repository:\n\n* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:\n\n    ```shell\n    git push origin --delete my-new-feature\n    ```\n\n* Check out the master branch:\n\n    ```shell\n    git checkout master -f\n    ```\n\n* Delete the local branch:\n\n    ```shell\n    git branch -D my-new-feature\n    ```\n\n* Update your master with the latest upstream version:\n\n    ```shell\n    git pull\n    ```\n\n## Commit Message Convention\n\nWe have very precise rules over how our git commit messages can be formatted.  This leads to **more readable messages** that are easy to follow when looking through the **project history**.  But also, we use the git commit messages to **generate the change log**.\n\nPlease read and follow the [Commit Message Format](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/blob/master/.github/COMMIT_CONVENTION.md).\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--\nPLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.\n\nISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION.\n-->\n\n<!-- Please search GitHub for a similar issue or PR before submitting -->\n\n# I'm submitting a\n\n<!-- \nE.g.\n    bug report,\n    feature request,\n    performance issue,\n    regression (a behavior that used to work and stopped working in a new release),\n    documentation issue or request,\n    or others... (please describe)\n-->\n\n## Current behavior\n\n<!-- Describe how the issue manifests. -->\n\n## Expected behavior\n\n<!-- Describe what the desired behavior would be. -->\n\n## Minimal reproduction of the problem with instructions\n\n<!--\nFor bug reports please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via github repo or similar tools.\n-->\n\n## What is the motivation / use case for changing the behavior\n\n<!-- Describe the motivation or the concrete use case. -->\n\n## Environment\n\n<!-- Anything may be useful?  Platform, Operating system version, IDE, package manager, HTTP server, ... -->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nPlease make sure to read the Pull Request Guidelines:\nhttps://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/blob/master/.github/CONTRIBUTING.md#submitting-a-pull-request\n-->\n\n<!-- PULL REQUEST TEMPLATE -->\n\n**Make sure the PR fulfills these requirements:**\n\n- When resolving a specific issue, make sure it's referenced in the PR's title (e.g. `Closes #xxx[,#xxx]`, where \"xxx\" is the issue number)\n\n- If adding a **new feature**, the PR's description includes: A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)\n\n- If this PR introduce a **breaking change**, please describe the impact and migration path for existing applications\n\n**What kind of change does this PR introduce?**\n\n<!--\nE.g.\n    bugfix,\n    feature,\n    code style update,\n    refactor,\n    build-related changes,\n    or others... (please describe)\n-->\n\n**More information:**\n"
  },
  {
    "path": ".github/main.workflow",
    "content": "workflow \"Deploy on Heroku\" {\n  on = \"push\"\n  resolves = [\n    \"verify\",\n  ]\n}\n\n# Login\naction \"login\" {\n  uses = \"actions/heroku@master\"\n  args = \"container:login\"\n  secrets = [\"HEROKU_API_KEY\"]\n}\n\n# Push\naction \"push\" {\n  needs = [\"login\"]\n  uses = \"actions/heroku@master\"\n  args = [\"container:push\", \"--app\", \"$HEROKU_APP\", \"web\"]\n  secrets = [\"HEROKU_API_KEY\"]\n  env = {\n    HEROKU_APP = \"express-react-typescript\"\n  }\n}\n\n# Release\naction \"deploy\" {\n  needs = [\"push\"]\n  uses = \"actions/heroku@master\"\n  args = [\"container:release\", \"--app\", \"$HEROKU_APP\", \"web\"]\n  secrets = [\"HEROKU_API_KEY\"]\n  env = {\n    HEROKU_APP = \"express-react-typescript\"\n  }\n}\n\n# Verify\naction \"verify\" {\n  needs = [\"deploy\"]\n  uses = \"actions/heroku@master\"\n  args = [\"apps:info\", \"$HEROKU_APP\"]\n  secrets = [\"HEROKU_API_KEY\"]\n  env = {\n    HEROKU_APP = \"express-react-typescript\"\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# General\n.DS_Store\n*~\n*.swp\n*.log\n\n# Thumbnails\n._*\nThumbs.db\n\n# Trash folder or files\n.Trashes\n.Trash-*\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Editor config folder\n.idea/\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n\n# Code coverage\ncoverage/\n.coveralls.yml\n\n# Misc\nnode_modules/\ndist/\n\n# Redis data\nbackend/redis-data/*\n!backend/redis-data/.gitkeep\n"
  },
  {
    "path": ".stylelintignore",
    "content": "node_modules/\ndist/\ncoverage/\n"
  },
  {
    "path": ".stylelintrc",
    "content": "{\n  \"extends\": \"stylelint-config-standard\"\n}\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:alpine\n\nARG PORT=${PORT}\n\nWORKDIR /usr/src/app\nCOPY . /usr/src/app/\n\nRUN apk add --no-cache --update make gcc libc-dev libpng-dev automake autoconf libtool nasm\nRUN yarn install\nRUN yarn build\n\nEXPOSE ${PORT}\n\nCMD [\"yarn\", \"serve\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Chong Guo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# express-webpack-react-redux-typescript-boilerplate\n\n[![Dependency Status](https://david-dm.org/Armour/express-webpack-react-redux-typescript-boilerplate/status.svg)](https://david-dm.org/Armour/express-webpack-react-redux-typescript-boilerplate)\n[![CircleCI](https://circleci.com/gh/Armour/express-webpack-react-redux-typescript-boilerplate/tree/master.svg?style=shield)](https://circleci.com/gh/Armour/express-webpack-react-redux-typescript-boilerplate/tree/master)\n[![Appveyor](https://ci.appveyor.com/api/projects/status/github/Armour/express-webpack-react-redux-typescript-boilerplate?svg=true&branch=master)](https://ci.appveyor.com/api/projects/status/github/Armour/express-webpack-react-redux-typescript-boilerplate?svg=true&branch=master)\n[![Coverage Status](https://coveralls.io/repos/github/Armour/express-webpack-react-redux-typescript-boilerplate/badge.svg?branch=master)](https://coveralls.io/github/Armour/express-webpack-react-redux-typescript-boilerplate?branch=master)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)\n[![Tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![Template from jarvis](https://img.shields.io/badge/Hi-Jarvis-ff69b4.svg)](https://github.com/Armour/Jarvis)\n\n## Example\n\n* [Demo Page](https://express-react-typescript.herokuapp.com/) - contains classic todo list, async server call, and 404 page with random [moe images](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/tree/master/frontend/src/pages/NotFoundPage/assets/images). (Support multi-language, currently English, Chinese, and Japanese)\n\n  ![Home Page](https://user-images.githubusercontent.com/5276065/44188928-402d7800-a0d5-11e8-8445-0c0dece815c2.png)\n\n  ![React Page](https://user-images.githubusercontent.com/5276065/44188929-402d7800-a0d5-11e8-8580-f9a330765f6a.png)\n\n  ![404 Page](https://user-images.githubusercontent.com/5276065/44188930-402d7800-a0d5-11e8-919c-a4baa2c969ab.png)\n\n## Stack\n\n* [x] [yarn](https://github.com/yarnpkg/yarn) - dependency manager\n* [x] [express](http://expressjs.com/) - node.js web framework for backend\n* [x] [postgresql](https://www.postgresql.org/) - advanced open source database\n* [x] [materialize](http://materializecss.com/) - a modern responsive front-end framework based on Material Design\n* [x] [sass](https://github.com/sass/sass) - CSS pre-processors\n* [x] [postcss](https://github.com/postcss/postcss) - CSS post-processor\n* [x] [css-modules](https://github.com/css-modules/css-modules) - for default scoped/local css\n* [x] [typescript](https://github.com/Microsoft/TypeScript) - a typed superset of javascript that scales\n* [x] [webpack 4](https://github.com/webpack/webpack) - module bundler\n* [x] [webpack-dev-server](https://github.com/webpack/webpack-dev-server) - serves a webpack app in development mode with hot reload\n* [x] followed [ES6 standard](https://github.com/lukehoban/es6features)\n* [x] [babel](https://babeljs.io/) - a JavaScript compiler that compile ES6 to ES5\n* [x] [react](https://facebook.github.io/react/) - a JavaScript library for building user interfaces\n* [x] [react-hot-loader 4](https://github.com/gaearon/react-hot-loader) - hot module reload!\n* [x] [react-router 4](https://github.com/ReactTraining/react-router) - declarative routing for React\n* [x] [react-redux 6](https://github.com/reactjs/react-redux) - the official react bindings for [redux 4](https://github.com/reactjs/redux) (a predictable state container for js apps)\n* [x] [react-saga](https://github.com/redux-saga/redux-saga/) - make redux asynchronous flows easy to read, write and test, the replacement for [redux-thunk](https://github.com/reduxjs/redux-thunk)\n* [x] [connected-react-router 6](https://github.com/supasate/connected-react-router) - a redux binding for react-router 4, the replacement for [react-router-redux v5](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-redux)\n* [x] [react-i18next](https://github.com/i18next/react-i18next) - internationalization for react done right\n* [x] [immutable.js](https://github.com/facebook/immutable-js/) - persistent Immutable data structures for react redux state management\n* [x] [editorconfig](http://editorconfig.org/) - maintain consistent coding styles between different editors and IDEs\n* [x] [eslint](http://eslint.org/) - lint javascript files (.js, .jsx)\n* [x] [tslint](https://palantir.github.io/tslint/) - lint typescript files (.ts, .tsx)\n* [x] [stylelint](https://stylelint.io/) - lint style files (.css, .scss)\n* [x] [commitlint](https://github.com/marionebl/commitlint) - lint git commit messages\n* [x] [jest](https://facebook.github.io/jest/) - painless javascript testing\n* [x] [coveralls](https://coveralls.io/) - test coverage\n* [x] [husky](https://github.com/typicode/husky) - git hooks\n* [x] [circle-ci 2](https://circleci.com/) - continuous integration tool for testing and deployment\n* [x] [heroku](https://www.heroku.com/) - a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud.\n* [x] [docker](https://github.com/docker/docker) - the open-source application container engine\n* [x] [RESTful API design](https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design) - follow RESTful api design best practice\n\n## How to run the sample code\n\n### Prerequisite\n\n* `yarn` or `npm`\n* (optional) `docker` with `docker-compose`\n\n### Quickest way\n\nThe easiest way to run the example project is to use `docker-compose`:\n\n```bash\ndocker-compose up --build\n```\n\nthat's it :)\n\nYou can also follow instructions below if you want to customize it.\n\n### Install project dependencies\n\nGo to project root directory:\n\n```bash\nyarn install\n```\n\nIf you find permission problem when trying to install yarn globally, check [this](https://github.com/yarnpkg/yarn/issues/1060#issuecomment-268160528) out.\n\n### Setup database and session store\n\nYou can either\n\n* setup `postgresql` and `redis` using docker images:\n\n```bash\ndocker-compose up -d postgres redis\n```\n\nor\n\n* maintain it by yourself, if so, make sure you set the right config in `backend/config.json`.\n\n### Build & Run\n\nOn development (with hot reload):\n\n```bash\nyarn dev\n```\n\nOn production (with [terser](https://github.com/terser-js/terser) and other optimazitions):\n\n```bash\nyarn prod\n```\n\n## Profile assets bundle\n\nThanks to [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer), assets bundle can be analyzed and optimized through [DLL Plugin](https://webpack.js.org/plugins/dll-plugin/).\n\n```bash\nyarn profile\n```\n\n## Run test\n\n```bash\nyarn test\n```\n\n## Code coverage\n\n```bash\nyarn coverage\n```\n\n## Deployment\n\nEvery push on master branch will trigger [Github Actions](.github/main.workflow) for heroku deployment.\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/blob/master/.github/CONTRIBUTING.md)\n\n## License\n\n[MIT License](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/blob/master/LICENSE)\n"
  },
  {
    "path": "__mocks__/fileMock.tsx",
    "content": "export default 'test-file-stub';\n"
  },
  {
    "path": "__mocks__/react-i18next.tsx",
    "content": "export const Translation = ({ children }: any) => children((k: string) => k, { i18n: {} });\n"
  },
  {
    "path": "backend/config.json",
    "content": "{\n  \"pgsql_hostname_dev\": \"localhost\",\n  \"pgsql_hostname_prod\": \"postgres\",\n  \"pgsql_username\": \"docker\",\n  \"pgsql_password\": \"docker\",\n  \"pgsql_database\": \"docker\",\n  \"redis_hostname_dev\": \"localhost\",\n  \"redis_hostname_prod\": \"redis\",\n  \"redis_port\": \"6379\",\n  \"http_port\": \"3003\"\n}\n"
  },
  {
    "path": "backend/controllers/notes.js",
    "content": "import status from 'http-status';\n\nimport db from '../db';\n\nexport const getAllNotes = async (req, res) => {\n  try {\n    const { rows: notes } = await db.query('SELECT * FROM notes ORDER BY id', []);\n    return res.status(status.OK).json({\n      data: {\n        notes,\n      },\n      error: null,\n    });\n  } catch (e) {\n    return res.status(status.INTERNAL_SERVER_ERROR).json({\n      data: null,\n      error: {\n        message: e.message,\n        // You can also add below fields for better error displaying\n        //   code - error code for your project\n        //   developerMessage - debug message for developers\n      },\n    });\n  }\n};\n\nexport const getNote = async (req, res) => {\n  try {\n    const { id } = req.params;\n    const { rows: notes } = await db.query('SELECT * FROM notes WHERE id = $1', [id]);\n    if (notes === undefined || notes.length === 0) {\n      return res.status(status.NOT_FOUND).json({\n        data: null,\n        error: {\n          message: `Note with id ${id} not found.`,\n        },\n      });\n    }\n    return res.status(status.OK).json({\n      data: {\n        note: notes[0],\n      },\n      error: null,\n    });\n  } catch (e) {\n    return res.status(status.INTERNAL_SERVER_ERROR).json({\n      data: null,\n      error: {\n        message: e.message,\n      },\n    });\n  }\n};\n\nexport const addNote = async (req, res) => {\n  try {\n    const { content } = req.body;\n    const { rows: notes } = await db.query('INSERT INTO notes(content) VALUES($1) RETURNING *', [content]);\n    if (notes === undefined || notes.length === 0) {\n      return res.status(status.NOT_FOUND).json({\n        data: null,\n        error: {\n          message: 'Insert note failed.',\n        },\n      });\n    }\n    return res.status(status.OK).json({\n      data: {\n        note: notes[0],\n      },\n      error: null,\n    });\n  } catch (e) {\n    return res.status(status.INTERNAL_SERVER_ERROR).json({\n      data: null,\n      error: {\n        message: e.message,\n      },\n    });\n  }\n};\n\nexport const editNote = async (req, res) => {\n  try {\n    const { id } = req.params;\n    const { content } = req.body;\n    const { rows: notes } = await db.query('UPDATE notes SET content = $1 WHERE id = $2 RETURNING *', [content, id]);\n    if (notes === undefined || notes.length === 0) {\n      return res.status(status.NOT_FOUND).json({\n        data: null,\n        error: {\n          message: `Update note with id ${id} failed.`,\n        },\n      });\n    }\n    return res.status(status.OK).json({\n      data: {\n        note: notes[0],\n      },\n      error: null,\n    });\n  } catch (e) {\n    return res.status(status.INTERNAL_SERVER_ERROR).json({\n      data: null,\n      error: {\n        message: e.message,\n      },\n    });\n  }\n};\n\nexport const removeNote = async (req, res) => {\n  try {\n    const { id } = req.params;\n    const { rows: notes } = await db.query('DELETE FROM notes WHERE id = $1 RETURNING *', [id]);\n    if (notes === undefined || notes.length === 0) {\n      return res.status(status.NOT_FOUND).json({\n        data: null,\n        error: {\n          message: `Delete note with id ${id} failed.`,\n        },\n      });\n    }\n    return res.status(status.OK).json({\n      data: {\n        note: notes[0],\n      },\n      error: null,\n    });\n  } catch (e) {\n    return res.status(status.INTERNAL_SERVER_ERROR).json({\n      data: null,\n      error: {\n        message: e.message,\n      },\n    });\n  }\n};\n"
  },
  {
    "path": "backend/db/index.js",
    "content": "import { Pool } from 'pg';\n\nimport config from '../config.json';\n\nconst isProduction = process.env.NODE_ENV === 'production';\n\nlet pool;\n\n// Create a postgresql connection pool\nif (process.env.DATABASE_URL !== undefined) {\n  // For Heroku Postgres deployment\n  // See https://devcenter.heroku.com/articles/heroku-postgresql#connecting-in-node-js\n  pool = new Pool({\n    connectionString: process.env.DATABASE_URL,\n    ssl: true,\n  });\n} else {\n  // All config is optional, the environment variables will be used if the config is not present\n  pool = new Pool({\n    host: isProduction\n      ? config.pgsql_hostname_prod\n      : config.pgsql_hostname_dev, // Default: process.env.PGHOST\n    database: config.pgsql_database, // Default: process.env.PGDATABASE\n    user: config.pgsql_username, // Default: process.env.PGUSER\n    password: config.pgsql_password, // Default: process.env.PGPASSWORD\n    port: config.pgsql_port, // Default: process.env.PGPORT\n  });\n}\n\n// Initializes a connection pool\nexport default {\n  query: (text, params) => pool.query(text, params),\n  pool,\n};\n"
  },
  {
    "path": "backend/jsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\"\n  }\n}\n"
  },
  {
    "path": "backend/redis-data/.gitkeep",
    "content": ""
  },
  {
    "path": "backend/routes/index.js",
    "content": "import express from 'express';\n\nimport notesRtr from './notes';\n\nconst router = express.Router();\n\nrouter.use('/notes', notesRtr);\n\nexport default router;\n"
  },
  {
    "path": "backend/routes/notes.js",
    "content": "import express from 'express';\n\nimport {\n  getAllNotes, getNote, addNote, editNote, removeNote,\n} from '../controllers/notes';\n\nconst router = express.Router();\n\nrouter.get('/', getAllNotes);\nrouter.get('/:id', getNote);\nrouter.post('/', addNote);\nrouter.put('/:id', editNote);\nrouter.delete('/:id', removeNote);\n\nexport default router;\n"
  },
  {
    "path": "backend/server.js",
    "content": "import express from 'express';\nimport path from 'path';\nimport logger from 'morgan';\nimport cookieParser from 'cookie-parser';\nimport bodyParser from 'body-parser';\nimport compression from 'compression';\nimport helmet from 'helmet';\nimport session from 'express-session';\nimport connectRedis from 'connect-redis';\nimport 'colors';\n\nimport config from './config.json';\nimport router from './routes/index';\n\nconst app = express();\nconst isProduction = process.env.NODE_ENV === 'production';\nconst port = process.env.PORT || config.http_port;\nconst RedisStore = connectRedis(session);\nconst server = require('http').createServer(app);\n\nif (isProduction) {\n  app.use(helmet());\n  app.disable('x-powered-by');\n  app.use(logger('combined'));\n  app.set('trust proxy', 1);\n} else {\n  app.use(logger('dev'));\n}\n\napp.use(compression());\napp.use(bodyParser.json({\n  limit: '20mb',\n}));\napp.use(bodyParser.urlencoded({\n  limit: '20mb',\n  extended: true,\n}));\napp.use(cookieParser());\napp.use(session({\n  store: new RedisStore({\n    host: isProduction ? config.redis_hostname_prod : config.redis_hostname_dev,\n    port: config.redis_port,\n  }),\n  secret: 'keyboard cat',\n  saveUninitialized: false,\n  resave: false,\n  cookie: {\n    httpOnly: !isProduction,\n    secure: isProduction,\n  },\n}));\n\n// api router\napp.use('/api/v1', router);\n\nif (isProduction) {\n  // Serve dist files if is production mode\n  const distPath = path.resolve(__dirname, '../frontend/dist/prod');\n  app.use(express.static(distPath));\n  app.get('*', (_, res) => {\n    res.sendFile(`${distPath}/index.html`);\n  });\n} else {\n  // Return 404 for non-exist api\n  app.get('*', (req, res) => {\n    res.status(404).send(`Api not exist for ${req.url}`);\n  });\n}\n\n// Start server listen on specific port\nserver.listen(port, (error) => {\n  if (error) {\n    console.error(`\\n${error}`);\n    server.close();\n    process.exit(1);\n  }\n  if (isProduction) {\n    console.info(`\\nExpress: Listening on port ${port}, open up http://localhost:${port}/ in your broswer!\\n`.green);\n  } else {\n    console.info(`\\nExpress: Serve api on http://localhost:${port}/ \\n`);\n  }\n});\n\nconst stopHandler = (signal) => {\n  console.error(`\\nExit process in responding to %s`.red, signal);\n  server.close();\n  process.exit(1);\n};\n\nprocess.on('SIGTERM', stopHandler, 'SIGTERM');\nprocess.on('SIGINT', stopHandler, 'SIGINT');\nprocess.on('SIGHUP', stopHandler, 'SIGINT');\n"
  },
  {
    "path": "backend/sql/data.sql",
    "content": "INSERT INTO notes (content) VALUES ('note data 1');\nINSERT INTO notes (content) VALUES ('note data 2');\n"
  },
  {
    "path": "backend/sql/schema.sql",
    "content": "Create Table notes (\n    id serial primary key,\n    content text not null,\n    created timestamptz default now()\n);\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3.7'\n\nservices:\n  web:\n    image: cguo/express-webpack-react-redux-typescript-boilerplate\n    build: # ignored when deploying a stack in swarm mode or kubernetes\n      context: .\n      args:\n        - PORT=${PORT}\n    ports:\n      - \"${PORT}:${PORT}\"\n    depends_on: # ignored when deploying a stack in swarm mode or kubernetes\n      - postgres\n      - redis\n    restart: always # ignored when deploying a stack in swarm mode or kubernetes\n    deploy: # ignored by docker-compose\n      replicas: 3\n      restart_policy:\n        condition: on-failure\n\n  postgres:\n    image: postgres:alpine\n    volumes:\n      - ./backend/sql/schema.sql:/docker-entrypoint-initdb.d/1-schema.sql\n      - ./backend/sql/data.sql:/docker-entrypoint-initdb.d/2-data.sql\n    environment:\n      - POSTGRES_DB=${POSTGRES_DB}\n      - POSTGRES_USER=${POSTGRES_USER}\n      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}\n    ports:\n      - \"5432:5432\"\n    restart: always # ignored when deploying a stack in swarm mode or kubernetes\n    deploy: # ignored by docker-compose\n      restart_policy:\n        condition: on-failure\n\n  redis:\n    image: redis:alpine\n    ports:\n      - \"6379:6379\"\n    volumes:\n      - ./backend/redis-data:/data\n    restart: always # ignored when deploying a stack in swarm mode or kubernetes\n    deploy: # ignored by docker-compose\n      restart_policy:\n        condition: on-failure\n    command: redis-server --appendonly yes\n"
  },
  {
    "path": "frontend/public/browserconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square150x150logo src=\"/mstile-150x150.png\"/>\n            <TileColor>#000000</TileColor>\n        </tile>\n    </msapplication>\n</browserconfig>\n"
  },
  {
    "path": "frontend/public/index.ejs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"x-ua-compatible\" content=\"ie=edge\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n  <meta name=\"description\" content=\"Full-stack boilerplate that using express with webpack, react and typescript!\">\n  <meta name=\"theme-color\" content=\"#2196f3\">\n  <title><%= htmlWebpackPlugin.options.title %></title>\n  <meta name=\"apple-mobile-web-app-title\" content=\"Boilerplate\">\n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"default\">\n  <link rel=\"apple-touch-icon\" sizes=\"120x120\" href=\"/apple-touch-icon-120x120.png\">\n  <link rel=\"apple-touch-icon\" sizes=\"152x152\" href=\"/apple-touch-icon-152x152.png\">\n  <link rel=\"apple-touch-icon\" sizes=\"167x167\" href=\"/apple-touch-icon-167x167.png\">\n  <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon-180x180.png\">\n  <link rel=\"apple-touch-startup-image\" href=\"/apple-touch-startup-icon-1024x1024.png\">\n  <link rel=\"mask-icon\" href=\"/safari-pinned-tab.svg\" color=\"#2196f3\">\n  <link rel=\"manifest\" href=\"/manifest.webmanifest\">\n  <link rel=\"shortcut icon\" href=\"/favicon.ico\">\n</head>\n<body>\n  <noscript>\n    For full functionality of this site it is necessary to enable JavaScript.\n    Here are the <a href=\"https://www.enable-javascript.com/\" target=\"_blank\"> instructions how to enable JavaScript in your web browser</a>.\n  </noscript>\n  <div id=\"root\"></div>\n  <!--\n    This HTML file is a template.\n    If you open it directly in the browser, you will see an empty page.\n\n    You can add webfonts, meta tags, or analytics to this file.\n    The build step will place the bundled scripts into the <body> tag.\n\n    To begin the development, run `yarn dev`.\n    To run in production mode, use `yarn prod`.\n  -->\n  <script>\n    // Async load Google Material Icons, eliminate render-blocking resources.\n    // See https://github.com/typekit/webfontloader#get-started for details.\n    WebFontConfig = {\n      google: {\n        families: ['Material Icons']\n      }\n    };\n    (function (d) {\n      var wf = d.createElement('script'), s = d.scripts[0];\n      wf.src = 'https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.28/webfontloader.js';\n      wf.async = true;\n      s.parentNode.insertBefore(wf, s);\n    })(document);\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "frontend/public/locales/en/common.json",
    "content": "{\n  \"header\": {\n    \"react\": \"React\",\n    \"dropdown\": \"Dropdown\",\n    \"notfound\": \"404\"\n  },\n  \"footer\": {\n    \"title\": \"Footer Content\",\n    \"content\": \"You can use rows and columns here to organize your footer content.\",\n    \"links\": \"Links\",\n    \"linkOne\": \"Link 1\",\n    \"linkTwo\": \"Link 2\",\n    \"linkThree\": \"Link 3\",\n    \"linkFour\": \"Link 4\",\n    \"moreLinks\": \"More Links\"\n  },\n  \"dropdown\": {\n    \"parallax\": \"Parallax\"\n  }\n}\n"
  },
  {
    "path": "frontend/public/locales/en/homePage.json",
    "content": "{\n  \"title\": \"Home Page\",\n  \"carousel\": {\n    \"focusButtonText\": \"Focus me!\",\n    \"firstPanelTitle\": \"First Panel\",\n    \"secondPanelTitle\": \"Second Panel\",\n    \"thirdPanelTitle\": \"Third Panel\",\n    \"fourthPanelTitle\": \"Fourth Panel\",\n    \"firstPanelDescription\": \"This is your first panel, try to swipe it!\",\n    \"secondPanelDescription\": \"This is your second panel, try to swipe it!\",\n    \"thirdPanelDescription\": \"This is your third panel, try to swipe it!\",\n    \"fourthPanelDescription\": \"This is your fourth panel, try to swipe it!\",\n    \"tooltipText\": \"Click me! >. <\",\n    \"toastText\": \"I am a toast!\"\n  },\n  \"pushpin\": {\n    \"green\": \"Green\",\n    \"blue\": \"Blue\",\n    \"orange\": \"Orange\",\n    \"red\": \"Red\",\n    \"purple\": \"Purple\",\n    \"cyan\": \"Cyan\",\n    \"linkOne\": \"Link 1\",\n    \"linkTwo\": \"Link 2\"\n  }\n}\n"
  },
  {
    "path": "frontend/public/locales/en/notFoundPage.json",
    "content": "{\n  \"title\": \"404 Page Not Found\"\n}\n"
  },
  {
    "path": "frontend/public/locales/en/parallaxPage.json",
    "content": "{\n  \"title\": \"Parallax Page\",\n  \"description\": \"Parallax is an effect where the background content or image in this case, is moved at a different speed than the foreground content while scrolling.\"\n}\n"
  },
  {
    "path": "frontend/public/locales/en/reactPage.json",
    "content": "{\n  \"title\": \"React Page\",\n  \"fetchNote\": {\n    \"asyncCalls\": \"async calls\",\n    \"fetchAllNotes\": \"Fetch All Notes From DB\"\n  },\n  \"todoLayout\": {\n    \"title\": \"todos\",\n    \"todoFooter\": {\n      \"all\": \"All\",\n      \"active\": \"Active\",\n      \"completed\": \"Completed\"\n    },\n    \"todoInput\": {\n      \"addTodo\": \"Add todo\"\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/public/locales/jp/common.json",
    "content": "{\n  \"header\": {\n    \"react\": \"React\",\n    \"dropdown\": \"ドロップダウンリスト\",\n    \"notfound\": \"404\"\n  },\n  \"footer\": {\n    \"title\": \"フッターコンテンツ\",\n    \"content\": \"ここでは行と列を使用してフッターのコンテンツを整理できます。\",\n    \"links\": \"リンク\",\n    \"linkOne\": \"リンク 1\",\n    \"linkTwo\": \"リンク 2\",\n    \"linkThree\": \"リンク 3\",\n    \"linkFour\": \"リンク 4\",\n    \"moreLinks\": \"その他のリンク\"\n  },\n  \"dropdown\": {\n    \"parallax\": \"視差\"\n  }\n}\n"
  },
  {
    "path": "frontend/public/locales/jp/homePage.json",
    "content": "{\n  \"title\": \"ホームページ\",\n  \"carousel\": {\n    \"focusButtonText\": \"フォーカス!\",\n    \"firstPanelTitle\": \"最初のパネル\",\n    \"secondPanelTitle\": \"第2のパネル\",\n    \"thirdPanelTitle\": \"第3のパネル\",\n    \"fourthPanelTitle\": \"第4のパネル\",\n    \"firstPanelDescription\": \"これはあなたの最初のパネルです、それをスワイプしよう！\",\n    \"secondPanelDescription\": \"これはあなたの第2のパネルです、それをスワイプしよう！\",\n    \"thirdPanelDescription\": \"これはあなたの第3のパネルです、それをスワイプしよう！\",\n    \"fourthPanelDescription\": \"これはあなたの第4のパネルです、それをスワイプしよう！\",\n    \"tooltipText\": \"クリック! >. <\",\n    \"toastText\": \"私はトーストです！\"\n  },\n  \"pushpin\": {\n    \"green\": \"緑\",\n    \"blue\": \"青\",\n    \"orange\": \"橙\",\n    \"red\": \"赤\",\n    \"purple\": \"紫\",\n    \"cyan\": \"シアン\",\n    \"linkOne\": \"リンク 1\",\n    \"linkTwo\": \"リンク 2\"\n  }\n}\n"
  },
  {
    "path": "frontend/public/locales/jp/notFoundPage.json",
    "content": "{\n  \"title\": \"404 ページが見つかりません\"\n}\n"
  },
  {
    "path": "frontend/public/locales/jp/parallaxPage.json",
    "content": "{\n  \"title\": \"視差ページ\",\n  \"description\": \"Parallax(パララックス)は、この場合のバックグラウンドコンテンツまたは画像がスクロール中にフォアグラウンドコンテンツとは異なる速度で移動される場合の効果です。\"\n}\n"
  },
  {
    "path": "frontend/public/locales/jp/reactPage.json",
    "content": "{\n  \"title\": \"Reactページ\",\n  \"fetchNote\": {\n    \"asyncCalls\": \"非同期関数\",\n    \"fetchAllNotes\": \"DB内のメモを取得する\"\n  },\n  \"todoLayout\": {\n    \"title\": \"todoリスト\",\n    \"todoFooter\": {\n      \"all\": \"全部\",\n      \"active\": \"アクティブ\",\n      \"completed\": \"完成した\"\n    },\n    \"todoInput\": {\n      \"addTodo\": \"追加する\"\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/public/locales/zh/common.json",
    "content": "{\n  \"header\": {\n    \"react\": \"React\",\n    \"dropdown\": \"下拉列表\",\n    \"notfound\": \"404\"\n  },\n  \"footer\": {\n    \"title\": \"页脚内容\",\n    \"content\": \"你可以在这里使用行或者列来编辑的页脚内容.\",\n    \"links\": \"链接\",\n    \"linkOne\": \"链接 1\",\n    \"linkTwo\": \"链接 2\",\n    \"linkThree\": \"链接 3\",\n    \"linkFour\": \"链接 4\",\n    \"moreLinks\": \"更多链接\"\n  },\n  \"dropdown\": {\n    \"parallax\": \"视差\"\n  }\n}\n"
  },
  {
    "path": "frontend/public/locales/zh/homePage.json",
    "content": "{\n  \"title\": \"首页\",\n  \"carousel\": {\n    \"focusButtonText\": \"看我看我!\",\n    \"firstPanelTitle\": \"第一个面板\",\n    \"secondPanelTitle\": \"第二个面板\",\n    \"thirdPanelTitle\": \"第三个面板\",\n    \"fourthPanelTitle\": \"第四个面板\",\n    \"firstPanelDescription\": \"这是你的第一个面板，试试左右滑动它！\",\n    \"secondPanelDescription\": \"这是你的第二个面板，试试左右滑动它！\",\n    \"thirdPanelDescription\": \"这是你的第三个面板，试试左右滑动它！\",\n    \"fourthPanelDescription\": \"这是你的第四个面板，试试左右滑动它！\",\n    \"tooltipText\": \"戳我! >. <\",\n    \"toastText\": \"哇我是一个toast!\"\n  },\n  \"pushpin\": {\n    \"green\": \"绿色\",\n    \"blue\": \"蓝色\",\n    \"orange\": \"橘色\",\n    \"red\": \"红色\",\n    \"purple\": \"紫色\",\n    \"cyan\": \"青色\",\n    \"linkOne\": \"链接 1\",\n    \"linkTwo\": \"链接 2\"\n  }\n}\n"
  },
  {
    "path": "frontend/public/locales/zh/notFoundPage.json",
    "content": "{\n  \"title\": \"404 页面不存在\"\n}\n"
  },
  {
    "path": "frontend/public/locales/zh/parallaxPage.json",
    "content": "{\n  \"title\": \"视差页面\",\n  \"description\": \"Parallax(视差) 是指一种背景内容或图像在滚动时以与前景内容不同的速度移动的效果。\"\n}\n"
  },
  {
    "path": "frontend/public/locales/zh/reactPage.json",
    "content": "{\n  \"title\": \"React页面\",\n  \"fetchNote\": {\n    \"asyncCalls\": \"异步函数\",\n    \"fetchAllNotes\": \"获取数据库所有笔记\"\n  },\n  \"todoLayout\": {\n    \"title\": \"待办事项\",\n    \"todoFooter\": {\n      \"all\": \"全部\",\n      \"active\": \"待办\",\n      \"completed\": \"已完成\"\n    },\n    \"todoInput\": {\n      \"addTodo\": \"添加待办事项\"\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/public/manifest.webmanifest",
    "content": "{\n  \"name\": \"Boilerplate\",\n  \"short_name\": \"Boilerplate\",\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"orientation\": \"portrait\",\n  \"background_color\": \"#2196f3\",\n  \"theme_color\": \"#2196f3\",\n  \"description\": \"Full-stack boilerplate that using express with webpack, react and typescript!\",\n  \"icons\": [\n    {\n      \"src\": \"icons/android-chrome-192x192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"icons/android-chrome-512x512.png\",\n      \"sizes\": \"512x512\",\n      \"type\": \"image/png\"\n    }\n  ]\n}\n"
  },
  {
    "path": "frontend/public/robots.txt",
    "content": "User-agent: *\nDisallow:\n"
  },
  {
    "path": "frontend/src/App.tsx",
    "content": "import { ConnectedRouter } from 'connected-react-router/immutable';\nimport { History } from 'history';\nimport React from 'react';\nimport { Provider } from 'react-redux';\nimport { Store } from 'redux';\n\nimport router from 'router';\nimport { IGlobalState } from 'types/global';\n\ninterface IAppProps {\n  store: Store<IGlobalState>;\n  history: History;\n}\n\nexport default (props: IAppProps) => (\n  <Provider store={props.store}>\n    <ConnectedRouter history={props.history}>\n      {router}\n    </ConnectedRouter>\n  </Provider>\n);\n"
  },
  {
    "path": "frontend/src/components/ContentLoader/__tests__/__snapshots__/contentLoader.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`BodyContentLoader should renders correctly 1`] = `\n<div\n  className=\"bodyContentLoader\"\n>\n  <ContentLoader\n    height={200}\n    primaryColor=\"#f3f3f3\"\n    secondaryColor=\"#d7d6d8\"\n    speed={1}\n    width={400}\n  >\n    <rect\n      height=\"15.1\"\n      rx=\"4\"\n      ry=\"4\"\n      width=\"76.63\"\n      x=\"160.5\"\n      y=\"11\"\n    />\n    <rect\n      height=\"151.36\"\n      rx=\"1\"\n      ry=\"1\"\n      width=\"275.4\"\n      x=\"60.5\"\n      y=\"35.05\"\n    />\n  </ContentLoader>\n</div>\n`;\n\nexports[`FooterContentLoader should renders correctly 1`] = `\n<ContentLoader\n  height={400}\n  primaryColor=\"#f3f3f3\"\n  secondaryColor=\"#d7d6d8\"\n  speed={1}\n  width={2500}\n>\n  <rect\n    height=\"400\"\n    width=\"2500\"\n    x=\"0\"\n    y=\"0\"\n  />\n</ContentLoader>\n`;\n\nexports[`HeaderContentLoader should renders correctly 1`] = `\n<ContentLoader\n  height={95}\n  primaryColor=\"#f3f3f3\"\n  secondaryColor=\"#d7d6d8\"\n  speed={1}\n  width={2500}\n>\n  <rect\n    height=\"95\"\n    width=\"2500\"\n    x=\"0\"\n    y=\"0\"\n  />\n</ContentLoader>\n`;\n"
  },
  {
    "path": "frontend/src/components/ContentLoader/__tests__/contentLoader.spec.tsx",
    "content": "import React from 'react';\nimport ShallowRenderer from 'react-test-renderer/shallow';\n\nimport { BodyContentLoader, FooterContentLoader, HeaderContentLoader } from '../contentLoader';\n\ndescribe('HeaderContentLoader', () => {\n  it('should renders correctly', () => {\n    const renderer = ShallowRenderer.createRenderer();\n    const result = renderer.render(\n      <HeaderContentLoader />,\n    );\n    expect(result).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n\ndescribe('BodyContentLoader', () => {\n  it('should renders correctly', () => {\n    const renderer = ShallowRenderer.createRenderer();\n    const result = renderer.render(\n      <BodyContentLoader />,\n    );\n    expect(result).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n\ndescribe('FooterContentLoader', () => {\n  it('should renders correctly', () => {\n    const renderer = ShallowRenderer.createRenderer();\n    const result = renderer.render(\n      <FooterContentLoader />,\n    );\n    expect(result).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/components/ContentLoader/contentLoader.scss",
    "content": ".bodyContentLoader {\n  margin: auto;\n}\n"
  },
  {
    "path": "frontend/src/components/ContentLoader/contentLoader.tsx",
    "content": "import React from 'react';\nimport ContentLoader from 'react-content-loader';\n\nconst styles = require('./contentLoader.scss');\n\nexport const BodyContentLoader = () => (\n  <div className={styles.bodyContentLoader}>\n    <ContentLoader\n      height={200}\n      width={400}\n      speed={1}\n      primaryColor='#f3f3f3'\n      secondaryColor='#d7d6d8'\n    >\n      <rect x='160.5' y='11' rx='4' ry='4' width='76.63' height='15.1' />\n      <rect x='60.5' y='35.05' rx='1' ry='1' width='275.4' height='151.36' />\n    </ContentLoader>\n  </div>\n);\n\nexport const HeaderContentLoader = () => (\n  <ContentLoader\n    height={95}\n    width={2500}\n    speed={1}\n    primaryColor='#f3f3f3'\n    secondaryColor='#d7d6d8'\n  >\n    <rect x='0' y='0' width='2500' height='95' />\n  </ContentLoader>\n);\n\nexport const FooterContentLoader = () => (\n  <ContentLoader\n    height={400}\n    width={2500}\n    speed={1}\n    primaryColor='#f3f3f3'\n    secondaryColor='#d7d6d8'\n  >\n    <rect x='0' y='0' width='2500' height='400' />\n  </ContentLoader>\n);\n"
  },
  {
    "path": "frontend/src/components/ContentLoader/index.tsx",
    "content": "export { BodyContentLoader, HeaderContentLoader, FooterContentLoader } from './contentLoader';\n"
  },
  {
    "path": "frontend/src/components/Dropdown/__tests__/__snapshots__/dropdown.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Dropdown should renders correctly 1`] = `\nArray [\n  <ul\n    className=\"dropdown-content\"\n    id=\"id\"\n  >\n    <li>\n      <a\n        href=\"/dropdown\"\n        onClick={[Function]}\n      >\n        dropdown.dropdown\n      </a>\n    </li>\n  </ul>,\n  \",\",\n]\n`;\n"
  },
  {
    "path": "frontend/src/components/Dropdown/__tests__/dropdown.spec.tsx",
    "content": "import 'materialize-css';\nimport React, { Fragment } from 'react';\nimport { BrowserRouter } from 'react-router-dom';\nimport TestRenderer from 'react-test-renderer';\n\nimport { Dropdown } from '../dropdown';\n\ndescribe('Dropdown', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <BrowserRouter>\n        <Fragment>\n          <Dropdown id='id' dropdownLists={['dropdown']} />,\n        </Fragment>\n      </BrowserRouter>,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/components/Dropdown/dropdown.tsx",
    "content": "import React from 'react';\nimport { Translation } from 'react-i18next';\nimport { Link } from 'react-router-dom';\n\ninterface IDropdownProps {\n  id: string;\n  dropdownLists: string[];\n}\n\nconst dropdownConfig: Partial<M.DropdownOptions> = {\n  coverTrigger: false,\n};\n\nexport class Dropdown extends React.Component<IDropdownProps> {\n  public componentDidMount() {\n    const elems = document.querySelectorAll('.dropdown-button');\n    M.Dropdown.init(elems, dropdownConfig);\n  }\n\n  public render() {\n    return (\n      <Translation ns='common'>\n        {(t) => (\n          <ul id={this.props.id} className='dropdown-content'>\n            {this.props.dropdownLists.map((key) =>\n              <li key={key}><Link to={`/${key}`}>{t('dropdown.' + key)}</Link></li>,\n            )}\n          </ul>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default Dropdown;\n"
  },
  {
    "path": "frontend/src/components/Dropdown/index.tsx",
    "content": "export { default } from './dropdown';\n"
  },
  {
    "path": "frontend/src/components/Footer/__tests__/__snapshots__/footer.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Footer should renders correctly 1`] = `\n<footer\n  className=\"page-footer\"\n>\n  <div\n    className=\"container\"\n  >\n    <div\n      className=\"row\"\n    >\n      <div\n        className=\"col l6 s12\"\n      >\n        <h5\n          className=\"white-text\"\n        >\n          footer.title\n        </h5>\n        <p\n          className=\"grey-text text-lighten-4\"\n        >\n          footer.content\n        </p>\n      </div>\n      <div\n        className=\"col l3 offset-l3 s12\"\n      >\n        <h5\n          className=\"white-text\"\n        >\n          footer.links\n        </h5>\n        <ul>\n          <li>\n            <a\n              className=\"grey-text text-lighten-3\"\n              href=\"#\"\n            >\n              footer.linkOne\n            </a>\n          </li>\n          <li>\n            <a\n              className=\"grey-text text-lighten-3\"\n              href=\"#\"\n            >\n              footer.linkTwo\n            </a>\n          </li>\n          <li>\n            <a\n              className=\"grey-text text-lighten-3\"\n              href=\"#\"\n            >\n              footer.linkThree\n            </a>\n          </li>\n          <li>\n            <a\n              className=\"grey-text text-lighten-3\"\n              href=\"#\"\n            >\n              footer.linkFour\n            </a>\n          </li>\n        </ul>\n      </div>\n    </div>\n  </div>\n  <div\n    className=\"footer-copyright\"\n  >\n    <div\n      className=\"container\"\n    >\n      © 2017 Copyright \n      <a\n        className=\"grey-text text-lighten-4\"\n        href=\"https://github.com/Armour\"\n      >\n        Armour\n      </a>\n      <a\n        className=\"grey-text text-lighten-4 right\"\n        href=\"#\"\n      >\n        footer.moreLinks\n      </a>\n    </div>\n  </div>\n</footer>\n`;\n"
  },
  {
    "path": "frontend/src/components/Footer/__tests__/footer.spec.tsx",
    "content": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { Footer } from '../footer';\n\ndescribe('Footer', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <Footer />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/components/Footer/footer.tsx",
    "content": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nexport class Footer extends React.Component {\n  public render() {\n    return (\n      <Translation ns='common'>\n        {(t) => (\n          <footer className='page-footer'>\n            <div className='container'>\n              <div className='row'>\n                <div className='col l6 s12'>\n                  <h5 className='white-text'>{t('footer.title')}</h5>\n                  <p className='grey-text text-lighten-4'>{t('footer.content')}</p>\n                </div>\n                <div className='col l3 offset-l3 s12'>\n                  <h5 className='white-text'>{t('footer.links')}</h5>\n                  <ul>\n                    <li><a className='grey-text text-lighten-3' href='#'>{t('footer.linkOne')}</a></li>\n                    <li><a className='grey-text text-lighten-3' href='#'>{t('footer.linkTwo')}</a></li>\n                    <li><a className='grey-text text-lighten-3' href='#'>{t('footer.linkThree')}</a></li>\n                    <li><a className='grey-text text-lighten-3' href='#'>{t('footer.linkFour')}</a></li>\n                  </ul>\n                </div>\n              </div>\n            </div>\n            <div className='footer-copyright'>\n              <div className='container'>\n                © 2017 Copyright <a className='grey-text text-lighten-4' href='https://github.com/Armour'>Armour</a>\n                <a className='grey-text text-lighten-4 right' href='#'>{t('footer.moreLinks')}</a>\n              </div>\n            </div>\n          </footer>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default Footer;\n"
  },
  {
    "path": "frontend/src/components/Footer/index.tsx",
    "content": "export { default } from './footer';\n"
  },
  {
    "path": "frontend/src/components/Header/__tests__/__snapshots__/header.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Header should renders correctly 1`] = `\nArray [\n  <div>\n    <nav>\n      <div\n        className=\"nav-wrapper\"\n      >\n        <div\n          className=\"container\"\n        >\n          <a\n            className=\"brand-logo\"\n            href=\"/\"\n            onClick={[Function]}\n          >\n            <img\n              alt=\"logo\"\n              className=\"logo\"\n              src={\n                Object {\n                  \"default\": \"test-file-stub\",\n                }\n              }\n            />\n          </a>\n          <a\n            className=\"sidenav-trigger\"\n            data-target=\"nav-mobile\"\n            href=\"#\"\n          >\n            <i\n              className=\"material-icons\"\n            >\n              menu\n            </i>\n          </a>\n          <ul\n            className=\"right hide-on-med-and-down\"\n            id=\"nav-desktop\"\n          >\n            <li\n              className=\"\"\n            >\n              <a\n                href=\"/react\"\n                onClick={[Function]}\n              >\n                header.react\n              </a>\n            </li>\n            <li\n              className=\"active\"\n            >\n              <a\n                className=\"dropdown-button\"\n                data-target=\"header-dropdown-desktop\"\n                href=\"#\"\n              >\n                header.dropdown\n                <i\n                  className=\"material-icons right\"\n                >\n                  arrow_drop_down\n                </i>\n              </a>\n            </li>\n            <li\n              className=\"\"\n            >\n              <a\n                href=\"/404\"\n                onClick={[Function]}\n              >\n                header.notfound\n              </a>\n            </li>\n            <Dropdown\n              dropdownLists={\n                Array [\n                  \"parallax\",\n                ]\n              }\n              id=\"header-dropdown-desktop\"\n            />\n          </ul>\n        </div>\n      </div>\n    </nav>\n    <ul\n      className=\"sidenav\"\n      id=\"nav-mobile\"\n    >\n      <li\n        className=\"\"\n      >\n        <a\n          href=\"/react\"\n          onClick={[Function]}\n        >\n          header.react\n        </a>\n      </li>\n      <li\n        className=\"active\"\n      >\n        <a\n          className=\"dropdown-button\"\n          data-target=\"header-dropdown-mobile\"\n          href=\"#\"\n        >\n          header.dropdown\n          <i\n            className=\"material-icons right\"\n          >\n            arrow_drop_down\n          </i>\n        </a>\n      </li>\n      <li\n        className=\"\"\n      >\n        <a\n          href=\"/404\"\n          onClick={[Function]}\n        >\n          header.notfound\n        </a>\n      </li>\n      <Dropdown\n        dropdownLists={\n          Array [\n            \"parallax\",\n          ]\n        }\n        id=\"header-dropdown-mobile\"\n      />\n    </ul>\n  </div>,\n  \",\",\n]\n`;\n"
  },
  {
    "path": "frontend/src/components/Header/__tests__/header.spec.tsx",
    "content": "import 'materialize-css';\nimport React, { Fragment } from 'react';\nimport { BrowserRouter } from 'react-router-dom';\nimport TestRenderer from 'react-test-renderer';\n\nimport { Header } from '../header';\n\njest.mock('components/Dropdown', () => 'Dropdown');\n\ndescribe('Header', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <BrowserRouter>\n        <Fragment>\n          <Header pathname='/parallax' />,\n        </Fragment>\n      </BrowserRouter>,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/components/Header/header.scss",
    "content": ".logo {\n  margin: 6px 0 0 0;\n  max-height: 50px;\n}\n"
  },
  {
    "path": "frontend/src/components/Header/header.tsx",
    "content": "import React from 'react';\nimport { Translation } from 'react-i18next';\nimport { connect } from 'react-redux';\nimport { Link } from 'react-router-dom';\n\nimport Dropdown from 'components/Dropdown';\nimport { IGlobalStateRecord } from 'types/global';\n\nconst styles = require('./header.scss');\n\n// Component\n\ninterface IHeaderStateProps {\n  pathname: string;\n}\n\nexport class Header extends React.Component<IHeaderStateProps> {\n  constructor(props: IHeaderStateProps) {\n    super(props);\n  }\n\n  public checkActive(urls: string[]) {\n    let active = false;\n    urls.forEach((url) => {\n      if (this.props.pathname === '/' + url) {\n        active = true;\n      }\n    });\n    return active ? 'active' : '';\n  }\n\n  public componentDidMount() {\n    const elems = document.querySelectorAll('.sidenav');\n    M.Sidenav.init(elems);\n  }\n\n  public render() {\n    const dropdownList = ['parallax'];\n    return (\n      <Translation ns='common'>\n        {(t) => (\n          <div>\n            <nav>\n              <div className='nav-wrapper'>\n                <div className='container'>\n                  <Link className='brand-logo' to='/'><img className={styles.logo} alt='logo' src={require('./assets/images/logo.png')}/></Link>\n                  <a href='#' data-target='nav-mobile' className='sidenav-trigger'>\n                    <i className='material-icons'>menu</i>\n                  </a>\n                  <ul id='nav-desktop' className='right hide-on-med-and-down'>\n                    <li className={this.checkActive(['react'])} key='react'><Link to='/react'>{t('header.react')}</Link></li>\n                    <li className={this.checkActive(dropdownList)} key='materialize'>\n                      <a className='dropdown-button' href='#' data-target='header-dropdown-desktop'>{t('header.dropdown')}<i className='material-icons right'>arrow_drop_down</i></a>\n                    </li>\n                    <li className={this.checkActive(['404'])} key='404'><Link to='/404'>{t('header.notfound')}</Link></li>\n                    <Dropdown id='header-dropdown-desktop' dropdownLists={dropdownList} />\n                  </ul>\n                </div>\n              </div>\n            </nav>\n            <ul id='nav-mobile' className='sidenav'>\n              <li className={this.checkActive(['react'])} key='react'><Link to='/react'>{t('header.react')}</Link></li>\n              <li className={this.checkActive(dropdownList)} key='materialize'>\n                <a className='dropdown-button' href='#' data-target='header-dropdown-mobile'>{t('header.dropdown')}<i className='material-icons right'>arrow_drop_down</i></a>\n              </li>\n              <li className={this.checkActive(['404'])} key='404'><Link to='/404'>{t('header.notfound')}</Link></li>\n              <Dropdown id='header-dropdown-mobile' dropdownLists={dropdownList} />\n            </ul>\n          </div>\n        )}\n      </Translation>\n    );\n  }\n}\n\n// Container\n\nconst mapStateToProps = (state: IGlobalStateRecord): IHeaderStateProps => ({\n  pathname: state.getIn(['router', 'location', 'pathname']),\n});\n\nexport default connect(\n  mapStateToProps,\n  null,\n)(Header);\n"
  },
  {
    "path": "frontend/src/components/Header/index.tsx",
    "content": "export { default } from './header';\n"
  },
  {
    "path": "frontend/src/i18n/index.tsx",
    "content": "import i18n from 'i18next';\nimport detector from 'i18next-browser-languagedetector';\nimport backend from 'i18next-xhr-backend';\nimport { initReactI18next } from 'react-i18next';\n\nimport { isProduction } from 'utils';\n\nexport default i18n\n  .use(detector)\n  .use(backend)\n  .use(initReactI18next)\n  .init({\n    debug: !isProduction,\n    backend: {\n      loadPath: './locales/{{lng}}/{{ns}}.json',\n    },\n    whitelist: ['en', 'zh', 'jp'],\n    fallbackLng: 'en',\n    fallbackNS: 'common',\n    interpolation: {\n      escapeValue: false, // not needed for react.\n    },\n    react: {\n      wait: false,\n    },\n  }, (err, _) => {\n    if (err) {\n      return console.error('Load i18n instance failed.', err);\n    }\n  });\n"
  },
  {
    "path": "frontend/src/index.tsx",
    "content": "import { createBrowserHistory } from 'history';\nimport OfflinePluginRuntime from 'offline-plugin/runtime';\nimport React from 'react';\nimport ReactDom from 'react-dom';\nimport { AppContainer } from 'react-hot-loader';\n\nimport 'i18n';\n\nimport App from 'App';\nimport { Map } from 'immutable';\nimport configureStore from 'store';\nimport { isProduction } from 'utils';\n\n// Webpack offline plugin\nif (isProduction) {\n  OfflinePluginRuntime.install();\n}\n\n// To keep reducers self-sufficient and reusable, we choose to not set\n// initial state here, and let each reducer to handle the default state\n// https://github.com/reactjs/redux/issues/1189#issuecomment-168025590\nconst initialState = Map();\n\n// Create browser history\nconst history = createBrowserHistory();\n\n// Configure store\nconst store = configureStore(history, initialState);\n\n// Create render function\nconst render = (Component: any) => {\n  ReactDom.render(\n    <AppContainer>\n      <Component store={store} history={history} />\n    </AppContainer>,\n    document.getElementById('root'),\n  );\n};\n\n// First time render\nrender(App);\n\n// Hot Reload Module API\nif (module.hot) {\n  module.hot.accept('./App', () => {\n    const NextApp = require('App').default;\n    render(NextApp);\n  });\n}\n"
  },
  {
    "path": "frontend/src/pages/HomePage/__tests__/__snapshots__/homePage.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`HomePage should renders correctly 1`] = `\nArray [\n  <div\n    className=\"home-page-block\"\n  >\n    <h1\n      className=\"page-title\"\n    >\n      title\n    </h1>\n    <div\n      className=\"container\"\n    >\n      <Carousel />\n    </div>\n  </div>,\n  <Pushpin\n    color=\"blue\"\n    depth=\"lighten-1\"\n  />,\n  <Pushpin\n    color=\"green\"\n    depth=\"lighten-1\"\n  />,\n  <Pushpin\n    color=\"orange\"\n    depth=\"lighten-1\"\n  />,\n  <Pushpin\n    color=\"red\"\n    depth=\"lighten-1\"\n  />,\n  <Pushpin\n    color=\"purple\"\n    depth=\"lighten-1\"\n  />,\n  <Pushpin\n    color=\"cyan\"\n    depth=\"lighten-1\"\n  />,\n  <TranslationButton />,\n]\n`;\n"
  },
  {
    "path": "frontend/src/pages/HomePage/__tests__/homePage.spec.tsx",
    "content": "import 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { HomePage } from '../homePage';\n\njest.mock('../components/Carousel', () => 'Carousel');\njest.mock('../components/Pushpin', () => 'Pushpin');\njest.mock('../components/TranslationButton', () => 'TranslationButton');\n\ndescribe('HomePage', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <HomePage />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/__tests__/__snapshots__/carousel.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Carousel should renders correctly 1`] = `\n<div\n  className=\"carousel carousel-slider center z-depth-3\"\n  data-indicators=\"true\"\n>\n  <div\n    className=\"carousel-fixed-item center\"\n  >\n    <a\n      className=\"btn tooltipped waves-effect white grey-text text-darken-2\"\n      onClick={[Function]}\n      role=\"button\"\n    >\n      carousel.focusButtonText\n    </a>\n  </div>\n  <a\n    className=\"carousel-item green white-text\"\n    href=\"#one!\"\n  >\n    <h2>\n      carousel.firstPanelTitle\n    </h2>\n    <p>\n      carousel.firstPanelDescription\n    </p>\n  </a>\n  <a\n    className=\"carousel-item amber white-text\"\n    href=\"#two!\"\n  >\n    <h2>\n      carousel.secondPanelTitle\n    </h2>\n    <p>\n      carousel.secondPanelDescription\n    </p>\n  </a>\n  <a\n    className=\"carousel-item red white-text\"\n    href=\"#three!\"\n  >\n    <h2>\n      carousel.thirdPanelTitle\n    </h2>\n    <p>\n      carousel.thirdPanelDescription\n    </p>\n  </a>\n  <a\n    className=\"carousel-item purple white-text\"\n    href=\"#four!\"\n  >\n    <h2>\n      carousel.fourthPanelTitle\n    </h2>\n    <p>\n      carousel.fourthPanelDescription\n    </p>\n  </a>\n</div>\n`;\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/__tests__/carousel.spec.tsx",
    "content": "import 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { Carousel } from '../carousel';\n\ndescribe('Carousel', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <Carousel />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/carousel.tsx",
    "content": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nimport { CAROUSEL_AUTOPLAY_INTERVAL, TOAST_DISPLAY_DURATION, TOOLTIP_DELAY_TIME } from './constants/carousel';\n\nconst tooltipConfig: Partial<M.TooltipOptions> = {\n  enterDelay: TOOLTIP_DELAY_TIME,\n  position: 'top',\n};\n\nconst carouselConfig: Partial<M.CarouselOptions> = {\n  fullWidth: true,\n  indicators: true,\n};\n\nconst toastConfig: Partial<M.ToastOptions> = {\n  inDuration: TOAST_DISPLAY_DURATION / 4,\n  outDuration: TOAST_DISPLAY_DURATION / 4,\n  displayLength: TOAST_DISPLAY_DURATION,\n};\n\nexport class Carousel extends React.Component {\n  public timer: number = 0;\n\n  public componentDidMount() {\n    const carouselElems = document.querySelectorAll('.carousel.carousel-slider');\n    const carousels = M.Carousel.init(carouselElems, carouselConfig);\n    this.timer = window.setTimeout(() => this.autoPlayCarousel(carousels), CAROUSEL_AUTOPLAY_INTERVAL);\n  }\n\n  public componentWillUnmount() {\n    clearTimeout(this.timer);\n  }\n\n  public autoPlayCarousel = (carousels: M.Carousel[]) => {\n    carousels.forEach((carousel) => {\n      if (!carousel.pressed) {\n        carousel.next();\n      }\n    });\n    this.timer = window.setTimeout(() => this.autoPlayCarousel(carousels), CAROUSEL_AUTOPLAY_INTERVAL);\n  }\n\n  public displayToast = (text: string) => (e: React.MouseEvent<HTMLElement>) => {\n    e.preventDefault();\n    toastConfig.html = text;\n    M.toast(toastConfig);\n  }\n\n  public initTooltip = (text: string) => {\n    tooltipConfig.html = text;\n    const tooltipElems = document.querySelectorAll('.tooltipped');\n    M.Tooltip.init(tooltipElems, tooltipConfig);\n  }\n\n  public render() {\n    return (\n      <Translation ns='homePage'>\n        {(t) => (\n          this.initTooltip(t('carousel.tooltipText')),\n          <div className='carousel carousel-slider center z-depth-3' data-indicators='true'>\n            <div className='carousel-fixed-item center'>\n              <a className='btn tooltipped waves-effect white grey-text text-darken-2' onClick={this.displayToast(t('carousel.toastText'))} role='button'>\n                {t('carousel.focusButtonText')}\n              </a>\n            </div>\n            <a className='carousel-item green white-text' href='#one!'>\n              <h2>{t('carousel.firstPanelTitle')}</h2>\n              <p>{t('carousel.firstPanelDescription')}</p>\n            </a>\n            <a className='carousel-item amber white-text' href='#two!'>\n              <h2>{t('carousel.secondPanelTitle')}</h2>\n              <p>{t('carousel.secondPanelDescription')}</p>\n            </a>\n            <a className='carousel-item red white-text' href='#three!'>\n              <h2>{t('carousel.thirdPanelTitle')}</h2>\n              <p>{t('carousel.thirdPanelDescription')}</p>\n            </a>\n            <a className='carousel-item purple white-text' href='#four!'>\n              <h2>{t('carousel.fourthPanelTitle')}</h2>\n              <p>{t('carousel.fourthPanelDescription')}</p>\n            </a>\n          </div>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default Carousel;\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/constants/carousel.tsx",
    "content": "// Toast timer\nexport const TOAST_DISPLAY_DURATION = 3000;\n\n// Tooltip timer\nexport const TOOLTIP_DELAY_TIME = 50;\n\n// Carousel autoplay interval\nexport const CAROUSEL_AUTOPLAY_INTERVAL = 3500;\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/index.tsx",
    "content": "export { default } from './carousel';\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/__tests__/__snapshots__/pushpin.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Pushpin should renders correctly 1`] = `\n<div\n  className=\"pushpin red base\"\n  id=\"red\"\n>\n  <nav\n    className=\"pushpin-nav pin-top\"\n    data-target=\"pushpin-red\"\n  >\n    <div\n      className=\"nav-wrapper red pushpin-red\"\n    >\n      <div\n        className=\"container\"\n      >\n        <a\n          className=\"brand-logo\"\n          href=\"#\"\n        >\n          pushpin.red\n        </a>\n        <ul\n          className=\"right hide-on-med-and-down\"\n          id=\"nav-demo-red\"\n        >\n          <li>\n            <a\n              href=\"#\"\n            >\n              pushpin.linkOne\n            </a>\n          </li>\n          <li>\n            <a\n              href=\"#\"\n            >\n              pushpin.linkTwo\n            </a>\n          </li>\n        </ul>\n      </div>\n    </div>\n  </nav>\n</div>\n`;\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/__tests__/pushpin.spec.tsx",
    "content": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { Pushpin } from '../pushpin';\n\ndescribe('Pushpin', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <Pushpin color='red' depth='base' />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/index.tsx",
    "content": "export { default } from './pushpin';\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/pushpin.scss",
    "content": ".pushpin {\n  height: 825px;\n}\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/pushpin.tsx",
    "content": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nconst styles = require('./pushpin.scss');\n\ninterface IPushpinProps {\n  color: string;\n  depth: string;\n}\n\nexport class Pushpin extends React.Component<IPushpinProps> {\n  public render() {\n    return (\n      <Translation ns='homePage'>\n        {(t) => (\n          <div id={this.props.color} className={`${styles.pushpin} ${this.props.color} ${this.props.depth}`}>\n            <nav className='pushpin-nav pin-top' data-target={'pushpin-' + this.props.color}>\n              <div className={`nav-wrapper ${this.props.color} pushpin-${this.props.color}`}>\n                <div className='container'>\n                  <a href='#' className='brand-logo'>{t('pushpin.' + this.props.color)}</a>\n                  <ul id={`nav-demo-${this.props.color}`} className='right hide-on-med-and-down'>\n                    <li><a href='#'>{t('pushpin.linkOne')}</a></li>\n                    <li><a href='#'>{t('pushpin.linkTwo')}</a></li>\n                  </ul>\n                </div>\n              </div>\n            </nav>\n          </div>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default Pushpin;\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/TranslationButton/__tests__/__snapshots__/translationButton.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TranslationButton should renders correctly 1`] = `\n<div\n  className=\"fixed-action-btn vertical\"\n>\n  <a\n    className=\"btn-floating btn-large red\"\n  >\n    <i\n      className=\"large material-icons\"\n    >\n      translate\n    </i>\n  </a>\n  <ul>\n    <li>\n      <a\n        className=\"btn-floating red\"\n        onClick={[Function]}\n      >\n        en\n      </a>\n    </li>\n    <li>\n      <a\n        className=\"btn-floating green\"\n        onClick={[Function]}\n      >\n        zh\n      </a>\n    </li>\n    <li>\n      <a\n        className=\"btn-floating yellow darken-1\"\n        onClick={[Function]}\n      >\n        jp\n      </a>\n    </li>\n  </ul>\n</div>\n`;\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/TranslationButton/__tests__/translationButton.spec.tsx",
    "content": "import 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TranslationButton } from '../translationButton';\n\ndescribe('TranslationButton', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <TranslationButton />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/TranslationButton/index.tsx",
    "content": "export { default } from './translationButton';\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/TranslationButton/translationButton.tsx",
    "content": "import i18next from 'i18next';\nimport React from 'react';\nimport { Translation } from 'react-i18next';\n\nconst floatingActionButtonConfig: Partial<M.FloatingActionButtonOptions> = {\n  direction: 'top',\n};\n\nexport class TranslationButton extends React.Component {\n  public componentDidMount() {\n    const elems = document.querySelectorAll('.fixed-action-btn');\n    M.FloatingActionButton.init(elems, floatingActionButtonConfig);\n  }\n\n  public changeLanguage = (i18n: i18next.i18n, lng: string) => (e: React.MouseEvent<HTMLElement>) => {\n    e.preventDefault();\n    i18n.changeLanguage(lng);\n  }\n\n  public render() {\n    return (\n      <Translation>\n        {(_, {i18n}) => (\n          <div className='fixed-action-btn vertical'>\n            <a className='btn-floating btn-large red'>\n              <i className='large material-icons'>translate</i>\n            </a>\n            <ul>\n              <li><a className='btn-floating red' onClick={this.changeLanguage(i18n, 'en')}>en</a></li>\n              <li><a className='btn-floating green' onClick={this.changeLanguage(i18n, 'zh')}>zh</a></li>\n              <li><a className='btn-floating yellow darken-1' onClick={this.changeLanguage(i18n, 'jp')}>jp</a></li>\n            </ul>\n          </div>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default TranslationButton;\n"
  },
  {
    "path": "frontend/src/pages/HomePage/homePage.scss",
    "content": ".home-page-block {\n  height: 820px;\n}\n"
  },
  {
    "path": "frontend/src/pages/HomePage/homePage.tsx",
    "content": "import React, { Fragment } from 'react';\nimport { Translation } from 'react-i18next';\n\nimport Carousel from './components/Carousel';\nimport Pushpin from './components/Pushpin';\nimport TranslationButton from './components/TranslationButton';\n\nconst styles = require('./homePage.scss');\n\nexport class HomePage extends React.Component {\n  public componentDidMount() {\n    document.querySelectorAll('.pushpin-nav').forEach((elem, _) => {\n      const target = document.querySelector('.' + elem.getAttribute('data-target')!);\n      const rect = target!.getBoundingClientRect();\n      let top = rect.top;\n      // Make sure element is not hidden (display: none) or disconnected\n      if (rect.width || rect.height || target!.getClientRects().length) {\n        top += window.pageYOffset - target!.ownerDocument!.documentElement!.clientTop;\n      }\n      const bottom = top + elem.parentElement!.getBoundingClientRect().height - rect.height;\n      M.Pushpin.init(elem, {\n        top,\n        bottom,\n      });\n    });\n  }\n\n  public render() {\n    return (\n      <Translation ns='homePage'>\n        {(t) => (\n          <Fragment>\n            <div className={styles['home-page-block']}>\n              <h1 className='page-title'>{t('title')}</h1>\n              <div className='container'>\n                <Carousel />\n              </div>\n            </div>\n            <Pushpin color='blue' depth='lighten-1' />\n            <Pushpin color='green' depth='lighten-1' />\n            <Pushpin color='orange' depth='lighten-1' />\n            <Pushpin color='red' depth='lighten-1' />\n            <Pushpin color='purple' depth='lighten-1' />\n            <Pushpin color='cyan' depth='lighten-1' />\n            <TranslationButton />\n          </Fragment>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default HomePage;\n"
  },
  {
    "path": "frontend/src/pages/HomePage/index.tsx",
    "content": "export { default } from './homePage';\n"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/__tests__/__snapshots__/notFoundPage.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`NotFoundPage should renders correctly 1`] = `\nArray [\n  <h1\n    className=\"page-title\"\n  >\n    title\n  </h1>,\n  <img\n    alt=\"not-found-img\"\n    className=\"not-found-img\"\n    height=\"550px\"\n    src={\n      Object {\n        \"default\": \"test-file-stub\",\n      }\n    }\n    width=\"750px\"\n  />,\n]\n`;\n"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/__tests__/notFoundPage.spec.tsx",
    "content": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { NotFoundPage } from '../notFoundPage';\n\ndescribe('NotFoundPage', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <NotFoundPage />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/index.tsx",
    "content": "export { default } from './notFoundPage';\n"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/notFoundPage.scss",
    "content": "@import 'sass/variables';\n\n.not-found-img {\n  display: block;\n  margin: auto;\n  padding: 60px;\n  max-width: 90%;\n  object-fit: cover;\n}\n\n@media only screen and (max-width: $small-screen) {\n  .not-found-img {\n    height: 360px;\n    max-width: 100%;\n  }\n}\n"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/notFoundPage.tsx",
    "content": "import React, { Fragment } from 'react';\nimport { Translation } from 'react-i18next';\n\nconst styles = require('./notFoundPage.scss');\n\nconst notFoundImageList = [\n  '404.gif',\n  '404-1.jpeg',\n  '404-2.jpeg',\n  '404.jpg',\n];\n\ninterface INotFoundPageState {\n  imageId: number;\n}\n\nexport class NotFoundPage extends React.Component<{}, INotFoundPageState> {\n  constructor(props: {}) {\n    super(props);\n    this.state = { imageId: this.getRandomInt(0, notFoundImageList.length) };\n  }\n\n  public getRandomInt = (min: number, max: number) => {\n    min = Math.ceil(min);\n    max = Math.floor(max);\n    return Math.floor(Math.random() * (max - min)) + min; // The maximum is exclusive and the minimum is inclusive\n  }\n\n  public render() {\n    return (\n      <Translation ns='notFoundPage'>\n        {(t) => (\n          <Fragment>\n            <h1 className='page-title'>{t('title')}</h1>\n            <img className={styles['not-found-img']} src={require('./assets/images/' + notFoundImageList[this.state.imageId])} alt='not-found-img' height='550px' width='750px' />\n          </Fragment>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default NotFoundPage;\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/__tests__/__snapshots__/parallaxPage.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`ParallaxPage should renders correctly 1`] = `\nArray [\n  <div\n    className=\"white\"\n  >\n    <h1\n      className=\"page-title\"\n    >\n      title\n    </h1>\n  </div>,\n  <div\n    className=\"parallax-container\"\n  >\n    <div\n      className=\"parallax\"\n    >\n      <img\n        alt=\"parallax-img\"\n        className=\"parallax-img\"\n        src={\n          Object {\n            \"default\": \"test-file-stub\",\n          }\n        }\n      />\n    </div>\n  </div>,\n  <div\n    className=\"section white\"\n  >\n    <div\n      className=\"row container\"\n    >\n      <h2\n        className=\"parallax-header\"\n      >\n        Parallax\n      </h2>\n      <p\n        className=\"grey-text text-darken-3\"\n      >\n        description\n      </p>\n    </div>\n    <div\n      className=\"row container\"\n    >\n      <h4\n        className=\"light\"\n      >\n        Parallax Demo Code\n      </h4>\n      <PrismCodes\n        language=\"language-tsx\"\n      />\n    </div>\n  </div>,\n  <div\n    className=\"parallax-container\"\n  >\n    <div\n      className=\"parallax\"\n    >\n      <img\n        alt=\"parallax-img\"\n        className=\"parallax-img\"\n        src={\n          Object {\n            \"default\": \"test-file-stub\",\n          }\n        }\n      />\n    </div>\n  </div>,\n]\n`;\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/__tests__/parallaxPage.spec.tsx",
    "content": "import 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { ParallaxPage } from '../parallaxPage';\n\njest.mock('../components/PrismCodes', () => 'PrismCodes');\n\ndescribe('ParallaxPage', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <ParallaxPage />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/__tests__/__snapshots__/prismCodes.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`PrismCodes component 1`] = `\n<pre\n  className=\"language-tsx\"\n>\n  <code\n    className=\"col s12 language-tsx\"\n  >\n    \n&lt;div&gt;\n  &lt;div className='white'&gt;\n    &lt;h1 className='page-title'&gt;{t('title')}&lt;/h1&gt;\n  &lt;/div&gt;\n  &lt;div className='parallax-container'&gt;\n    &lt;div className='parallax'&gt;\n      &lt;img className='parallax-img' src={require('./assets/images/parallax1.jpg')} alt='parallax-img' /&gt;\n    &lt;/div&gt;\n  &lt;/div&gt;\n  &lt;div className='section white'&gt;\n    &lt;div className='row container'&gt;\n      &lt;h2 className={styles['parallax-header']}&gt;Parallax&lt;/h2&gt;\n      &lt;p className='grey-text text-darken-3'&gt;{t('description')}&lt;/p&gt;\n    &lt;/div&gt;\n    &lt;div className='row container'&gt;\n      &lt;h4 className='light'&gt;Parallax Demo Code&lt;/h4&gt;\n      &lt;PrismCodes language='language-tsx'&gt;\n        {PARALLAX_CODE}\n      &lt;/PrismCodes&gt;\n    &lt;/div&gt;\n  &lt;/div&gt;\n  &lt;div className='parallax-container'&gt;\n    &lt;div className='parallax'&gt;\n      &lt;img className='parallax-img' src={require('./assets/images/parallax2.jpg')} alt='parallax-img' /&gt;\n    &lt;/div&gt;\n  &lt;/div&gt;\n&lt;/div&gt;\n\n  </code>\n</pre>\n`;\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/__tests__/prismCodes.spec.tsx",
    "content": "import React from 'react';\nimport renderer from 'react-test-renderer';\n\nimport { PARALLAX_CODE } from '../constants/prismCodes';\nimport PrismCodes from '../prismCodes';\n\njest.mock('prismjs', () => ({\n  highlightAll: () => { return; },\n}));\n\ntest('PrismCodes component', () => {\n  const component = renderer.create(\n    <PrismCodes language='language-tsx'>\n      {PARALLAX_CODE}\n    </PrismCodes>,\n  );\n  const tree = component.toJSON();\n  expect(tree).toMatchSnapshot();\n  component.unmount();\n});\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/constants/prismCodes.tsx",
    "content": "// Code snippets\nexport const PARALLAX_CODE = `\n<div>\n  <div className='white'>\n    <h1 className='page-title'>{t('title')}</h1>\n  </div>\n  <div className='parallax-container'>\n    <div className='parallax'>\n      <img className='parallax-img' src={require('./assets/images/parallax1.jpg')} alt='parallax-img' />\n    </div>\n  </div>\n  <div className='section white'>\n    <div className='row container'>\n      <h2 className={styles['parallax-header']}>Parallax</h2>\n      <p className='grey-text text-darken-3'>{t('description')}</p>\n    </div>\n    <div className='row container'>\n      <h4 className='light'>Parallax Demo Code</h4>\n      <PrismCodes language='language-tsx'>\n        {PARALLAX_CODE}\n      </PrismCodes>\n    </div>\n  </div>\n  <div className='parallax-container'>\n    <div className='parallax'>\n      <img className='parallax-img' src={require('./assets/images/parallax2.jpg')} alt='parallax-img' />\n    </div>\n  </div>\n</div>\n`;\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/index.tsx",
    "content": "export { default } from './prismCodes';\nexport * from './constants/prismCodes';\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/prismCodes.scss",
    "content": "code,\npre {\n  position: relative;\n  font-size: 1em;\n}\n\npre[class*=\"language-\"] {\n  padding: 20px 22px;\n  border: solid 1px rgba(51, 51, 51, 0.12);\n}\n\npre[class*=\"language-\"]::before {\n  position: absolute;\n  padding: 1px 5px;\n  background: #e8e6e3;\n  top: 0;\n  left: 0;\n  font-family: \"Roboto\", sans-serif;\n  -webkit-font-smoothing: antialiased;\n  color: #555;\n  content: attr(class);\n  font-size: 0.9em;\n  border: solid 1px rgba(51, 51, 51, 0.12);\n  border-top: none;\n  border-left: none;\n}\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/prismCodes.tsx",
    "content": "import React from 'react';\n\nimport { highlightAll } from 'prismjs';\nimport 'prismjs/themes/prism.css';\nimport './prismCodes.scss';\n\ninterface IPrismCodesProps {\n  language: string;\n}\n\nexport default class PrismCodes extends React.Component<IPrismCodesProps> {\n  public componentDidMount() {\n    highlightAll();\n  }\n\n  public render() {\n    return (\n      <pre className={this.props.language}>\n        <code className={`col s12 ${this.props.language}`}>\n          {this.props.children}\n        </code>\n      </pre>\n    );\n  }\n}\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/index.tsx",
    "content": "export { default } from './parallaxPage';\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/parallaxPage.scss",
    "content": "@import 'sass/variables';\n\n.parallax-header {\n  color: $primary-color;\n  font-weight: 300;\n}\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/parallaxPage.tsx",
    "content": "import React, { Fragment } from 'react';\nimport { Translation } from 'react-i18next';\n\nimport PrismCodes, { PARALLAX_CODE } from './components/PrismCodes';\n\nconst styles = require('./parallaxPage.scss');\n\nexport class ParallaxPage extends React.Component {\n  public componentDidMount() {\n    const elems = document.querySelectorAll('.parallax');\n    M.Parallax.init(elems);\n  }\n\n  public render() {\n    return (\n      <Translation ns='parallaxPage'>\n        {(t) => (\n          <Fragment>\n            <div className='white'>\n              <h1 className='page-title'>{t('title')}</h1>\n            </div>\n            <div className='parallax-container'>\n              <div className='parallax'>\n                <img className='parallax-img' src={require('./assets/images/parallax1.jpg')} alt='parallax-img' />\n              </div>\n            </div>\n            <div className='section white'>\n              <div className='row container'>\n                <h2 className={styles['parallax-header']}>Parallax</h2>\n                <p className='grey-text text-darken-3'>{t('description')}</p>\n              </div>\n              <div className='row container'>\n                <h4 className='light'>Parallax Demo Code</h4>\n                <PrismCodes language='language-tsx'>\n                  {PARALLAX_CODE}\n                </PrismCodes>\n              </div>\n            </div>\n            <div className='parallax-container'>\n              <div className='parallax'>\n                <img className='parallax-img' src={require('./assets/images/parallax2.jpg')} alt='parallax-img' />\n              </div>\n            </div>\n          </Fragment>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default ParallaxPage;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/__tests__/__snapshots__/reactPage.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`ReactPage should renders correctly 1`] = `\n<div>\n  <div\n    className=\"react-container\"\n  >\n    <h1\n      className=\"page-title\"\n    >\n      title\n    </h1>\n    <TodoLayout />\n    <FetchNote />\n  </div>\n</div>\n`;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/__tests__/reactPage.spec.tsx",
    "content": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { ReactPage } from '../reactPage';\n\njest.mock('../components/FetchNote', () => 'FetchNote');\njest.mock('../components/TodoLayout', () => 'TodoLayout');\n\ndescribe('ReactPage', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <ReactPage />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/__tests__/__snapshots__/fetchNote.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`FetchNote should renders correctly 1`] = `\n<div\n  className=\"center-align z-depth-2 fetch-note-layout\"\n>\n  <span\n    className=\"fetch-note-title\"\n  >\n    fetchNote.asyncCalls\n  </span>\n  <div\n    className=\"fetchAllNotes\"\n  >\n    <a\n      className=\"btn waves-effect fetch-note-filter-btn\"\n      onClick={[Function]}\n      role=\"button\"\n    >\n      fetchNote.fetchAllNotes\n    </a>\n  </div>\n</div>\n`;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/__tests__/fetchNote.spec.tsx",
    "content": "import { List } from 'immutable';\nimport 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { FetchNote } from '../fetchNote';\n\n// Mock dispatch\nconst dispatchMock = () => { return; };\n\ndescribe('FetchNote', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <FetchNote\n        notes={List<any>()}\n        loading={false}\n        error={''}\n        fetchAllNotes={dispatchMock}\n        fetchNote={dispatchMock}\n        addNote={dispatchMock}\n        editNote={dispatchMock}\n        removeNote={dispatchMock}\n      />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/fetchNote.scss",
    "content": "@import 'sass/variables';\n@import '../../reactPage';\n\n.fetch-note-layout {\n  @extend .react-block;\n}\n\n.fetch-note-title {\n  @extend .react-block-title;\n}\n\n.fetch-note-collection {\n  margin: 25px 0 0 0;\n}\n\n.fetch-note-error-panel {\n  margin: 20px 0 0 0;\n}\n\n.fetch-note-filter-btn {\n  width: 30%;\n}\n\n@media only screen and (max-width: $small-screen) {\n  .fetch-note-filter-btn {\n    width: 80%;\n    max-width: 300px;\n  }\n}\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/fetchNote.tsx",
    "content": "import { List } from 'immutable';\nimport React from 'react';\nimport { Translation } from 'react-i18next';\nimport { connect } from 'react-redux';\nimport { AnyAction, Dispatch } from 'redux';\n\nimport { ADD_NOTE_REQUESTED, EDIT_NOTE_REQUESTED, FETCH_ALL_NOTES_REQUESTED, FETCH_NOTE_REQUESTED, REMOVE_NOTE_REQUESTED } from 'services/notes/constants';\nimport { INote } from 'services/notes/types';\nimport { IGlobalStateRecord } from 'types/global';\n\nconst styles = require('./fetchNote.scss');\n\n// Component\n\ninterface IFetchNoteStateProps {\n  notes: List<INote>;\n  loading: boolean;\n  error: string;\n}\n\ninterface IFetchNoteDispatchProps {\n  fetchAllNotes(): void;\n  fetchNote(id: number): void;\n  addNote(content: string): void;\n  editNote(id: number, content: string): void;\n  removeNote(id: number): void;\n}\n\ninterface IFetchNoteProps extends IFetchNoteStateProps, IFetchNoteDispatchProps { }\n\nexport class FetchNote extends React.Component<IFetchNoteProps> {\n  public fetchAllNotes = (e: React.MouseEvent<HTMLElement>) => {\n    e.preventDefault();\n    this.props.fetchAllNotes();\n  }\n\n  public render() {\n    const noteList = this.props.notes.map((note) =>\n      (\n        <li className='collection-item'>\n          <div>Id: {note.id} &emsp; Content: {note.content}</div>\n        </li>\n      ),\n    );\n    const noteListCollection = <ul className={`collection ${styles['fetch-note-collection']}`}> {noteList} </ul>;\n    const errorPanel = (\n      <div className={`card-panel red ${styles['fetch-note-error-panel']}`}>\n        <span className='white-text'>{this.props.error}</span>\n      </div>\n    );\n    return (\n      <Translation ns='reactPage'>\n        {(t) => (\n          <div className={`center-align z-depth-2 ${styles['fetch-note-layout']}`}>\n            <span className={styles['fetch-note-title']}>{t('fetchNote.asyncCalls')}</span>\n            <div className='fetchAllNotes'>\n              <a className={`btn waves-effect ${styles['fetch-note-filter-btn']}`} onClick={this.fetchAllNotes} role='button'>\n                {t('fetchNote.fetchAllNotes')}\n              </a>\n            </div>\n            {noteList.count() > 0 && noteListCollection}\n            {this.props.error !== '' && errorPanel}\n          </div>\n        )}\n      </Translation>\n    );\n  }\n}\n\n// Container\n\nconst mapStateToProps = (state: IGlobalStateRecord): IFetchNoteStateProps => ({\n    notes: state.get('notesState').notes,\n    loading: state.get('notesState').loading,\n    error: state.get('notesState').error,\n});\n\nconst mapDispatchToProps = (dispatch: Dispatch<AnyAction>): IFetchNoteDispatchProps => ({\n  fetchAllNotes: () => {\n    dispatch({ type: FETCH_ALL_NOTES_REQUESTED, payload: {} });\n  },\n  fetchNote: (id: number) => {\n    dispatch({ type: FETCH_NOTE_REQUESTED, payload: { id } });\n  },\n  addNote: (content: string) => {\n    dispatch({ type: ADD_NOTE_REQUESTED, payload: { content } });\n  },\n  editNote: (id: number, content: string) => {\n    dispatch({ type: EDIT_NOTE_REQUESTED, payload: { id, content } });\n  },\n  removeNote: (id: number) => {\n    dispatch({ type: REMOVE_NOTE_REQUESTED, payload: { id } });\n  },\n});\n\nexport default connect(\n  mapStateToProps,\n  mapDispatchToProps,\n)(FetchNote);\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/index.tsx",
    "content": "export { default } from './fetchNote';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/__tests__/__snapshots__/todoLayout.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TodoLayout should renders correctly 1`] = `\n<div\n  className=\"center-align z-depth-2 todo-layout\"\n>\n  <span\n    className=\"todo-title\"\n  >\n    title\n  </span>\n  <TodoInput />\n  <TodoList />\n  <TodoFooter />\n</div>\n`;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/__tests__/todoLayout.spec.tsx",
    "content": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TodoLayout } from '../todoLayout';\n\njest.mock('../components/TodoFooter', () => 'TodoFooter');\njest.mock('../components/TodoInput', () => 'TodoInput');\njest.mock('../components/TodoList', () => 'TodoList');\n\ndescribe('TodoLayout', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <TodoLayout />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/__tests__/__snapshots__/todoFooter.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TodoFooter should renders correctly 1`] = `\n<div\n  className=\"todo-footer\"\n>\n  <TodoFilter\n    filter=\"SHOW_ALL\"\n  >\n    todoLayout.todoFooter.all\n  </TodoFilter>\n  <TodoFilter\n    filter=\"SHOW_ACTIVE\"\n  >\n    todoLayout.todoFooter.active\n  </TodoFilter>\n  <TodoFilter\n    filter=\"SHOW_COMPLETED\"\n  >\n    todoLayout.todoFooter.completed\n  </TodoFilter>\n</div>\n`;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/__tests__/todoFooter.spec.tsx",
    "content": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TodoFooter } from '../todoFooter';\n\njest.mock('../components/TodoFilter', () => 'TodoFilter');\n\ndescribe('TodoFooter', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <TodoFooter />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/__tests__/__snapshots__/todoFilter.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TodoFilter should renders correctly 1`] = `\n<a\n  className=\"btn waves-effect waves-light todo-filter-btn\"\n  href=\"#\"\n  onClick={[Function]}\n>\n  test\n</a>\n`;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/__tests__/todoFilter.spec.tsx",
    "content": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TodoFilter } from '../todoFilter';\n\n// Mock dispatch\nconst dispatchMock = () => { return; };\n\ndescribe('TodoFilter', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <TodoFilter active={true} setVisibilityFilter={dispatchMock}>\n        {'test'}\n      </TodoFilter>,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/index.tsx",
    "content": "export { default } from './todoFilter';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/todoFilter.scss",
    "content": "@import 'sass/variables';\n\n.todo-filter-btn {\n  width: 120px;\n  text-align: center;\n  margin: 3px;\n  padding: 0;\n}\n\n@media only screen and (max-width: $small-screen) {\n  .todo-filter-btn {\n    display: block;\n    width: 80%;\n    max-width: 300px;\n    margin: auto;\n  }\n}\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/todoFilter.tsx",
    "content": "import React from 'react';\nimport { connect } from 'react-redux';\nimport { AnyAction, Dispatch } from 'redux';\n\nimport { setVisibilityFilter } from 'services/todos/actions';\nimport { IGlobalStateRecord } from 'types/global';\n\nconst styles = require('./todoFilter.scss');\n\n// Component\n\ninterface ITodoFilterStateProps {\n  active: boolean;\n}\n\ninterface ITodoFilterDispatchProps {\n  setVisibilityFilter(): void;\n}\n\ninterface ITodoFilterProps extends ITodoFilterStateProps, ITodoFilterDispatchProps { }\n\nexport class TodoFilter extends React.Component<ITodoFilterProps> {\n  public onClick = (e: React.MouseEvent<HTMLElement>) => {\n    e.preventDefault();\n    this.props.setVisibilityFilter();\n  }\n\n  public render() {\n    if (this.props.active) {\n      return (\n        <a href='#' className={`btn waves-effect waves-light ${styles['todo-filter-btn']}`} onClick={this.onClick}>\n          {this.props.children}\n        </a>\n      );\n    } else {\n      return (\n        <a href='#' className={`btn-flat waves-effect waves-light ${styles['todo-filter-btn']}`} onClick={this.onClick}>\n          {this.props.children}\n        </a>\n      );\n    }\n  }\n}\n\n// Container\n\ninterface ITodoFilterOwnProps {\n  filter: string;\n}\n\nconst mapStateToProps = (state: IGlobalStateRecord, ownProps: ITodoFilterOwnProps): ITodoFilterStateProps => ({\n  active: ownProps.filter === state.get('todosState').visibilityFilter,\n});\n\nconst mapDispatchToProps = (dispatch: Dispatch<AnyAction>, ownProps: ITodoFilterOwnProps): ITodoFilterDispatchProps => ({\n  setVisibilityFilter: () => {\n    dispatch(setVisibilityFilter(ownProps.filter));\n  },\n});\n\nexport default connect(\n  mapStateToProps,\n  mapDispatchToProps,\n)(TodoFilter);\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/index.tsx",
    "content": "export { default } from './todoFooter';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/todoFooter.tsx",
    "content": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nimport TodoFilter from './components/TodoFilter';\n\nexport class TodoFooter extends React.Component {\n  public render() {\n    return (\n      <Translation ns='reactPage'>\n        {(t) => (\n          <div className='todo-footer'>\n            <TodoFilter filter='SHOW_ALL'>\n              {t('todoLayout.todoFooter.all')}\n            </TodoFilter>\n            <TodoFilter filter='SHOW_ACTIVE'>\n              {t('todoLayout.todoFooter.active')}\n            </TodoFilter>\n            <TodoFilter filter='SHOW_COMPLETED'>\n              {t('todoLayout.todoFooter.completed')}\n            </TodoFilter>\n          </div>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default TodoFooter;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/__tests__/__snapshots__/todoInput.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TodoInput should renders correctly 1`] = `\n<form\n  onSubmit={[Function]}\n>\n  <div\n    className=\"input-field\"\n  >\n    <input\n      id=\"input-addTodo\"\n      type=\"text\"\n    />\n    <label>\n      todoLayout.todoInput.addTodo\n    </label>\n  </div>\n</form>\n`;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/__tests__/todoInput.spec.tsx",
    "content": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TodoInput } from '../todoInput';\n\n// Mock dispatch\nconst dispatchMock = () => { return; };\n\ndescribe('TodoInput', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <TodoInput onSubmit={dispatchMock} />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/index.tsx",
    "content": "export { default } from './todoInput';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/todoInput.tsx",
    "content": "import React, { Fragment } from 'react';\nimport { Translation } from 'react-i18next';\nimport { connect } from 'react-redux';\nimport { AnyAction, Dispatch } from 'redux';\n\nimport { addTodo } from 'services/todos/actions';\n\n// Component\n\ninterface ITodoInputDispatchProps {\n  onSubmit(inputValue: string): void;\n}\n\nlet input: HTMLInputElement;\n\nexport class TodoInput extends React.Component<ITodoInputDispatchProps> {\n  public onSubmit = (e: React.FormEvent<HTMLFormElement>): void => {\n    e.preventDefault();\n    if (!input.value.trim()) {\n      return;\n    }\n    this.props.onSubmit(input.value);\n    input.value = '';\n  }\n\n  public setInput = (node: HTMLInputElement): void => {\n    input = node;\n  }\n\n  public render() {\n    return (\n      <Translation ns='reactPage'>\n        {(t) => (\n          <Fragment>\n            <form onSubmit={this.onSubmit}>\n              <div className='input-field'>\n                <input id='input-addTodo' type='text' ref={this.setInput} />\n                <label>{t('todoLayout.todoInput.addTodo')}</label>\n              </div>\n            </form>\n          </Fragment>\n        )}\n      </Translation>\n    );\n  }\n}\n\n// Container\n\nconst mapDispatchToProps = (dispatch: Dispatch<AnyAction>): ITodoInputDispatchProps => ({\n  onSubmit: (inputValue: string) => {\n    dispatch(addTodo(inputValue));\n  },\n});\n\nexport default connect(\n  null,\n  mapDispatchToProps,\n)(TodoInput);\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/__tests__/__snapshots__/todoList.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TodoList should renders correctly 1`] = `null`;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/__tests__/todoList.spec.tsx",
    "content": "import { List } from 'immutable';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TodoList } from '../todoList';\n\njest.mock('../components/Todo', () => 'Todo');\n\n// Mock dispatch\nconst dispatchMock = () => { return; };\n\ndescribe('TodoList', () => {\n  it('should renders correctly', () => {\n    const renderer = TestRenderer.create(\n      <TodoList todos={List<any>()} toggleTodo={dispatchMock} />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/__tests__/__snapshots__/todo.spec.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Todo should renders correctly 1`] = `\n<a\n  className=\"collection-item waves-effect waves-teal\"\n  href=\"#\"\n  onClick={[Function]}\n>\n  <div\n    className=\"truncate todo-incompleted\"\n  >\n    text\n  </div>\n</a>\n`;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/__tests__/todo.spec.tsx",
    "content": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport Todo from '../todo';\n\n// Mock dispatch\nconst dispatchMock = () => { return; };\n\ndescribe('Todo', () => {\n  it('should renders correctly', () => {\n    const todo = {\n      id: 'id',\n      completed: false,\n      text: 'text',\n    };\n    const renderer = TestRenderer.create(\n      <Todo {...todo} onClick={dispatchMock} />,\n    );\n    expect(renderer).toMatchSnapshot();\n    renderer.unmount();\n  });\n});\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/index.tsx",
    "content": "export { default } from './todo';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/todo.scss",
    "content": ".todo-completed {\n  text-decoration: line-through;\n  color: grey;\n}\n\n.todo-incompleted {\n  text-decoration: none;\n}\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/todo.tsx",
    "content": "import React from 'react';\n\nimport { ITodo } from 'services/todos/types';\n\nconst styles = require('./todo.scss');\n\ninterface ITodoProps extends ITodo {\n  onClick(e: React.MouseEvent<HTMLElement>): void;\n}\n\nexport default class Todo extends React.Component<ITodoProps> {\n  public onClick = (e: React.MouseEvent<HTMLElement>) => {\n    e.preventDefault();\n    this.props.onClick(e);\n  }\n\n  public render() {\n    if (this.props.completed) {\n      return (\n        <a href='#' className='collection-item waves-effect' onClick={this.onClick} >\n          <div className={`truncate ${styles['todo-completed']}`}>\n            {this.props.text}\n          </div>\n        </a>\n      );\n    } else {\n      return (\n        <a href='#' className='collection-item waves-effect waves-teal' onClick={this.onClick} >\n          <div className={`truncate ${styles['todo-incompleted']}`}>\n            {this.props.text}\n          </div>\n        </a>\n      );\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/index.tsx",
    "content": "export { default } from './todoList';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/todoList.tsx",
    "content": "import { List } from 'immutable';\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { AnyAction, Dispatch } from 'redux';\n\nimport { toggleTodo } from 'services/todos/actions';\nimport { VISIBILITY_FILTER_OPTIONS } from 'services/todos/constants';\nimport { ITodo, ITodosStateRecord } from 'services/todos/types';\nimport { IGlobalStateRecord } from 'types/global';\nimport Todo from './components/Todo';\n\n// Component\n\ninterface ITodoListStateProps {\n  todos: List<ITodo>;\n}\n\ninterface ITodoListDispatchProps {\n  toggleTodo(id: string): void;\n}\n\ninterface ITodoListProps extends ITodoListStateProps, ITodoListDispatchProps { }\n\nexport class TodoList extends React.Component<ITodoListProps> {\n  public onClick = (id: string) => (e: React.MouseEvent<HTMLElement>) => {\n    e.preventDefault();\n    this.props.toggleTodo(id);\n  }\n\n  public render() {\n    const todoList = this.props.todos.map((todo) =>\n      <Todo key={todo.id} {...todo} onClick={this.onClick(todo.id)} />,\n    );\n    if (todoList.count() > 0) {\n      return (\n        <ul className='collection'>\n          {todoList}\n        </ul>\n      );\n    } else {\n      return (null);\n    }\n  }\n}\n\n// Container\n\nconst getVisibleTodos = (todosState: ITodosStateRecord): List<ITodo> => {\n  switch (todosState.visibilityFilter) {\n    case VISIBILITY_FILTER_OPTIONS.SHOW_ALL:\n      return todosState.todos;\n    case VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED:\n      return todosState.todos.filter((t) => t !== undefined && t.completed);\n    case VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE:\n      return todosState.todos.filter((t) => t !== undefined && !t.completed);\n    default:\n      return todosState.todos;\n  }\n};\n\nconst mapStateToProps = (state: IGlobalStateRecord): ITodoListStateProps => ({\n  todos: getVisibleTodos(state.get('todosState')),\n});\n\nconst mapDispatchToProps = (dispatch: Dispatch<AnyAction>): ITodoListDispatchProps => ({\n  toggleTodo: (id: string) => {\n    dispatch(toggleTodo(id));\n  },\n});\n\nexport default connect(\n  mapStateToProps,\n  mapDispatchToProps,\n)(TodoList);\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/index.tsx",
    "content": "export { default } from './todoLayout';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/todoLayout.scss",
    "content": "@import 'sass/variables';\n@import '../../reactPage';\n\n.todo-layout {\n  @extend .react-block;\n}\n\n.todo-title {\n  @extend .react-block-title;\n}\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/todoLayout.tsx",
    "content": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nimport TodoFooter from './components/TodoFooter';\nimport TodoInput from './components/TodoInput';\nimport TodoList from './components/TodoList';\n\nconst styles = require('./todoLayout.scss');\n\nexport class TodoLayout extends React.Component {\n  public render() {\n    return (\n      <Translation ns='reactPage'>\n        {(t) => (\n          <div className={`center-align z-depth-2 ${styles['todo-layout']}`}>\n            <span className={styles['todo-title']}>{t('title')}</span>\n            <TodoInput />\n            <TodoList />\n            <TodoFooter />\n          </div>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default TodoLayout;\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/index.tsx",
    "content": "export { default } from './reactPage';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/reactPage.scss",
    "content": "@import 'sass/variables';\n\n.react-block {\n  border: 1px lightgray solid;\n  margin: 50px auto;\n  width: 850px;\n  padding: 20px 30px 40px;\n}\n\n@media only screen and (max-width: $medium-screen) {\n  .react-block {\n    max-width: 80%;\n  }\n}\n\n@media only screen and (max-width: $small-screen) {\n  .react-block {\n    max-width: 85%;\n  }\n}\n\n.react-block-title {\n  display: block;\n  font-weight: 100;\n  font-size: 4.5em;\n  color: $secondary-color;\n  margin-bottom: 20px;\n}\n\n@media only screen and (max-width: $medium-screen) {\n  .react-block-title {\n    font-weight: 100;\n    font-size: 4em;\n  }\n}\n\n@media only screen and (max-width: $small-screen) {\n  .react-block-title {\n    font-weight: 100;\n    font-size: 3em;\n  }\n}\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/reactPage.tsx",
    "content": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nimport FetchNote from './components/FetchNote';\nimport TodoLayout from './components/TodoLayout';\n\nexport class ReactPage extends React.Component {\n  public render() {\n    return (\n      <Translation ns='reactPage'>\n        {(t) => (\n          <div>\n            <div className='react-container'>\n              <h1 className='page-title'>{t('title')}</h1>\n              <TodoLayout />\n              <FetchNote />\n            </div>\n          </div>\n        )}\n      </Translation>\n    );\n  }\n}\n\nexport default ReactPage;\n"
  },
  {
    "path": "frontend/src/reducers/index.tsx",
    "content": "import { connectRouter } from 'connected-react-router/immutable';\nimport { History } from 'history';\nimport { combineReducers } from 'redux-immutable';\n\nimport notesReducer from 'services/notes/reducer';\nimport todosReducer from 'services/todos/reducer';\nimport { IGlobalState } from 'types/global';\n\nexport default (history: History) => combineReducers<IGlobalState>({\n  router: connectRouter(history),\n  notesState: notesReducer,\n  todosState: todosReducer,\n});\n"
  },
  {
    "path": "frontend/src/router.tsx",
    "content": "import React, { Fragment, lazy, Suspense } from 'react';\nimport { Route, Switch } from 'react-router';\n\nimport 'materialize-css';\nimport 'sass/global';\n\nimport { BodyContentLoader, FooterContentLoader, HeaderContentLoader } from 'components/ContentLoader';\nimport Footer from 'components/Footer';\nimport Header from 'components/Header';\n\nconst HomePage = lazy(() => import(\n  /*\n    webpackChunkName: \"home-page\",\n    webpackPreload: true\n  */\n  'pages/HomePage'));\n\nconst NotFoundPage = lazy(() => import(\n  /*\n    webpackChunkName: \"not-found-page\",\n    webpackPrefetch: true\n  */\n 'pages/NotFoundPage'));\n\nconst ParallaxPage = lazy(() => import(\n  /*\n    webpackChunkName: \"parallax-page\",\n    webpackPrefetch: true\n  */\n  'pages/ParallaxPage'));\n\nconst ReactPage = lazy(() => import(\n  /*\n    webpackChunkName: \"react-page\",\n    webpackPrefetch: true\n  */\n  'pages/ReactPage'));\n\nexport default (\n  <Fragment>\n    <Suspense fallback={<HeaderContentLoader />}>\n      <Header />\n    </Suspense>\n    <Suspense fallback={<BodyContentLoader />}>\n      <Switch>\n        <Route exact path='/' component={HomePage} />\n        <Route path='/react' component={ReactPage} />\n        <Route path='/parallax' component={ParallaxPage} />\n        <Route component={NotFoundPage} />\n      </Switch>\n    </Suspense>\n    <Suspense fallback={<FooterContentLoader />}>\n      <Footer />\n    </Suspense>\n  </Fragment>\n);\n"
  },
  {
    "path": "frontend/src/sagas/index.tsx",
    "content": "import { all } from 'redux-saga/effects';\n\nimport notes from 'services/notes/sagas';\n\nexport default function* sagas() {\n  yield all([\n    notes(),\n  ]);\n}\n"
  },
  {
    "path": "frontend/src/sass/global.scss",
    "content": "@import \"variables\";\n\n:global {\n  @import \"~materialize-css/sass/materialize\";\n\n  .container {\n    height: 100%;\n  }\n\n  .page-title {\n    color: $primary-color;\n    text-align: center;\n  }\n\n  @media only screen and (max-width: $medium-screen) {\n    .page-title {\n      font-size: 4em;\n    }\n  }\n\n  @media only screen and (max-width: $small-screen) {\n    .page-title {\n      font-size: 2.5em;\n    }\n  }\n\n  // Highlight css-module caused undefined class\n  .undefined {\n    border: solid white 3px;\n    outline: solid red 3px;\n  }\n}\n"
  },
  {
    "path": "frontend/src/sass/variables.scss",
    "content": "// This file override materialize css variables\n// https://github.com/Dogfalo/materialize/blob/master/sass/components/_variables.scss\n\n$primary-color: #2196F3;\n$secondary-color: #26A69A;\n$carousel-height: 80%;\n$small-screen: 600px;\n$medium-screen: 992px;\n$large-screen: 1200px;\n"
  },
  {
    "path": "frontend/src/services/notes/actions.tsx",
    "content": "import {ADD_NOTE_REQUESTED, EDIT_NOTE_REQUESTED, FETCH_ALL_NOTES_REQUESTED, FETCH_NOTE_REQUESTED, REMOVE_NOTE_REQUESTED } from './constants';\nimport { IActionAddNoteRequested, IActionEditNoteRequested, IActionFetchAllNotesRequested, IActionFetchNoteRequested, IActionRemoveNoteRequested } from './types';\n\nexport const fetchAllNotes = (payload: IActionFetchAllNotesRequested['payload']): IActionFetchAllNotesRequested => ({\n  type: FETCH_ALL_NOTES_REQUESTED,\n  payload,\n});\n\nexport const fetchNote = (payload: IActionFetchNoteRequested['payload']): IActionFetchNoteRequested => ({\n  type: FETCH_NOTE_REQUESTED,\n  payload,\n});\n\nexport const editNote = (payload: IActionEditNoteRequested['payload']): IActionEditNoteRequested => ({\n  type: EDIT_NOTE_REQUESTED,\n  payload,\n});\n\nexport const addNote = (payload: IActionAddNoteRequested['payload']): IActionAddNoteRequested => ({\n  type: ADD_NOTE_REQUESTED,\n  payload,\n});\n\nexport const removeNote = (payload: IActionRemoveNoteRequested['payload']): IActionRemoveNoteRequested => ({\n  type: REMOVE_NOTE_REQUESTED,\n  payload,\n});\n"
  },
  {
    "path": "frontend/src/services/notes/apis.tsx",
    "content": "import axios from 'axios';\n\nimport { IActionAddNoteRequested, IActionEditNoteRequested, IActionFetchNoteRequested, IActionRemoveNoteRequested } from './types';\n\nconst notesUrl = 'api/v1/notes';\n\nexport default class NotesAPI {\n  public static fetchAll() {\n    return axios.get(`${notesUrl}`, {\n      headers: {\n        Accept: 'application/json',\n      },\n    }).then((res) => {\n      return res.data;\n    }).catch((err) => {\n      if (err.response != null) {\n        throw Error(err.response.data.error.message);\n      }\n      throw Error(err);\n    });\n  }\n\n  public static fetch(payload: IActionFetchNoteRequested['payload']) {\n    return axios.get(`${notesUrl}/${payload.id}`, {\n      headers: {\n        Accept: 'application/json',\n      },\n    }).then((res) => {\n      return res.data;\n    }).catch((err) => {\n      if (err.response != null) {\n        throw Error(err.response.data.error.message);\n      }\n      throw Error(err);\n    });\n  }\n\n  public static add(payload: IActionAddNoteRequested['payload']) {\n    return axios.post(notesUrl, payload, {\n      headers: {\n        'Content-Type': 'application/json',\n        'Accept': 'application/json',\n      },\n    }).then((res) => {\n      return res.data;\n    }).catch((err) => {\n      if (err.response != null) {\n        throw Error(err.response.data.error.message);\n      }\n      throw Error(err);\n    });\n  }\n\n  public static edit(payload: IActionEditNoteRequested['payload']) {\n    return axios.put(`${notesUrl}/${payload.id}`, payload, {\n      headers: {\n        'Content-Type': 'application/json',\n        'Accept': 'application/json',\n      },\n    }).then((res) => {\n      return res.data;\n    }).catch((err) => {\n      if (err.response != null) {\n        throw Error(err.response.data.error.message);\n      }\n      throw Error(err);\n    });\n  }\n\n  public static remove(payload: IActionRemoveNoteRequested['payload']) {\n    return axios.delete(`${notesUrl}/${payload.id}`, {\n      headers: {\n        Accept: 'application/json',\n      },\n    }).then((res) => {\n      return res.data;\n    }).catch((err) => {\n      if (err.response != null) {\n        throw Error(err.response.data.error.message);\n      }\n      throw Error(err);\n    });\n  }\n}\n"
  },
  {
    "path": "frontend/src/services/notes/constants.tsx",
    "content": "import { IAsyncCall } from 'types/global';\n\nexport const FETCH_ALL_NOTES_REQUESTED = 'FETCH_ALL_NOTES/REQUESTED';\nexport const FETCH_ALL_NOTES_SUCCESS = 'FETCH_ALL_NOTES/SUCCESS';\nexport const FETCH_ALL_NOTES_FAILURE = 'FETCH_ALL_NOTES/FAILURE';\nexport const ASYNC_FETCH_ALL_NOTES: IAsyncCall = {\n  REQUESTED: FETCH_ALL_NOTES_REQUESTED,\n  SUCCESS: FETCH_ALL_NOTES_SUCCESS,\n  FAILURE: FETCH_ALL_NOTES_FAILURE,\n};\nexport const FETCH_NOTE_REQUESTED = 'FETCH_NOTE/REQUESTED';\nexport const FETCH_NOTE_SUCCESS = 'FETCH_NOTE/SUCCESS';\nexport const FETCH_NOTE_FAILURE = 'FETCH_NOTE/FAILURE';\nexport const ASYNC_FETCH_NOTE: IAsyncCall = {\n  REQUESTED: FETCH_NOTE_REQUESTED,\n  SUCCESS: FETCH_NOTE_SUCCESS,\n  FAILURE: FETCH_NOTE_FAILURE,\n};\nexport const EDIT_NOTE_REQUESTED = 'EDIT_NOTE/REQUESTED';\nexport const EDIT_NOTE_SUCCESS = 'EDIT_NOTE/SUCCESS';\nexport const EDIT_NOTE_FAILURE = 'EDIT_NOTE/FAILURE';\nexport const ASYNC_EDIT_NOTE: IAsyncCall = {\n  REQUESTED: EDIT_NOTE_REQUESTED,\n  SUCCESS: EDIT_NOTE_SUCCESS,\n  FAILURE: EDIT_NOTE_FAILURE,\n};\nexport const ADD_NOTE_REQUESTED = 'ADD_NOTE/REQUESTED';\nexport const ADD_NOTE_SUCCESS = 'ADD_NOTE/SUCCESS';\nexport const ADD_NOTE_FAILURE = 'ADD_NOTE/FAILURE';\nexport const ASYNC_ADD_NOTE: IAsyncCall = {\n  REQUESTED: ADD_NOTE_REQUESTED,\n  SUCCESS: ADD_NOTE_SUCCESS,\n  FAILURE: ADD_NOTE_FAILURE,\n};\nexport const REMOVE_NOTE_REQUESTED = 'REMOVE_NOTE/REQUESTED';\nexport const REMOVE_NOTE_SUCCESS = 'REMOVE_NOTE/SUCCESS';\nexport const REMOVE_NOTE_FAILURE = 'REMOVE_NOTE/FAILURE';\nexport const ASYNC_REMOVE_NOTE: IAsyncCall = {\n  REQUESTED: REMOVE_NOTE_REQUESTED,\n  SUCCESS: REMOVE_NOTE_SUCCESS,\n  FAILURE: REMOVE_NOTE_FAILURE,\n};\n"
  },
  {
    "path": "frontend/src/services/notes/reducer.tsx",
    "content": "import { List, Record } from 'immutable';\n\nimport { FETCH_ALL_NOTES_FAILURE, FETCH_ALL_NOTES_REQUESTED, FETCH_ALL_NOTES_SUCCESS } from './constants';\nimport { IActionsNotes, INote, INotesState, INotesStateRecord } from './types';\n\nexport const getNotesStateRecord = (state: INotesState): INotesStateRecord => {\n  class NotesStateRecord extends Record(state) implements INotesStateRecord {}\n  return new NotesStateRecord();\n};\n\nconst initialState = getNotesStateRecord({\n  notes: List<INote>(),\n  loading: false,\n  error: '',\n});\n\nexport default (state: INotesStateRecord = initialState, action: IActionsNotes): INotesStateRecord => {\n  switch (action.type) {\n    case FETCH_ALL_NOTES_REQUESTED:\n      return state.set('loading', true);\n    case FETCH_ALL_NOTES_SUCCESS:\n      const noteList: INote[] = [];\n      action.payload.data.notes.forEach((note: INote) => {\n        noteList.push({\n          id: note.id,\n          content: note.content,\n        });\n      });\n      return state.clear().set('notes', List(noteList));\n    case FETCH_ALL_NOTES_FAILURE:\n      return state.clear().set('error', action.payload.error);\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "frontend/src/services/notes/sagas.tsx",
    "content": "import { all, call, put, takeEvery } from 'redux-saga/effects';\n\nimport { IAsyncCall } from 'types/global';\nimport NotesAPI from './apis';\nimport { ASYNC_ADD_NOTE, ASYNC_EDIT_NOTE, ASYNC_FETCH_ALL_NOTES, ASYNC_FETCH_NOTE, ASYNC_REMOVE_NOTE } from './constants';\n\nfunction* asyncHandler(action: IAsyncCall, api: (payload: any) => Promise<any>, payload: any) {\n  try {\n    const resJson = yield call(api, payload);\n    yield put({ type: action.SUCCESS, payload: { data: resJson.data } });\n  } catch (err) {\n    yield put({ type: action.FAILURE, payload: { error: err.message } });\n  }\n}\n\nfunction* sagaAsyncCallGenerator(action: IAsyncCall, api: (payload: any) => Promise<any>) {\n  yield takeEvery(action.REQUESTED, asyncHandler, action, api);\n}\n\nexport default function* rootSaga() {\n  yield all([\n    sagaAsyncCallGenerator(ASYNC_FETCH_ALL_NOTES, NotesAPI.fetchAll),\n    sagaAsyncCallGenerator(ASYNC_FETCH_NOTE, NotesAPI.fetch),\n    sagaAsyncCallGenerator(ASYNC_ADD_NOTE, NotesAPI.add),\n    sagaAsyncCallGenerator(ASYNC_EDIT_NOTE, NotesAPI.edit),\n    sagaAsyncCallGenerator(ASYNC_REMOVE_NOTE, NotesAPI.remove),\n  ]);\n}\n"
  },
  {
    "path": "frontend/src/services/notes/types.d.ts",
    "content": "import { List, Record } from 'immutable';\n\nimport { ADD_NOTE_FAILURE, ADD_NOTE_REQUESTED, ADD_NOTE_SUCCESS, EDIT_NOTE_FAILURE, EDIT_NOTE_REQUESTED, EDIT_NOTE_SUCCESS, FETCH_ALL_NOTES_FAILURE, FETCH_ALL_NOTES_REQUESTED, FETCH_ALL_NOTES_SUCCESS, FETCH_NOTE_FAILURE, FETCH_NOTE_REQUESTED, FETCH_NOTE_SUCCESS, REMOVE_NOTE_FAILURE, REMOVE_NOTE_REQUESTED, REMOVE_NOTE_SUCCESS } from './constants';\n\n// Notes state\nexport interface INotesState {\n  notes: List<INote>;\n  loading: boolean;\n  error: string;\n}\nexport interface INote {\n  id: number;\n  content: string;\n}\nexport interface INotesStateRecord extends Record<INotesState>, INotesState {}\n\n// Notes actions\nexport interface IActionFetchAllNotesRequested {\n  type: typeof FETCH_ALL_NOTES_REQUESTED;\n  payload: {};\n}\nexport interface IActionFetchAllNotesSuccess {\n  type: typeof FETCH_ALL_NOTES_SUCCESS;\n  payload: {\n    data: any,\n  };\n}\nexport interface IActionFetchAllNotesFailure {\n  type: typeof FETCH_ALL_NOTES_FAILURE;\n  payload: {\n    error: string,\n  };\n}\n\nexport interface IActionFetchNoteRequested {\n  type: typeof FETCH_NOTE_REQUESTED;\n  payload: {\n    id: number,\n  };\n}\nexport interface IActionFetchNoteSuccess {\n  type: typeof FETCH_NOTE_SUCCESS;\n  payload: {\n    data: any,\n  };\n}\nexport interface IActionFetchNoteFailure {\n  type: typeof FETCH_NOTE_FAILURE;\n  payload: {\n    error: string,\n  };\n}\n\nexport interface IActionAddNoteRequested {\n  type: typeof ADD_NOTE_REQUESTED;\n  payload: {\n    content: string,\n  };\n}\nexport interface IActionAddNoteSuccess {\n  type: typeof ADD_NOTE_SUCCESS;\n  payload: {\n    data: any,\n  };\n}\nexport interface IActionAddNoteFailure {\n  type: typeof ADD_NOTE_FAILURE;\n  payload: {\n    error: string,\n  };\n}\n\nexport interface IActionEditNoteRequested {\n  type: typeof EDIT_NOTE_REQUESTED;\n  payload: {\n    id: number,\n    content: string,\n  };\n}\nexport interface IActionEditNoteSuccess {\n  type: typeof EDIT_NOTE_SUCCESS;\n  payload: {\n    data: any,\n  };\n}\nexport interface IActionEditNoteFailure {\n  type: typeof EDIT_NOTE_FAILURE;\n  payload: {\n    error: string,\n  };\n}\n\nexport interface IActionRemoveNoteRequested {\n  type: typeof REMOVE_NOTE_REQUESTED;\n  payload: {\n    id: number,\n  };\n}\nexport interface IActionRemoveNoteSuccess {\n  type: typeof REMOVE_NOTE_SUCCESS;\n  payload: {\n    data: any,\n  };\n}\nexport interface IActionRemoveNoteFailure {\n  type: typeof REMOVE_NOTE_FAILURE;\n  payload: {\n    error: string,\n  };\n}\n\nexport type IActionsNotes\n  = IActionFetchAllNotesRequested\n  | IActionFetchAllNotesSuccess\n  | IActionFetchAllNotesFailure\n  | IActionFetchNoteRequested\n  | IActionFetchNoteSuccess\n  | IActionFetchNoteFailure\n  | IActionAddNoteRequested\n  | IActionAddNoteSuccess\n  | IActionAddNoteFailure\n  | IActionEditNoteRequested\n  | IActionEditNoteSuccess\n  | IActionEditNoteFailure\n  | IActionRemoveNoteRequested\n  | IActionRemoveNoteSuccess\n  | IActionRemoveNoteFailure;\n"
  },
  {
    "path": "frontend/src/services/todos/__test__/actions.spec.tsx",
    "content": "import { addTodo, setVisibilityFilter, toggleTodo } from '../actions';\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO, VISIBILITY_FILTER_OPTIONS } from '../constants';\nimport { IActionAddTodo, IActionSetVisibilityFilter, IActionToggleTodo } from '../types';\n\ndescribe('[Actions] todos test', () => {\n  it('[addTodo] should return IActionAddTodo with input text, random id string and completed as false', () => {\n    const actionAddTodo: IActionAddTodo = addTodo('text');\n    expect(actionAddTodo.type === ADD_TODO).toBeTruthy();\n    expect(typeof actionAddTodo.id === 'string').toBeTruthy();\n    expect(actionAddTodo.text).toBe('text');\n    expect(actionAddTodo.completed).toBe(false);\n  });\n\n  it('[toggleTodo] should return IActionToggleTodo with input id', () => {\n    const actionToggleTodo: IActionToggleTodo = toggleTodo('id');\n    expect(actionToggleTodo.type === TOGGLE_TODO).toBeTruthy();\n    expect(actionToggleTodo.id).toBe('id');\n  });\n\n  it('[setVisibilityFilter] should return IActionSetVisibilityFilter with input filter', () => {\n    const actionSetVisibilityShowAll: IActionSetVisibilityFilter = setVisibilityFilter(VISIBILITY_FILTER_OPTIONS.SHOW_ALL);\n    expect(actionSetVisibilityShowAll.type === SET_VISIBILITY_FILTER).toBeTruthy();\n    expect(actionSetVisibilityShowAll.filter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ALL);\n\n    const actionSetVisibilityShowActive: IActionSetVisibilityFilter = setVisibilityFilter(VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE);\n    expect(actionSetVisibilityShowActive.type === SET_VISIBILITY_FILTER).toBeTruthy();\n    expect(actionSetVisibilityShowActive.filter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE);\n\n    const actionSetVisibilityShowComleted: IActionSetVisibilityFilter = setVisibilityFilter(VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED);\n    expect(actionSetVisibilityShowComleted.type === SET_VISIBILITY_FILTER).toBeTruthy();\n    expect(actionSetVisibilityShowComleted.filter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED);\n  });\n});\n"
  },
  {
    "path": "frontend/src/services/todos/__test__/reducer.spec.tsx",
    "content": "import { List } from 'immutable';\n\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO, VISIBILITY_FILTER_OPTIONS } from '../constants';\nimport reducer, { getTodosStateRecord } from '../reducer';\nimport { IActionAddTodo, IActionSetVisibilityFilter, IActionToggleTodo, ITodo } from '../types';\n\ndescribe('[Reducers] todos test', () => {\n  const initialState = getTodosStateRecord({\n    todos: List<ITodo>([\n      {\n        id: 'initial_id',\n        text: 'initial_text',\n        completed: false,\n      },\n    ]),\n    visibilityFilter: VISIBILITY_FILTER_OPTIONS.SHOW_ALL,\n  });\n\n  const actionAddTodo: IActionAddTodo = {\n    type: ADD_TODO,\n    id: 'test_id',\n    text: 'test_text',\n    completed: false,\n  };\n  const actionToggleTodo: IActionToggleTodo = {\n    type: TOGGLE_TODO,\n    id: 'initial_id',\n  };\n  const actionToggleTodoInvalid: IActionToggleTodo = {\n    type: TOGGLE_TODO,\n    id: 'invalid_id',\n  };\n  const actionShowAll: IActionSetVisibilityFilter = {\n    type: SET_VISIBILITY_FILTER,\n    filter: VISIBILITY_FILTER_OPTIONS.SHOW_ALL,\n  };\n  const actionShowActive: IActionSetVisibilityFilter = {\n    type: SET_VISIBILITY_FILTER,\n    filter: VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE,\n  };\n  const actionShowCompleted: IActionSetVisibilityFilter = {\n    type: SET_VISIBILITY_FILTER,\n    filter: VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED,\n  };\n\n  beforeEach(() => {\n    expect(initialState.todos.count()).toBe(1);\n    expect(initialState.visibilityFilter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ALL);\n  });\n\n  it('[ADD_TODO] should return state with new todo added', () => {\n    const stateAddTodo = reducer(initialState, actionAddTodo);\n    expect(stateAddTodo.visibilityFilter).toEqual(initialState.visibilityFilter);\n    expect(stateAddTodo.todos).toEqual(List([\n      {\n        id: 'initial_id',\n        text: 'initial_text',\n        completed: false,\n      },\n      {\n        id: 'test_id',\n        text: 'test_text',\n        completed: false,\n      },\n    ]));\n  });\n\n  it('[TOGGLE_TODO] should return state with one todo completed toggled', () => {\n    const stateToggleTodo = reducer(initialState, actionToggleTodo);\n    expect(stateToggleTodo.visibilityFilter).toEqual(initialState.visibilityFilter);\n    expect(stateToggleTodo.todos).toEqual(List([\n      {\n        id: 'initial_id',\n        text: 'initial_text',\n        completed: true,\n      },\n    ]));\n  });\n\n  it('[TOGGLE_TODO] should return previous state if id is not found', () => {\n    const stateToggleTodoInvalid = reducer(initialState, actionToggleTodoInvalid);\n    expect(stateToggleTodoInvalid.visibilityFilter).toEqual(initialState.visibilityFilter);\n    expect(stateToggleTodoInvalid.todos).toEqual(initialState.todos);\n  });\n\n  it('[SET_VISIBILITY_FILTER] should return state with corresponding filter', () => {\n    const stateShowCompleted = reducer(initialState, actionShowCompleted);\n    expect(stateShowCompleted.visibilityFilter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED);\n    expect(stateShowCompleted.todos).toEqual(initialState.todos);\n\n    const stateShowActive = reducer(stateShowCompleted, actionShowActive);\n    expect(stateShowActive.visibilityFilter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE);\n    expect(stateShowActive.todos).toEqual(initialState.todos);\n\n    const stateShowAll = reducer(stateShowActive, actionShowAll);\n    expect(stateShowAll.visibilityFilter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ALL);\n    expect(stateShowAll.todos).toEqual(initialState.todos);\n  });\n\n  it('[DEFAULT_ACTION] should return previous state if action is not found', () => {\n    const stateTestDefault = reducer(initialState, {} as any);\n    expect(stateTestDefault.visibilityFilter).toEqual(initialState.visibilityFilter);\n    expect(stateTestDefault.todos).toEqual(initialState.todos);\n  });\n\n  it('[UNDEFINED_STATE] should use default state if state is not defined', () => {\n    const stateTestUndefined = reducer(undefined, {} as any);\n    expect(stateTestUndefined.visibilityFilter).toEqual(initialState.visibilityFilter);\n    expect(stateTestUndefined.todos).toEqual(List([\n      {\n        id: 'fake_id',\n        text: 'Add your own todo task above, click to mark each todo as completed',\n        completed: false,\n      },\n    ]));\n  });\n});\n"
  },
  {
    "path": "frontend/src/services/todos/actions.tsx",
    "content": "import { v4 } from 'uuid';\n\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO } from './constants';\nimport { IActionAddTodo, IActionSetVisibilityFilter, IActionToggleTodo } from './types';\n\nexport const addTodo = (text: string): IActionAddTodo => ({\n  type: ADD_TODO,\n  id: v4(),\n  text,\n  completed: false,\n});\n\nexport const toggleTodo = (id: string): IActionToggleTodo => ({\n  type: TOGGLE_TODO,\n  id,\n});\n\nexport const setVisibilityFilter = (filter: string): IActionSetVisibilityFilter => ({\n  type: SET_VISIBILITY_FILTER,\n  filter,\n});\n"
  },
  {
    "path": "frontend/src/services/todos/constants.tsx",
    "content": "// Todo action types\nexport const ADD_TODO = 'ADD_TODO';\nexport const TOGGLE_TODO = 'TOGGLE_TODO';\n\n// Visibility action types\nexport const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';\n\n// Visibility filter options\nexport const VISIBILITY_FILTER_OPTIONS = {\n  SHOW_ALL: 'SHOW_ALL',\n  SHOW_COMPLETED: 'SHOW_COMPLETED',\n  SHOW_ACTIVE: 'SHOW_ACTIVE',\n};\n"
  },
  {
    "path": "frontend/src/services/todos/reducer.tsx",
    "content": "import { List, Record } from 'immutable';\n\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO, VISIBILITY_FILTER_OPTIONS } from './constants';\nimport { IActionsTodo, ITodo, ITodosState, ITodosStateRecord } from './types';\n\nexport const getTodosStateRecord = (state: ITodosState): ITodosStateRecord => {\n  class TodosStateRecord extends Record(state) implements ITodosStateRecord {}\n  return new TodosStateRecord();\n};\n\nconst initialState = getTodosStateRecord({\n  todos: List<ITodo>([\n    {\n      id: 'fake_id',\n      text: 'Add your own todo task above, click to mark each todo as completed',\n      completed: false,\n    },\n  ]),\n  visibilityFilter: VISIBILITY_FILTER_OPTIONS.SHOW_ALL,\n});\n\nexport default (state: ITodosStateRecord = initialState, action: IActionsTodo): ITodosStateRecord => {\n  switch (action.type) {\n    case ADD_TODO:\n      return state.update('todos', (todos) => todos.push({\n        id: action.id,\n        text: action.text,\n        completed: action.completed,\n      }));\n    case TOGGLE_TODO:\n      const index = state.todos.findIndex((s) => s !== undefined && s.id === action.id);\n      return state.update('todos', (todos) => index === -1 ? todos : todos.update(index, (s) => ({ ...s, completed: !s.completed })));\n    case SET_VISIBILITY_FILTER:\n      return state.set('visibilityFilter', action.filter);\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "frontend/src/services/todos/types.d.ts",
    "content": "import { List, Record } from 'immutable';\n\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO } from './constants';\n\n// Todos state\nexport interface ITodosState {\n  todos: List<ITodo>;\n  visibilityFilter: string;\n}\nexport interface ITodo {\n  id: string;\n  text: string;\n  completed: boolean;\n}\nexport interface ITodosStateRecord extends Record<ITodosState>, ITodosState {}\n\n// Todos actions\nexport interface IActionAddTodo {\n  type: typeof ADD_TODO;\n  id: string;\n  text: string;\n  completed: boolean;\n}\nexport interface IActionToggleTodo {\n  type: typeof TOGGLE_TODO;\n  id: string;\n}\nexport interface IActionSetVisibilityFilter {\n  type: typeof SET_VISIBILITY_FILTER;\n  filter: string;\n}\n\nexport type IActionsTodo\n  = IActionAddTodo\n  | IActionToggleTodo\n  | IActionSetVisibilityFilter;\n"
  },
  {
    "path": "frontend/src/store/index.tsx",
    "content": "import { routerMiddleware } from 'connected-react-router/immutable';\nimport { History } from 'history';\nimport Immutable from 'immutable';\nimport { applyMiddleware, compose, createStore, Store } from 'redux';\nimport logger from 'redux-logger';\nimport createSagaMiddleware from 'redux-saga';\n\nimport createRootReducer from 'reducers';\nimport sagas from 'sagas';\nimport { IGlobalState } from 'types/global';\n\nexport default (history: History, initialState: IGlobalState | {}): Store<IGlobalState> => {\n  // Create the saga middleware\n  const sagaMiddleware = createSagaMiddleware();\n\n  // Enhancer\n  const composeEnhancers =\n    typeof window === 'object' && (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?\n      (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({\n        serialize: {\n          immutable: Immutable,\n        },\n      }) : compose;\n  const enhancer = composeEnhancers(\n    applyMiddleware(routerMiddleware(history), sagaMiddleware, logger),\n  );\n\n  // Store\n  const store = createStore(createRootReducer(history), initialState, enhancer);\n  sagaMiddleware.run(sagas);\n\n  // Enable Webpack hot module replacement for reducers\n  if (module.hot) {\n    module.hot.accept('../reducers', () => {\n      store.replaceReducer(createRootReducer(history));\n    });\n  }\n\n  return store;\n};\n"
  },
  {
    "path": "frontend/src/types/global.d.ts",
    "content": "import { RouterState } from 'connected-react-router/immutable';\nimport { Record } from 'immutable';\n\nimport { INotesState, INotesStateRecord } from 'services/notes/types';\nimport { ITodosState, ITodosStateRecord } from 'services/todos/types';\n\n// Global state\nexport interface IGlobalState {\n  todosState: ITodosStateRecord;\n  notesState: INotesStateRecord;\n  router: RouterState;\n}\nexport interface IGlobalStateRecord extends Record<IGlobalState>, IGlobalState {}\n\n// Interface for async call steps\nexport interface IAsyncCall {\n  REQUESTED: string;\n  SUCCESS: string;\n  FAILURE: string;\n}\n"
  },
  {
    "path": "frontend/src/utils/index.tsx",
    "content": "export const isProduction = process.env.NODE_ENV === 'production';\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"express-webpack-react-redux-typescript-boilerplate\",\n  \"version\": \"1.0.0\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate.git\"\n  },\n  \"author\": \"Chong Guo <armourcy@gmail.com>\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"clean\": \"trash frontend/dist coverage\",\n    \"dll\": \"cross-env NODE_ENV=production webpack --config webpack.config.dll.babel.js\",\n    \"dev\": \"yarn clean && yarn dll && concurrently \\\"yarn dev-server\\\" \\\"yarn dev-client\\\"\",\n    \"dev-server\": \"cross-env NODE_ENV=development nodemon --exec babel-node backend/server.js\",\n    \"dev-client\": \"cross-env NODE_ENV=development webpack-dev-server --hot --config webpack.config.dev.babel.js\",\n    \"prod\": \"yarn build && yarn serve\",\n    \"build\": \"yarn clean && yarn dll && cross-env NODE_ENV=production webpack --config webpack.config.prod.babel.js\",\n    \"serve\": \"cross-env NODE_ENV=production nodemon --exec babel-node backend/server.js\",\n    \"profile\": \"yarn clean && yarn dll && cross-env NODE_ENV=production webpack --config webpack.config.profile.babel.js\",\n    \"eslint\": \"eslint -f codeframe \\\"**/*.js\\\"\",\n    \"tslint\": \"tslint -t codeFrame -c tslint.json \\\"**/*.tsx\\\" \\\"**/*.d.ts\\\"\",\n    \"stylelint\": \"stylelint \\\"**/*.css **/*.sass **/*.scss\\\"\",\n    \"lint\": \"yarn eslint && yarn tslint && yarn stylelint\",\n    \"test\": \"jest --runInBand --coverage\",\n    \"coverage\": \"cat ./coverage/lcov.info | ./node_modules/.bin/coveralls\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"yarn lint\",\n      \"commit-msg\": \"commitlint -e $HUSKY_GIT_PARAMS\",\n      \"pre-push\": \"yarn test && yarn coverage\"\n    }\n  },\n  \"dependencies\": {\n    \"@babel/cli\": \"^7.2.3\",\n    \"@babel/core\": \"^7.2.2\",\n    \"@babel/plugin-proposal-class-properties\": \"^7.3.0\",\n    \"@babel/plugin-syntax-dynamic-import\": \"^7.2.0\",\n    \"@babel/plugin-transform-runtime\": \"^7.2.0\",\n    \"@babel/preset-env\": \"^7.3.1\",\n    \"@babel/preset-react\": \"^7.0.0\",\n    \"@babel/preset-typescript\": \"^7.1.0\",\n    \"@types/i18next\": \"^12.1.0\",\n    \"@types/i18next-browser-languagedetector\": \"^2.0.1\",\n    \"@types/i18next-xhr-backend\": \"^1.4.1\",\n    \"@types/materialize-css\": \"^1.0.6\",\n    \"@types/prismjs\": \"^1.9.0\",\n    \"@types/react\": \"^16.8.2\",\n    \"@types/react-content-loader\": \"^3.1.4\",\n    \"@types/react-dom\": \"^16.8.0\",\n    \"@types/react-redux\": \"^7.0.1\",\n    \"@types/react-router\": \"^4.4.3\",\n    \"@types/react-router-dom\": \"^4.3.1\",\n    \"@types/redux-immutable\": \"^4.0.1\",\n    \"@types/redux-logger\": \"^3.0.6\",\n    \"@types/uuid\": \"^3.4.4\",\n    \"@types/webpack-env\": \"^1.13.7\",\n    \"add-asset-html-webpack-plugin\": \"^3.1.3\",\n    \"awesome-typescript-loader\": \"^5.2.1\",\n    \"axios\": \"^0.18.0\",\n    \"babel-loader\": \"^8.0.5\",\n    \"babel-plugin-prismjs\": \"^1.0.2\",\n    \"body-parser\": \"^1.18.3\",\n    \"colors\": \"^1.3.3\",\n    \"compression\": \"^1.7.3\",\n    \"concurrently\": \"^4.1.0\",\n    \"connect-redis\": \"^3.4.0\",\n    \"connected-react-router\": \"^6.2.2\",\n    \"cookie-parser\": \"^1.4.3\",\n    \"copy-webpack-plugin\": \"^4.6.0\",\n    \"cross-env\": \"^5.2.0\",\n    \"css-loader\": \"^2.1.0\",\n    \"cssnano\": \"^4.1.8\",\n    \"dotenv-webpack\": \"^1.7.0\",\n    \"duplicate-package-checker-webpack-plugin\": \"^3.0.0\",\n    \"express\": \"^4.16.4\",\n    \"express-session\": \"^1.15.6\",\n    \"file-loader\": \"^3.0.1\",\n    \"fork-ts-checker-webpack-plugin\": \"^0.5.2\",\n    \"helmet\": \"^3.15.0\",\n    \"history\": \"^4.7.2\",\n    \"html-webpack-plugin\": \"^3.2.0\",\n    \"http-status\": \"^1.3.1\",\n    \"i18next\": \"^14.1.1\",\n    \"i18next-browser-languagedetector\": \"^2.2.4\",\n    \"i18next-xhr-backend\": \"^1.5.1\",\n    \"image-webpack-loader\": \"^4.6.0\",\n    \"immutable\": \"4.0.0-rc.12\",\n    \"materialize-css\": \"1.0.0\",\n    \"mini-css-extract-plugin\": \"^0.5.0\",\n    \"morgan\": \"^1.9.1\",\n    \"node-sass\": \"^4.11.0\",\n    \"nodemon\": \"^1.18.9\",\n    \"offline-plugin\": \"^5.0.6\",\n    \"pg\": \"^7.8.0\",\n    \"postcss\": \"^7.0.14\",\n    \"postcss-import\": \"^12.0.1\",\n    \"postcss-loader\": \"^3.0.0\",\n    \"postcss-preset-env\": \"^6.5.0\",\n    \"prismjs\": \"^1.15.0\",\n    \"progress-bar-webpack-plugin\": \"^1.12.1\",\n    \"react\": \"^16.8.1\",\n    \"react-content-loader\": \"^4.0.1\",\n    \"react-dom\": \"^16.8.1\",\n    \"react-hot-loader\": \"^4.6.5\",\n    \"react-i18next\": \"^10.0.1\",\n    \"react-redux\": \"^6.0.0\",\n    \"react-router\": \"^4.3.1\",\n    \"react-router-dom\": \"^4.3.1\",\n    \"redux\": \"^4.0.1\",\n    \"redux-immutable\": \"^4.0.0\",\n    \"redux-logger\": \"^3.0.6\",\n    \"redux-saga\": \"^1.0.1\",\n    \"sass-loader\": \"^7.1.0\",\n    \"style-loader\": \"^0.23.1\",\n    \"trash-cli\": \"^1.4.0\",\n    \"ts-loader\": \"^5.3.3\",\n    \"typescript\": \"^3.3.3\",\n    \"url-loader\": \"^1.1.2\",\n    \"uuid\": \"^3.3.2\",\n    \"webpack\": \"^4.29.3\",\n    \"webpack-bundle-analyzer\": \"^3.0.3\",\n    \"webpack-cli\": \"^3.2.3\",\n    \"webpack-merge\": \"^4.2.1\",\n    \"webpack-pwa-manifest\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"@babel/node\": \"^7.2.2\",\n    \"@types/jest\": \"^24.0.0\",\n    \"@types/react-test-renderer\": \"^16.8.0\",\n    \"babel-core\": \"7.0.0-bridge.0\",\n    \"case-sensitive-paths-webpack-plugin\": \"^2.2.0\",\n    \"commitlint\": \"^7.5.0\",\n    \"commitlint-config-armour\": \"^1.2.1\",\n    \"coveralls\": \"^3.0.2\",\n    \"dotenv\": \"^6.2.0\",\n    \"eslint\": \"^5.13.0\",\n    \"eslint-config-airbnb\": \"^17.1.0\",\n    \"eslint-import-resolver-webpack\": \"^0.11.0\",\n    \"eslint-plugin-import\": \"^2.16.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.2.1\",\n    \"eslint-plugin-react\": \"^7.12.4\",\n    \"husky\": \"1.3.1\",\n    \"identity-obj-proxy\": \"^3.0.0\",\n    \"jest\": \"^24.1.0\",\n    \"react-test-renderer\": \"^16.8.1\",\n    \"stylelint\": \"^9.10.1\",\n    \"stylelint-config-standard\": \"^18.2.0\",\n    \"ts-jest\": \"^23.10.5\",\n    \"tslint\": \"^5.12.1\",\n    \"tslint-react\": \"^3.6.0\",\n    \"webpack-dev-server\": \"^3.1.14\"\n  },\n  \"resolutions\": {\n    \"**/event-stream\": \"^4.0.1\"\n  },\n  \"browserslist\": [\n    \"> 1%\",\n    \"last 2 versions\"\n  ],\n  \"nodemonConfig\": {\n    \"watch\": [\n      \"backend/controllers/\",\n      \"backend/routes/\",\n      \"backend/db/\",\n      \"backend/config.json\",\n      \"backend/server.js\"\n    ]\n  },\n  \"jest\": {\n    \"preset\": \"ts-jest\",\n    \"moduleNameMapper\": {\n      \"\\\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$\": \"<rootDir>/__mocks__/fileMock.tsx\",\n      \"\\\\.(css|less|scss|sass)$\": \"identity-obj-proxy\"\n    },\n    \"moduleDirectories\": [\n      \"node_modules\",\n      \"frontend/src\",\n      \"backend\"\n    ],\n    \"moduleFileExtensions\": [\n      \"js\",\n      \"jsx\",\n      \"json\",\n      \"ts\",\n      \"tsx\"\n    ]\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"baseUrl\": \"frontend/src\",\n    \"target\": \"es6\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"jsx\": \"react\",\n    \"esModuleInterop\": true,\n    \"noEmitOnError\": true,\n    \"noImplicitReturns\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"sourceMap\": true,\n    \"strict\": true\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"**/__tests__/**\"\n  ]\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n  \"extends\": [\n    \"tslint:recommended\",\n    \"tslint-react\"\n  ],\n  \"rules\": {\n    \"jsx-boolean-value\": false,\n    \"jsx-no-multiline-js\": false,\n    \"max-line-length\": false,\n    \"no-console\": [\n      true,\n      {\n        \"allow\": [\n          \"warn\",\n          \"error\"\n        ]\n      }\n    ],\n    \"no-require-imports\": false,\n    \"no-submodule-imports\": false,\n    \"no-var-requires\": false,\n    \"newline-before-return\": false,\n    \"object-literal-sort-keys\": false,\n    \"quotemark\": [\n      true,\n      \"single\",\n      \"avoid-escape\",\n      \"avoid-template\"\n    ],\n    \"trailing-comma\": [\n      true,\n      {\n        \"multiline\": \"always\",\n        \"singleline\": \"never\"\n      }\n    ],\n    \"variable-name\": [\n      true,\n      \"ban-keywords\"\n    ]\n  },\n  \"linterOptions\": {\n    \"exclude\": [\n      \"node_modules/**\"\n    ]\n  }\n}\n"
  },
  {
    "path": "webpack.config.base.babel.js",
    "content": "import 'dotenv/config'; // Allow webpack config file to use .env variables\n\nimport path from 'path';\nimport webpack from 'webpack';\n\nimport cssnano from 'cssnano';\nimport postcssImport from 'postcss-import';\nimport postcssPresetEnv from 'postcss-preset-env';\n\nimport AddAssetHtmlPlugin from 'add-asset-html-webpack-plugin';\nimport CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';\nimport CopyWebpackPlugin from 'copy-webpack-plugin';\nimport DotenvPlugin from 'dotenv-webpack';\nimport DuplicatePackageCheckerPlugin from 'duplicate-package-checker-webpack-plugin';\nimport HtmlWebpackPlugin from 'html-webpack-plugin';\nimport MiniCssExtractPlugin from 'mini-css-extract-plugin';\nimport ProgressBarWebpackPlugin from 'progress-bar-webpack-plugin';\nimport ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';\n\nconst ReactManifest = './frontend/dist/dll/react_manifest.json';\nconst MaterializeManifest = './frontend/dist/dll/materialize_manifest.json';\nconst I18nextManifest = './frontend/dist/dll/i18next_manifest.json';\nconst OtherManifest = './frontend/dist/dll/other_manifest.json';\nconst devMode = process.env.NODE_ENV !== 'production';\n\nexport default {\n  // The base directory, an absolute path, for resolving entry points and loaders from configuration\n  context: path.resolve(__dirname),\n\n  // Get mode from NODE_ENV\n  mode: process.env.NODE_ENV,\n\n  // Determine how the different types of modules within a project will be treated\n  module: {\n    rules: [\n      // Use babel-loader for ts(x) files\n      {\n        test: /\\.tsx?$/,\n        exclude: /node_modules/,\n        use: [\n          {\n            loader: 'babel-loader',\n            options: {\n              cacheDirectory: true,\n            },\n          },\n        ],\n      },\n      // Use a list of loaders to load materialize and prism css files\n      {\n        test: /\\.css$/,\n        use: [\n          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,\n          {\n            loader: 'css-loader',\n            options: {\n              sourceMap: !devMode,\n              modules: true,\n              importLoaders: 1,\n            },\n          },\n          {\n            loader: 'postcss-loader',\n            options: {\n              sourceMap: true,\n              plugins: () => [postcssImport, postcssPresetEnv, cssnano],\n            },\n          },\n        ],\n      },\n      // Use a list of loaders to load scss files\n      {\n        test: /\\.scss$/,\n        use: [\n          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,\n          {\n            loader: 'css-loader',\n            options: {\n              sourceMap: !devMode,\n              modules: true,\n              importLoaders: 2,\n            },\n          },\n          {\n            loader: 'postcss-loader',\n            options: {\n              sourceMap: true,\n              plugins: () => [postcssImport, postcssPresetEnv, cssnano],\n            },\n          },\n          { loader: 'sass-loader', options: { sourceMap: true } },\n        ],\n      },\n      // Use image-webpack-loader and url-loader to load images\n      {\n        test: /\\.(png|jpe?g|gif|svg|webp|tiff)(\\?.*)?$/,\n        use: [\n          { loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]' } },\n          { loader: 'image-webpack-loader', options: { disable: devMode } },\n        ],\n      },\n      // Use url-loader to load font related files\n      {\n        test: /\\.(woff2?|eot|ttf|otf)(\\?.*)?$/,\n        use: [\n          { loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]' } },\n        ],\n      },\n      // Use url-loader to load audio related files\n      {\n        test: /\\.(mp4|webm|ogg|mp3|wav|flac|aac)(\\?.*)?$/,\n        use: [\n          { loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]' } },\n        ],\n      },\n    ],\n  },\n\n  // A list of used webpack plugins\n  plugins: [\n    // Enforces case sensitive paths.\n    new CaseSensitivePathsPlugin(),\n    // Supports dotenv file\n    new DotenvPlugin(),\n    // Warns when multiple versions of the same package exist in a build\n    new DuplicatePackageCheckerPlugin(),\n    // Load pre-build dll reference files\n    new webpack.DllReferencePlugin({ manifest: ReactManifest }),\n    new webpack.DllReferencePlugin({ manifest: MaterializeManifest }),\n    new webpack.DllReferencePlugin({ manifest: I18nextManifest }),\n    new webpack.DllReferencePlugin({ manifest: OtherManifest }),\n    // Extract css part from javascript bundle into separated file\n    new MiniCssExtractPlugin({\n      filename: '[name].[contenthash:10].css',\n      chunkFilename: '[name].[contenthash:10].css',\n    }),\n    // Better building progress display\n    new ProgressBarWebpackPlugin(),\n    // Runs typescript type checker on a separate process\n    new ForkTsCheckerWebpackPlugin(),\n    // Generate html file to dist folder\n    new HtmlWebpackPlugin({\n      title: 'Boilerplate',\n      template: path.resolve(__dirname, 'frontend/public/index.ejs'),\n    }),\n    // Add dll reference files to html\n    new AddAssetHtmlPlugin({\n      filepath: path.resolve(__dirname, 'frontend/dist/dll/*_dll.js'),\n      includeSourcemap: false,\n    }),\n    // Copy static files to build dir\n    new CopyWebpackPlugin([\n      {\n        from: 'frontend/public',\n        ignore: ['index.ejs'],\n      },\n    ]),\n  ],\n\n  // Change how modules are resolved\n  resolve: {\n    // What directories should be searched when resolving modules\n    modules: [\n      'node_modules',\n      'frontend/src',\n    ],\n    // Automatically resolve certain extensions (Ex. import 'folder/name(.ext)')\n    extensions: [\n      '.ts',\n      '.tsx',\n      '.js',\n      '.jsx',\n      '.json',\n      '.css',\n      '.scss',\n    ],\n  },\n};\n"
  },
  {
    "path": "webpack.config.dev.babel.js",
    "content": "import path from 'path';\nimport merge from 'webpack-merge';\n\nimport BaseWebpackConfig from './webpack.config.base.babel';\n\nexport default merge(BaseWebpackConfig, {\n  // The point or points to enter the application.\n  entry: {\n    app: [\n      './frontend/src/index',\n    ],\n  },\n\n  // Affecting the output of the compilation\n  output: {\n    // path: the output directory as an absolute path (required)\n    path: path.resolve(__dirname, 'frontend/dist/dev'),\n    // filename: specifies the name of entry output file (required)\n    filename: '[name].[hash:10].js',\n    // chunkFilename: specifies the name of non-entry output files (e.g. dynamic import component)\n    chunkFilename: '[name].[hash:10].js',\n  },\n\n  devServer: {\n    // Port number for webpack dev server\n    port: process.env.PORT_WEBPACK_DEV_SERVER,\n    // Proxy for api call\n    proxy: {\n      '/api/v1': {\n        target: `http://localhost:${process.env.PORT}/`,\n        secure: false,\n      },\n    },\n    // Automatically open page\n    open: true,\n    // Serves index.html (contains 404 page in react-router) in place of any 404 responses\n    historyApiFallback: true,\n    // Shows a full-screen overlay when there are compiler errors\n    overlay: true,\n  },\n\n  // Source map mode\n  // https://webpack.js.org/configuration/devtool\n  devtool: 'eval-source-map',\n});\n"
  },
  {
    "path": "webpack.config.dll.babel.js",
    "content": "import path from 'path';\nimport webpack from 'webpack';\n\nimport ProgressBarWebpackPlugin from 'progress-bar-webpack-plugin';\n\nconst reactVendors = [\n  'connected-react-router',\n  'connected-react-router/immutable',\n  'react',\n  'react-dom',\n  'react-hot-loader',\n  'react-i18next',\n  'redux-immutable',\n  'react-router-dom',\n  'react-redux',\n  'redux',\n  'redux-logger',\n  'redux-saga',\n];\n\nconst materializeVendors = [\n  'materialize-css',\n];\n\nconst i18nextVendors = [\n  'i18next',\n  'i18next-xhr-backend',\n  'i18next-browser-languagedetector',\n];\n\nconst otherVendors = [\n  'axios',\n  'immutable',\n  'regenerator-runtime',\n];\n\nconst config = {\n  // Get mode from NODE_ENV\n  mode: process.env.NODE_ENV,\n\n  // The base directory, an absolute path, for resolving entry points and loaders from configuration\n  context: path.resolve(__dirname),\n\n  // The point or points to enter the application.\n  entry: {\n    react: reactVendors,\n    materialize: materializeVendors,\n    i18next: i18nextVendors,\n    other: otherVendors,\n  },\n\n  // Affecting the output of the compilation\n  output: {\n    // path: the output directory as an absolute path (required)\n    path: path.resolve(__dirname, 'frontend/dist/dll/'),\n    // filename: specifies the name of output file on disk (required)\n    filename: '[name]_dll.js',\n    // library: name of the generated dll reference\n    library: '[name]_dll',\n  },\n\n  // A list of used webpack plugins\n  plugins: [\n    // Better building progress display\n    new ProgressBarWebpackPlugin(),\n    // Output manifest json file for each generated dll reference file\n    new webpack.DllPlugin({\n      path: path.resolve(__dirname, 'frontend/dist/dll/[name]_manifest.json'),\n      name: '[name]_dll',\n      format: true,\n    }),\n  ],\n\n  // Turn off performance hints (assets size limit)\n  performance: {\n    hints: false,\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "webpack.config.prod.babel.js",
    "content": "import path from 'path';\nimport merge from 'webpack-merge';\n\nimport OfflinePlugin from 'offline-plugin';\n\nimport BaseWebpackConfig from './webpack.config.base.babel';\n\nexport default merge(BaseWebpackConfig, {\n  // The point or points to enter the application.\n  entry: {\n    app: './frontend/src/index',\n  },\n\n  // Affecting the output of the compilation\n  output: {\n    // path: the output directory as an absolute path (required)\n    path: path.resolve(__dirname, 'frontend/dist/prod'),\n    // filename: specifies the name of entry output file (required)\n    filename: '[name].[chunkhash:10].js',\n    // chunkFilename: specifies the name of non-entry output files (e.g. dynamic import component)\n    chunkFilename: '[name].[chunkhash:10].js',\n    // publicPath: specifies the server-relative URL of the output resource directory\n    // https://webpack.js.org/configuration/output/#output-publicpath\n    publicPath: '/',\n  },\n\n  // A list of used webpack plugins\n  plugins: [\n    // It's always better if OfflinePlugin is the last plugin added\n    new OfflinePlugin(),\n  ],\n\n  // Source map mode\n  // https://webpack.js.org/configuration/devtool\n  devtool: 'source-map',\n});\n"
  },
  {
    "path": "webpack.config.profile.babel.js",
    "content": "import merge from 'webpack-merge';\n\nimport { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';\n\nimport ProdWebpackConfig from './webpack.config.prod.babel';\n\nexport default merge(ProdWebpackConfig, {\n  plugins: [\n    // Webpack bundle analyzer for profiling\n    new BundleAnalyzerPlugin({\n      analyzerPort: process.env.PORT_BUNDLE_ANALYZER || 3005,\n      generateStatsFile: true,\n    }),\n  ],\n});\n"
  }
]